import { NextResponse } from 'next/server';
import { getServerSession } from 'next-auth';
import { authOptions } from '../../auth/[...nextauth]/route';
import { prisma } from '@/lib/prisma';

interface Coordinate {
  lat: number;
  lon: number;
}

interface AddressPoint {
  coordinate: Coordinate;
  city: string;
  street: string;
  originalIndex: number;
}

// İki nokta arası mesafeyi hesapla (Haversine formülü)
function calculateDistance(coord1: Coordinate, coord2: Coordinate): number {
  const R = 6371; // Dünya'nın yarıçapı (km)
  const dLat = toRad(coord2.lat - coord1.lat);
  const dLon = toRad(coord2.lon - coord1.lon);
  const lat1 = toRad(coord1.lat);
  const lat2 = toRad(coord2.lat);

  const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
           Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
  return R * c;
}

function toRad(value: number): number {
  return value * Math.PI / 180;
}

async function geocodeAddress(address: string): Promise<Coordinate | null> {
  try {
    const query = encodeURIComponent(address + ", Switzerland");
    const response = await fetch(
      `https://nominatim.openstreetmap.org/search?q=${query}&format=json&limit=1`,
      {
        headers: {
          'User-Agent': 'PostaciApp/1.0'
        }
      }
    );

    if (!response.ok) {
      throw new Error('Geocoding request failed');
    }

    const data = await response.json();
    if (data && data.length > 0) {
      return {
        lat: parseFloat(data[0].lat),
        lon: parseFloat(data[0].lon)
      };
    }
    return null;
  } catch (error) {
    console.error('Geocoding error:', error);
    return null;
  }
}

function optimizeRoute(points: AddressPoint[]): number[] {
  if (points.length < 2) return points.map((_, i) => i);

  const optimizedRoute: number[] = [0]; // Başlangıç noktası
  const unvisited = points.slice(1).map((_, i) => i + 1);

  while (unvisited.length > 0) {
    const currentPoint = points[optimizedRoute[optimizedRoute.length - 1]];
    let nearestPoint = -1;
    let nearestIndex = -1;
    let minScore = Infinity;

    // Tüm ziyaret edilmemiş noktaları değerlendir
    for (let i = 0; i < unvisited.length; i++) {
      const pointIndex = unvisited[i];
      const candidatePoint = points[pointIndex];
      
      // Mesafe ve şehir bazlı skor hesapla
      const distance = calculateDistance(currentPoint.coordinate, candidatePoint.coordinate);
      
      // Şehir aynıysa ekstra bonus ver (daha düşük skor daha iyi)
      const cityScore = currentPoint.city === candidatePoint.city ? 0 : 2;
      
      // Toplam skoru hesapla (mesafe + şehir skoru)
      const totalScore = distance + cityScore;

      // En düşük skorlu noktayı bul
      if (totalScore < minScore) {
        minScore = totalScore;
        nearestPoint = pointIndex;
        nearestIndex = i;
      }
    }

    if (nearestPoint !== -1) {
      optimizedRoute.push(nearestPoint);
      unvisited.splice(nearestIndex, 1);

      // Debug bilgisi
      console.log(`Eklenen nokta: ${points[nearestPoint].street}, ${points[nearestPoint].city}`);
      console.log(`Mesafe skoru: ${minScore}`);
    }
  }

  return optimizedRoute;
}

