require('dotenv').config();
const express = require('express');
const axios = require('axios');
const cors = require('cors');
const path = require('path');

const app = express();
app.use(cors());
app.use(express.json());
app.use(express.static(path.join(__dirname, 'public')));

const PORT = process.env.PORT || 3000;

// ─────────────────────────────────────────────
// IATA-LOOKUP-TABELLE
// ─────────────────────────────────────────────

const IATA_LOOKUP = {
  // Deutschland
  'stuttgart': 'STR', 'frankfurt': 'FRA', 'münchen': 'MUC', 'munich': 'MUC',
  'berlin': 'BER', 'hamburg': 'HAM', 'düsseldorf': 'DUS', 'dusseldorf': 'DUS',
  'köln': 'CGN', 'koeln': 'CGN', 'cologne': 'CGN', 'hannover': 'HAJ',
  'nürnberg': 'NUE', 'nuremberg': 'NUE', 'leipzig': 'LEJ', 'bremen': 'BRE',
  'dresden': 'DRS', 'karlsruhe': 'FKB', 'friedrichshafen': 'FDH',
  'bad saulgau': 'FDH', 'ravensburg': 'FDH', 'biberach': 'FDH', 'sigmaringen': 'FDH',
  'saarbrücken': 'SCN', 'erfurt': 'ERF', 'paderborn': 'PAD',
  'münster': 'FMO', 'rostock': 'RLG', 'dortmund': 'DTM', 'weeze': 'NRN',
  // Österreich
  'wien': 'VIE', 'vienna': 'VIE', 'graz': 'GRZ', 'linz': 'LNZ',
  'salzburg': 'SZG', 'innsbruck': 'INN', 'klagenfurt': 'KLU',
  // Schweiz
  'zürich': 'ZRH', 'zurich': 'ZRH', 'genf': 'GVA', 'geneva': 'GVA',
  'basel': 'BSL', 'bern': 'BRN',
  // Polen
  'warschau': 'WAW', 'warsaw': 'WAW', 'warszawa': 'WAW',
  'krakau': 'KRK', 'krakow': 'KRK', 'kraków': 'KRK',
  'breslau': 'WRO', 'wroclaw': 'WRO', 'wrocław': 'WRO',
  'danzig': 'GDN', 'gdansk': 'GDN', 'gdańsk': 'GDN',
  'posen': 'POZ', 'poznan': 'POZ', 'poznań': 'POZ',
  'katowice': 'KTW', 'lodz': 'LCJ', 'łódź': 'LCJ',
  'rzeszow': 'RZE', 'rzeszów': 'RZE', 'bydgoszcz': 'BZG',
  'szczecin': 'SZZ', 'lublin': 'LUZ',
  // Tschechien
  'prag': 'PRG', 'prague': 'PRG', 'praha': 'PRG',
  'brünn': 'BRQ', 'brno': 'BRQ', 'ostrava': 'OSR',
  // Ungarn
  'budapest': 'BUD', 'debrecen': 'DEB',
  // Slowakei
  'bratislava': 'BTS', 'košice': 'KSC', 'kosice': 'KSC',
  // Rumänien
  'bukarest': 'OTP', 'bucharest': 'OTP', 'bucurești': 'OTP',
  'cluj': 'CLJ', 'timisoara': 'TSR', 'timișoara': 'TSR',
  // Niederlande
  'amsterdam': 'AMS', 'eindhoven': 'EIN', 'rotterdam': 'RTM',
  // Belgien
  'brüssel': 'BRU', 'brussels': 'BRU', 'bruxelles': 'BRU',
  'lüttich': 'LGG', 'liege': 'LGG', 'liège': 'LGG',
  // Frankreich
  'paris': 'CDG', 'lyon': 'LYS', 'marseille': 'MRS',
  'nizza': 'NCE', 'nice': 'NCE', 'toulouse': 'TLS',
  'bordeaux': 'BOD', 'strasbourg': 'SXB', 'straßburg': 'SXB',
  'lille': 'LIL', 'nantes': 'NTE', 'montpellier': 'MPL',
  // Spanien
  'madrid': 'MAD', 'barcelona': 'BCN', 'sevilla': 'SVQ', 'seville': 'SVQ',
  'valencia': 'VLC', 'bilbao': 'BIO', 'málaga': 'AGP', 'malaga': 'AGP',
  'palma': 'PMI', 'teneriffa': 'TFS', 'tenerife': 'TFS',
  'alicante': 'ALC', 'zaragoza': 'ZAZ',
  // Italien
  'rom': 'FCO', 'rome': 'FCO', 'roma': 'FCO',
  'mailand': 'MXP', 'milan': 'MXP', 'milano': 'MXP',
  'venedig': 'VCE', 'venice': 'VCE', 'venezia': 'VCE',
  'florenz': 'FLR', 'florence': 'FLR', 'firenze': 'FLR',
  'neapel': 'NAP', 'naples': 'NAP', 'napoli': 'NAP',
  'bologna': 'BLQ', 'turin': 'TRN', 'torino': 'TRN',
  'catania': 'CTA', 'palermo': 'PMO', 'bari': 'BRI', 'verona': 'VRN',
  // UK
  'london': 'LHR', 'manchester': 'MAN', 'birmingham': 'BHX',
  'edinburgh': 'EDI', 'glasgow': 'GLA', 'bristol': 'BRS',
  'newcastle': 'NCL', 'leeds': 'LBA', 'liverpool': 'LPL',
  // Skandinavien
  'stockholm': 'ARN', 'oslo': 'OSL', 'kopenhagen': 'CPH', 'copenhagen': 'CPH',
  'helsinki': 'HEL', 'göteborg': 'GOT', 'gothenburg': 'GOT',
  'bergen': 'BGO', 'stavanger': 'SVG', 'tampere': 'TMP',
  // Osteuropa
  'kiev': 'KBP', 'kiew': 'KBP', 'kyiv': 'KBP',
  'riga': 'RIX', 'tallinn': 'TLL', 'vilnius': 'VNO', 'minsk': 'MSQ',
  'sofia': 'SOF', 'zagreb': 'ZAG', 'belgrad': 'BEG', 'belgrade': 'BEG',
  'sarajevo': 'SJJ', 'laibach': 'LJU', 'ljubljana': 'LJU',
  'skopje': 'SKP', 'tirana': 'TIA', 'chisinau': 'KIV',
  'thessaloniki': 'SKG', 'athen': 'ATH', 'athens': 'ATH', 'athina': 'ATH',
  // Türkei
  'istanbul': 'IST', 'ankara': 'ESB', 'izmir': 'ADB', 'antalya': 'AYT',
  // Russland
  'moskau': 'SVO', 'moscow': 'SVO', 'moskva': 'SVO',
  'st. petersburg': 'LED', 'saint petersburg': 'LED', 'sankt petersburg': 'LED',
  // Naher Osten
  'dubai': 'DXB', 'abu dhabi': 'AUH', 'doha': 'DOH',
  'riad': 'RUH', 'riyadh': 'RUH', 'kuwait': 'KWI',
  'beirut': 'BEY', 'tel aviv': 'TLV', 'amman': 'AMM', 'muscat': 'MCT',
  // Asien
  'peking': 'PEK', 'beijing': 'PEK', 'shanghai': 'PVG',
  'tokio': 'NRT', 'tokyo': 'NRT', 'osaka': 'KIX',
  'seoul': 'ICN', 'singapur': 'SIN', 'singapore': 'SIN',
  'hongkong': 'HKG', 'hong kong': 'HKG',
  'bangkok': 'BKK', 'mumbai': 'BOM', 'delhi': 'DEL',
  'jakarta': 'CGK', 'manila': 'MNL', 'kuala lumpur': 'KUL',
  'taipei': 'TPE', 'colombo': 'CMB', 'karachi': 'KHI',
  // Amerika
  'new york': 'JFK', 'los angeles': 'LAX', 'chicago': 'ORD',
  'miami': 'MIA', 'san francisco': 'SFO', 'boston': 'BOS',
  'washington': 'IAD', 'toronto': 'YYZ', 'montreal': 'YUL',
  'vancouver': 'YVR', 'mexico city': 'MEX', 'mexiko stadt': 'MEX',
  'sao paulo': 'GRU', 'rio de janeiro': 'GIG',
  'buenos aires': 'EZE', 'bogota': 'BOG', 'lima': 'LIM', 'santiago': 'SCL',
  // Afrika / Ozeanien
  'kairo': 'CAI', 'cairo': 'CAI',
  'johannesburg': 'JNB', 'kapstadt': 'CPT', 'cape town': 'CPT',
  'nairobi': 'NBO', 'casablanca': 'CMN', 'addis abeba': 'ADD',
  'sydney': 'SYD', 'melbourne': 'MEL', 'auckland': 'AKL',
  'brisbane': 'BNE', 'perth': 'PER',
};