export async function POST(request: Request) {
  try {
    // Oturum kontrolü
    const session = await getServerSession(authOptions);
    if (!session?.user?.email) {
      return new Response(
        JSON.stringify({
          success: false,
          error: 'Oturum gerekli'
        }),
        { 
          status: 200,
          headers: { 'Content-Type': 'application/json' }
        }
      );
    }

    const { addresses, routeId } = await request.json();
    console.log('Gelen adresler:', addresses);

    if (!Array.isArray(addresses) || addresses.length < 2) {
      return new Response(
        JSON.stringify({
          success: false,
          error: 'En az 2 adres gerekli'
        }),
        { 
          status: 200,
          headers: { 'Content-Type': 'application/json' }
        }
      );
    }

    // Adresleri geocode et
    const points: AddressPoint[] = [];
    for (let i = 0; i < addresses.length; i++) {
      const address = addresses[i];
      const parts = address.split(',');
      const cityMatch = parts[1]?.match(/\d{4}\s+([^\d]+)/);
      const city = cityMatch ? cityMatch[1].trim() : '';
      
      const coordinate = await geocodeAddress(address);
      console.log(`Adres ${i + 1} koordinatları:`, { address, city, coordinate });
      
      if (coordinate) {
        points.push({
          coordinate,
          city,
          street: parts[0].trim(),
          originalIndex: i
        });
      }
      
      // Rate limiting
      if (i < addresses.length - 1) {
        await new Promise(resolve => setTimeout(resolve, 1000));
      }
    }

    console.log('Geocode edilmiş noktalar:', points);

    if (points.length < 2) {
      return new Response(
        JSON.stringify({
          success: false,
          error: 'Yeterli sayıda geçerli adres bulunamadı'
        }),
        { 
          status: 200,
          headers: { 'Content-Type': 'application/json' }
        }
      );
    }

    // Rotayı optimize et
    const optimizedIndices = optimizeRoute(points);
    console.log('Optimize edilmiş sıralama:', optimizedIndices);

    // Doğrudan points dizisinden alarak optimizedRoute'u oluştur
    const optimizedRoute = optimizedIndices.map(i => ({
      index: i,  // originalIndex yerine doğrudan sıra numarasını kullan
      lat: points[i].coordinate.lat,
      lon: points[i].coordinate.lon,
      address: addresses[i]  // points[i].originalIndex yerine doğrudan i kullan
    }));

    console.log('Optimize edilmiş rota:', optimizedRoute);

    // Toplam mesafeyi hesapla
    let totalDistance = 0;
    for (let i = 0; i < optimizedRoute.length - 1; i++) {
      totalDistance += calculateDistance(
        { lat: optimizedRoute[i].lat, lon: optimizedRoute[i].lon },
        { lat: optimizedRoute[i + 1].lat, lon: optimizedRoute[i + 1].lon }
      );
    }

    // Tahmini süreyi hesapla (ortalama 30 km/saat hız varsayarak)
    const estimatedDuration = (totalDistance / 30) * 60; // dakika cinsinden

    // Rotanın durumunu güncelle
    if (routeId) {
      // Önce tüm adresleri getir
      const existingAddresses = await prisma.route.findUnique({
        where: { id: routeId },
        include: { addresses: true }
      });

      if (existingAddresses) {
        // Her optimize edilmiş adres için güncelleme yap
        await Promise.all(optimizedRoute.map(async (route, newOrder) => {
          const addressToUpdate = existingAddresses.addresses.find(
            addr => addr.street === route.address.split(',')[0].trim()
          );

          if (addressToUpdate) {
            await prisma.address.update({
              where: { id: addressToUpdate.id },
              data: {
                order: newOrder,
                latitude: route.lat,
                longitude: route.lon
              }
            });
          }
        }));

        // Route'un durumunu güncelle
        await prisma.route.update({
          where: { id: routeId },
          data: { status: 'OPTIMIZED' }
        });
      }
    }

    return new Response(
      JSON.stringify({
        success: true,
        data: {
          route: optimizedRoute,
          distance: Math.round(totalDistance * 10) / 10, // 1 ondalık basamak
          duration: Math.round(estimatedDuration)
        }
      }),
      { 
        status: 200,
        headers: { 'Content-Type': 'application/json' }
      }
    );
  } catch (error) {
    console.error('Route optimization error:', error);
    return new Response(
      JSON.stringify({
        success: false,
        error: 'Rota optimizasyonu sırasında bir hata oluştu'
      }),
      { 
        status: 200,
        headers: { 'Content-Type': 'application/json' }
      }
    );
  }
} 