function getIataCode(cityInput) {
  if (!cityInput) return null;
  const normalized = cityInput.toLowerCase().trim().split(',')[0].trim();
  return IATA_LOOKUP[normalized] || null;
}

// ─────────────────────────────────────────────
// HILFSFUNKTIONEN
// ─────────────────────────────────────────────

// Geocoding: Adresse → lat/lng + Stadtname (Google Maps oder Nominatim)
async function geocodeAddress(address) {
  if (process.env.GOOGLE_MAPS_API_KEY && !process.env.GOOGLE_MAPS_API_KEY.startsWith('dein_')) {
    try {
      const res = await axios.get('https://maps.googleapis.com/maps/api/geocode/json', {
        params: { address, key: process.env.GOOGLE_MAPS_API_KEY },
      });
      const result = res.data.results?.[0];
      if (result) {
        const loc = result.geometry.location;
        const components = result.address_components || [];
        const cityComp =
          components.find((c) => c.types.includes('locality')) ||
          components.find((c) => c.types.includes('administrative_area_level_1'));
        return {
          lat: loc.lat,
          lng: loc.lng,
          city: cityComp?.long_name || address.split(',')[0].trim(),
        };
      }
    } catch (_) {}
  }

  // Nominatim Fallback
  const queries = [
    address,
    address
      .replace(/Polen/gi, 'Poland').replace(/Warschau/gi, 'Warsaw')
      .replace(/Deutschland/gi, 'Germany').replace(/Österreich/gi, 'Austria')
      .replace(/Schweiz/gi, 'Switzerland').replace(/Frankreich/gi, 'France')
      .replace(/München/gi, 'Munich').replace(/Wien/gi, 'Vienna')
      .replace(/Prag/gi, 'Prague').replace(/Mailand/gi, 'Milan')
      .replace(/Brüssel/gi, 'Brussels').replace(/Lissabon/gi, 'Lisbon')
      .replace(/Köln/gi, 'Cologne').replace(/Athen/gi, 'Athens'),
    address.split(',').slice(-2).join(',').trim(),
    address.split(',')[0].trim(),
  ];

  for (const q of queries) {
    if (!q) continue;
    try {
      const res = await axios.get('https://nominatim.openstreetmap.org/search', {
        params: { q, format: 'json', limit: 1, addressdetails: 1 },
        headers: { 'User-Agent': 'TravelDesk/1.0' },
        timeout: 5000,
      });
      if (res.data?.length > 0) {
        const item = res.data[0];
        const city =
          item.address?.city ||
          item.address?.municipality ||
          item.address?.town ||
          item.address?.village ||
          item.address?.state ||
          address.split(',')[0].trim();
        return { lat: parseFloat(item.lat), lng: parseFloat(item.lon), city };
      }
    } catch (_) {}
  }

  // Letzter Fallback: Stadtname direkt aus Input
  const city = address.split(',')[0].trim();
  throw new Error(`Adresse nicht gefunden: ${city}`);
}

// Distanz (Google Routes API oder Haversine-Fallback)
async function getDistanceMeters(originLat, originLng, destLat, destLng) {
  if (process.env.GOOGLE_MAPS_API_KEY && !process.env.GOOGLE_MAPS_API_KEY.startsWith('dein_')) {
    try {
      const res = await axios.post(
        'https://routes.googleapis.com/distanceMatrix/v2:computeRouteMatrix',
        {
          origins: [{ waypoint: { location: { latLng: { latitude: originLat, longitude: originLng } } } }],
          destinations: [{ waypoint: { location: { latLng: { latitude: destLat, longitude: destLng } } } }],
          travelMode: 'WALK',
        },
        {
          headers: {
            'X-Goog-Api-Key': process.env.GOOGLE_MAPS_API_KEY,
            'X-Goog-FieldMask': 'originIndex,destinationIndex,duration,distanceMeters',
            'Content-Type': 'application/json',
          },
        }
      );
      const entry = res.data?.[0];
      if (entry?.distanceMeters) return entry.distanceMeters;
    } catch (_) {}
  }
  // Haversine-Fallback (Luftlinie)
  const R = 6371000;
  const φ1 = (originLat * Math.PI) / 180;
  const φ2 = (destLat * Math.PI) / 180;
  const Δφ = ((destLat - originLat) * Math.PI) / 180;
  const Δλ = ((destLng - originLng) * Math.PI) / 180;
  const a =
    Math.sin(Δφ / 2) ** 2 +
    Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) ** 2;
  return Math.round(R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)));
}

// IATA via GPT-Fallback
async function getIataViaGpt(cityName) {
  const apiKey = process.env.OPENAI_API_KEY;
  if (!apiKey || apiKey.startsWith('dein_')) return null;
  try {
    const res = await axios.post(
      'https://api.openai.com/v1/chat/completions',
      {
        model: 'gpt-4o-mini',
        max_tokens: 5,
        temperature: 0,
        messages: [
          {
            role: 'system',
            content:
              'Du bist ein Flughafen-Experte. Antworte IMMER nur mit einem einzigen 3-buchstabigen IATA-Code für den nächstgelegenen Flughafen (auch Regionalflughafen). Nichts anderes.',
          },
          { role: 'user', content: `IATA-Code des nächstgelegenen Flughafens für: ${cityName}` },
        ],
      },
      { headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' } }
    );
    const code = res.data.choices?.[0]?.message?.content?.trim().toUpperCase();
    if (/^[A-Z]{3}$/.test(code)) return code;
  } catch (_) {}
  return null;
}


// ─────────────────────────────────────────────
// DEEP-LINK-GENERATOREN
// ─────────────────────────────────────────────

function generateFlightLink({ originIATA, destIATA, departDate, returnDate, adults, directOnly }) {
  if (!originIATA || !destIATA) {
    return `https://flights.booking.com/`;
  }
  const type = returnDate ? 'ROUNDTRIP' : 'ONEWAY';
  let url =
    `https://flights.booking.com/flights/${originIATA}-${destIATA}/` +
    `?type=${type}` +
    `&from=${originIATA}` +
    `&to=${destIATA}` +
    `&cabinClass=ECONOMY` +
    `&sort=CHEAPEST` +
    `&depart=${departDate}` +
    `&adults=${adults}` +
    `&locale=de-de` +
    `&salesCurrency=EUR` +
    `&customerCurrency=EUR`;
  if (returnDate) url += `&return=${returnDate}`;
  if (directOnly) url += `&stops=0`;
  return url;
}

function generateHotelLink({ cityName, checkin, checkout, adults, refundableOnly }) {
  const [cy, cm, cd] = checkin.split('-');
  const [oy, om, od] = checkout.split('-');
  let url =
    `https://www.booking.com/searchresults.de.html` +
    `?ss=${encodeURIComponent(cityName)}` +
    `&checkin_year=${cy}` +
    `&checkin_month=${parseInt(cm)}` +
    `&checkin_monthday=${parseInt(cd)}` +
    `&checkout_year=${oy}` +
    `&checkout_month=${parseInt(om)}` +
    `&checkout_monthday=${parseInt(od)}` +
    `&group_adults=${adults}` +
    `&group_children=0` +
    `&no_rooms=1` +
    `&order=price`;
  if (refundableOnly) url += `&nflt=fc%3D1`;
  return url;
}

function generateCarLink({ cityName, iataCode, pickupDate, dropoffDate, lat, lng }) {
  const pu = new Date(pickupDate);
  const doDate = new Date(dropoffDate);
  const params = new URLSearchParams({
    preflang: 'de',
    prefcurrency: 'EUR',
    driversAge: '30',
    locationName: iataCode ? `Flughafen ${cityName}` : cityName,
    locationIata: iataCode || '',
    dropLocationName: iataCode ? `Flughafen ${cityName}` : cityName,
    dropLocationIata: iataCode || '',
    puDay: pu.getUTCDate(),
    puMonth: pu.getUTCMonth() + 1,
    puYear: pu.getUTCFullYear(),
    puHour: '10',
    puMinute: '0',
    doDay: doDate.getUTCDate(),
    doMonth: doDate.getUTCMonth() + 1,
    doYear: doDate.getUTCFullYear(),
    doHour: '10',
    doMinute: '0',
    ftsType: 'A',
    dropFtsType: 'A',
  });
  if (lat && lng) {
    params.set('coordinates', `${lat},${lng}`);
    params.set('dropCoordinates', `${lat},${lng}`);
  }
  return `https://cars.booking.com/search-results?${params.toString()}`;
}

// ─────────────────────────────────────────────
// HAUPT-ENDPOINT: POST /api/search
// ─────────────────────────────────────────────

app.post('/api/search', async (req, res) => {
  const {
    origin,
    destStreet = '',
    destHouseNumber = '',
    destPostalCode = '',
    destCity = '',
    destCountry = '',
    checkin,
    checkout,
    adults = 1,
    directFlightsOnly = false,
    refundableOnly = true,
    hotelAddress = '',
    specialRequirements = '',
  } = req.body;

  // Adresse aus Einzelfeldern zusammensetzen
  const streetPart = [destStreet.trim(), destHouseNumber.trim()].filter(Boolean).join(' ');
  const destination = [streetPart, destPostalCode.trim(), destCity.trim(), destCountry.trim()]
    .filter(Boolean).join(', ');

  const nights = Math.round((new Date(checkout) - new Date(checkin)) / 86400000);

  // ── Schritt 1: Fabrikadresse geocoden ─────
  let destCoords = null;
  // Stadt direkt aus dem Formularfeld – kein Parsing nötig
  let destinationCity = destCity.trim() || destination.split(',')[0].trim();

  try {
    destCoords = await geocodeAddress(destination);
    // Geocoded city nur als Fallback wenn destCity leer
    if (!destCity.trim()) destinationCity = destCoords.city;
  } catch (_) {}

  // ── Schritt 2: IATA-Codes ermitteln ───────
  const originCity = origin.split(',')[0].trim();

  let originIATA = getIataCode(originCity);
  let destIATA = getIataCode(destinationCity) || getIataCode(destination.split(',')[0].trim());
  let iataFallbackUsed = false;
  let iataError = null;

  if (!originIATA) {
    originIATA = await getIataViaGpt(originCity);
    if (originIATA) iataFallbackUsed = true;
  }
  if (!destIATA) {
    destIATA = await getIataViaGpt(destinationCity);
    if (destIATA) iataFallbackUsed = true;
  }

  if (!originIATA || !destIATA) {
    iataError = {
      origin: !originIATA ? originCity : null,
      dest: !destIATA ? destinationCity : null,
    };
  }

  // ── Schritt 3: Mietwagen-Logik ─────────────
  let carNeeded = null;
  let carMessage = 'Hotel noch nicht gebucht? Buche zuerst ein Hotel und trage die Adresse ein für automatischen Check.';
  let distanceMeters = null;
  let hotelAddressUsed = '';

  if (hotelAddress && hotelAddress.trim()) {
    hotelAddressUsed = hotelAddress.trim();
    if (destCoords) {
      try {
        const hotelCoords = await geocodeAddress(hotelAddress);
        distanceMeters = await getDistanceMeters(
          hotelCoords.lat, hotelCoords.lng,
          destCoords.lat, destCoords.lng
        );
        if (distanceMeters < 1500) {
          carNeeded = false;
          carMessage = `✅ Kein Mietwagen nötig – dein Hotel ist nur ${distanceMeters}m von der Fabrik entfernt (fußläufig).`;
        } else {
          carNeeded = true;
          carMessage = `🚗 Mietwagen empfohlen – dein Hotel ist ${(distanceMeters / 1000).toFixed(1)} km von der Fabrik entfernt.`;
        }
      } catch (_) {
        carNeeded = null;
        carMessage = 'Hotel-Adresse konnte nicht gefunden werden. Bitte manuell prüfen.';
      }
    } else {
      carMessage = 'Fabrikadresse konnte nicht geocoded werden – Mietwagen bitte manuell prüfen.';
    }
  }

  // ── Schritt 4: Deep-Links generieren ──────
  const flightLink = generateFlightLink({
    originIATA, destIATA, departDate: checkin,
    returnDate: checkout, adults, directOnly: directFlightsOnly,
  });
  const hotelLink = generateHotelLink({
    cityName: destinationCity, checkin, checkout, adults, refundableOnly,
  });
  const carLink = generateCarLink({
    cityName: destinationCity, iataCode: destIATA,
    pickupDate: checkin, dropoffDate: checkout,
    lat: destCoords?.lat, lng: destCoords?.lng,
  });

  res.json({
    flightLink,
    hotelLink,
    carLink,
    originIATA,
    destIATA,
    iataFallbackUsed,
    iataError,
    carNeeded,
    carMessage,
    distanceMeters,
    hotelAddressUsed,
    meta: {
      origin: originCity,
      destination,
      destinationCity,
      checkin,
      checkout,
      nights,
      adults,
    },
  });
});

// ─────────────────────────────────────────────
// ENDPOINT: POST /api/check-distance
// ─────────────────────────────────────────────

app.post('/api/check-distance', async (req, res) => {
  const { hotelAddress, fabrikAddress } = req.body;

  if (!hotelAddress || !fabrikAddress) {
    return res.status(400).json({ error: 'Beide Adressen werden benötigt.' });
  }

  try {
    const [hotelCoords, fabrikCoords] = await Promise.all([
      geocodeAddress(hotelAddress),
      geocodeAddress(fabrikAddress),
    ]);

    const distanceMeters = await getDistanceMeters(
      hotelCoords.lat, hotelCoords.lng,
      fabrikCoords.lat, fabrikCoords.lng
    );

    const carNeeded = distanceMeters >= 1500;
    const destinationCity = fabrikCoords.city;

    const carLink = generateCarLink({
      cityName: destinationCity,
      pickupDate: req.body.checkin || new Date().toISOString().split('T')[0],
      dropoffDate: req.body.checkout || new Date().toISOString().split('T')[0],
      lat: fabrikCoords.lat, lng: fabrikCoords.lng,
    });

    res.json({
      distanceMeters,
      carNeeded,
      carMessage: carNeeded
        ? `🚗 Mietwagen empfohlen – ${(distanceMeters / 1000).toFixed(1)} km zur Fabrik`
        : `✅ Kein Mietwagen nötig – ${distanceMeters}m (fußläufig)`,
      carLink,
    });
  } catch (e) {
    res.status(400).json({
      error: 'Adresse nicht gefunden. Bitte prüfe die Schreibweise.',
    });
  }
});

// ─────────────────────────────────────────────
// SERVER STARTEN
// ─────────────────────────────────────────────

app.listen(PORT, () => {
  console.log(`\n✅ TravelDesk läuft auf http://localhost:${PORT}\n`);
});
