import { writeFileSync } from 'fs';
import { join } from 'path';
import { prisma } from 'src/db';
import { FlightAwareService } from 'src/services/flightaware/flightaware.service';

import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

import { GetAirlineDTO } from './dto/get-airline.dto';
import { GetFlightsBoundsDTO } from './dto/get-flights-bounds.dto';
import { GetFlightsDTO } from './dto/get-flights.dto';

@Injectable()
export class FlightsService {
  constructor(
    private configService: ConfigService,
    private flightAwareService: FlightAwareService,
  ) {}

  async getAirline(body: GetAirlineDTO) {
    const airline = await prisma.airline.findFirst({
      where: {
        OR: [
          {
            iata: {
              equals: body.search.substring(0, 2),
              mode: 'insensitive',
            },
            icao: {
              equals: body.search.substring(0, 3),
              mode: 'insensitive',
            },
          },
        ],
      },
    });
    if (!airline)
      throw new HttpException('No airline found', HttpStatus.NOT_FOUND);
    return airline;
  }

  async getFlightsBetweenTwoAirports(query: GetFlightsDTO) {
    const { from, to, date } = query;
    const apiKey = this.configService.getOrThrow<string>(
      'AVIATION_STACK_API_KEY',
    );

    if (!apiKey) {
      throw new HttpException(
        'Internal Server Error',
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }

    // Normalize date to start of day to ensure cache hits
    const _date = new Date(date);
    _date.setUTCHours(0, 0, 0, 0);

    const dayEndDate = new Date(_date);
    dayEndDate.setDate(_date.getDate() + 1);

    // First check in database
    try {
      const existingFlights = await prisma.flights.findMany({
        where: {
          AND: [
            {
              arrival: {
                path: ['iataCode'],
                string_contains: to,
              },
              departure: {
                path: ['iataCode'],
                string_contains: from,
              },
            },
            {
              date: {
                gte: _date,
                lt: dayEndDate,
              },
            },
          ],
        },
        orderBy: {
          createdAt: 'desc',
        },
      });
      if (existingFlights.length > 0) {
        console.log(
          `Cache HIT: Found ${existingFlights.length} flights for ${from} to ${to} on ${_date.toISOString().split('T')[0]}`,
        );
        return {
          success: true,
          date,
          route: { from, to },
          flightCount: existingFlights.length,
          flights: existingFlights,
        };
      }
      console.log(
        `Cache MISS: No flights found for ${from} to ${to} on ${_date.toISOString().split('T')[0]}, fetching from API`,
      );
    } catch (error) {
      console.error('Database query error:', error);
    }

    try {
      const apiUrl = new URL('https://api.aviationstack.com/v1/timetable');
      apiUrl.searchParams.append('access_key', apiKey);
      apiUrl.searchParams.append('iataCode', from);
      apiUrl.searchParams.append('type', 'departure');

      const response = await fetch(apiUrl);

      if (!response.ok) {
        console.log(await response.text());
        throw new Error(`API request failed with status ${response.status}`);
      }

      const flights = await response.json();

      // Use upsert to prevent duplicate records
      await Promise.all(
        flights.data.map(async (flight) => {
          // Create a unique identifier for this flight
          const uniqueId = `${flight.departure?.iataCode || 'unknown'}_${flight.arrival?.iataCode || 'unknown'}_${flight.flight?.iataNumber || flight.flight?.icaoNumber || 'unknown'}_${_date.toISOString().split('T')[0]}`;

          return prisma.flights.upsert({
            where: {
              id: uniqueId,
            },
            update: {
              airline: flight.airline,
              arrival: flight.arrival,
              codeshared: flight.codeshared,
              flight: flight.flight,
              departure: flight.departure,
              type: flight.type,
              status: flight.status,
            },
            create: {
              id: uniqueId,
              date: _date,
              airline: flight.airline,
              arrival: flight.arrival,
              codeshared: flight.codeshared,
              flight: flight.flight,
              departure: flight.departure,
              type: flight.type,
              status: flight.status,
            },
          });
        }),
      );

      console.log(
        `Cached ${flights.data.length} flights from API for ${from} on ${_date.toISOString().split('T')[0]}`,
      );

      const routeFlights = flights.data.filter(
        (flight) =>
          flight.departure?.iataCode === from &&
          flight.arrival?.iataCode === to,
      );

      return {
        success: true,
        date,
        route: { from, to },
        flightCount: routeFlights.length,
        flights: routeFlights,
      };
    } catch (error) {
      console.error('Error fetching flights:', error);
      throw new HttpException(
        'Failed to fetch flight data',
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }

  async getFlightsInBounds(query: GetFlightsBoundsDTO) {
    const { minLat, maxLat, minLng, maxLng } = query;
    return {
      success: true,
      timestamp: new Date().toISOString(),
      bounds: {
        minLat,
        maxLat,
        minLng,
        maxLng,
      },
      totalAircraft: 0,
      airborneAircraft: 0,
      flights: [],
    };
    // try {
    //   // Validate bounds
    //   if (minLat >= maxLat) {
    //     throw new HttpException(
    //       'Minimum latitude must be less than maximum latitude',
    //       HttpStatus.BAD_REQUEST,
    //     );
    //   }

    //   if (minLng >= maxLng) {
    //     throw new HttpException(
    //       'Minimum longitude must be less than maximum longitude',
    //       HttpStatus.BAD_REQUEST,
    //     );
    //   }

    //   // Check if bounds are too large (prevent world-wide queries)
    //   const latSpan = maxLat - minLat;
    //   const lngSpan = maxLng - minLng;
    //   const maxAllowedSpan = 30; // degrees

    //   if (latSpan > maxAllowedSpan || lngSpan > maxAllowedSpan) {
    //     throw new HttpException(
    //       `Bounds too large. Maximum allowed span is ${maxAllowedSpan} degrees. Current span: lat=${latSpan.toFixed(2)}, lng=${lngSpan.toFixed(2)}`,
    //       HttpStatus.BAD_REQUEST,
    //     );
    //   }

    //   // Get flights from FlightAware
    //   const flightAwareData = await this.flightAwareService.getFlightsInBounds(
    //     minLat,
    //     maxLat,
    //     minLng,
    //     maxLng,
    //   );

    //   // Filter and transform the data to a more user-friendly format
    //   const flights = flightAwareData.flights
    //     .filter((flight) => {
    //       // Only include flights with position data and that are airborne
    //       return (
    //         flight.last_position &&
    //         flight.last_position.latitude &&
    //         flight.last_position.longitude &&
    //         flight.status !== 'Cancelled' &&
    //         flight.status !== 'Landed'
    //       );
    //     })
    //     .map((flight) => ({
    //       ident_icao: flight.ident_icao || flight.ident,
    //       ident_iata: flight.ident_iata,
    //       callsign: flight.ident,

    //       // Position and movement data
    //       latitude: flight.last_position?.latitude,
    //       longitude: flight.last_position?.longitude,
    //       altitude: flight.last_position?.altitude,
    //       velocity: flight.last_position?.groundspeed,
    //       heading: flight.last_position?.heading,
    //       verticalRate:
    //         flight.last_position?.altitude_change === 'C'
    //           ? 1
    //           : flight.last_position?.altitude_change === 'D'
    //             ? -1
    //             : 0,
    //       lastContact:
    //         flight.last_position?.timestamp || new Date().toISOString(),
    //       isOnGround:
    //         flight.status === 'Landed' || flight.status === 'On Ground',

    //       // Aircraft details
    //       aircraftType: flight.aircraft_type,
    //       aircraftTypeFaa: flight.aircraft_type_faa,
    //       aircraftFamily: flight.aircraft_family,
    //       manufacturer: flight.manufacturer,
    //       model: flight.model,
    //       engineType: flight.engine_type,
    //       engineCount: flight.engine_count,
    //       wakeTurbulenceCategory: flight.wake_turbulence_category,

    //       // Flight plan and registration
    //       aircraftId: flight.flight_plan?.aircraft_id,
    //       aircraftRegistration: flight.flight_plan?.owner || 'Unknown',
    //       ownerIcao: flight.flight_plan?.owner_icao,
    //       ownerIata: flight.flight_plan?.owner_iata,
    //       ownerName: flight.flight_plan?.owner_name,

    //       // Operator information
    //       operator: flight.operator?.name || flight.operator?.callsign,
    //       operatorIcao: flight.operator?.icao,
    //       operatorIata: flight.operator?.iata,
    //       operatorCountry: flight.operator?.country,

    //       // Route information
    //       origin: {
    //         code: flight.origin?.code,
    //         codeIcao: flight.origin?.code_icao,
    //         codeIata: flight.origin?.code_iata,
    //         name: flight.origin?.name,
    //         city: flight.origin?.city,
    //         timezone: flight.origin?.timezone,
    //       },
    //       destination: {
    //         code: flight.destination?.code,
    //         codeIcao: flight.destination?.code_icao,
    //         codeIata: flight.destination?.code_iata,
    //         name: flight.destination?.name,
    //         city: flight.destination?.city,
    //         timezone: flight.destination?.timezone,
    //       },

    //       // Flight status and timing
    //       status: flight.status,
    //       progressPercent: flight.progress_percent,
    //       routeDistance: flight.route_distance,
    //       filedAirspeedKts: flight.filed_airspeed_kts,
    //       filedAltitude: flight.filed_altitude,

    //       // Schedule information
    //       scheduledOff: flight.scheduled_off,
    //       scheduledOn: flight.scheduled_on,
    //       estimatedOff: flight.estimated_off,
    //       estimatedOn: flight.estimated_on,
    //       actualOff: flight.actual_off,
    //       actualOn: flight.actual_on,

    //       // Delays
    //       departureDelay: flight.departure_delay,
    //       arrivalDelay: flight.arrival_delay,

    //       // Airport details
    //       gateOrigin: flight.gate_origin,
    //       gateDestination: flight.gate_destination,
    //       terminalOrigin: flight.terminal_origin,
    //       terminalDestination: flight.terminal_destination,

    //       // Additional metadata
    //       route: flight.route,
    //       type: flight.type,
    //     }));

    //   return {
    //     success: true,
    //     timestamp: new Date().toISOString(),
    //     bounds: {
    //       minLat,
    //       maxLat,
    //       minLng,
    //       maxLng,
    //     },
    //     totalAircraft: flightAwareData.flights.length,
    //     airborneAircraft: flights.length,
    //     flights,
    //   };
    // } catch (error) {
    //   console.error('Error fetching flights in bounds:', error);
    //   if (error instanceof HttpException) {
    //     throw error;
    //   }
    //   throw new HttpException(
    //     'Failed to fetch flight data for the specified region',
    //     HttpStatus.INTERNAL_SERVER_ERROR,
    //   );
    // }
  }

  async getTrackedFlights(userId: string) {
    try {
      // Fetch all flights from the database, ordered by most recently created
      const flights = await prisma.flight.findMany({
        where: {
          userId,
        },
        orderBy: {
          createdAt: 'desc',
        },
        include: {
          greatCircleDistance: true,
          flightAwareData: {
            omit: {
              faFlightId: true,
            },
          },
        },
      });

      // Collect all unique airport codes to query
      const airportCodes = new Set<string>();
      // Collect all unique airline codes to query
      const airlineIataCodes = new Set<string>();
      const airlineIcaoCodes = new Set<string>();
      flights.forEach((flight) => {
        const departure = flight.departure as any;
        const arrival = flight.arrival as any;
        const airline = (flight as any).airline as any;

        if (departure?.airport?.iata) airportCodes.add(departure.airport.iata);
        if (departure?.airport?.icao) airportCodes.add(departure.airport.icao);
        if (arrival?.airport?.iata) airportCodes.add(arrival.airport.iata);
        if (arrival?.airport?.icao) airportCodes.add(arrival.airport.icao);

        if (airline?.iata) airlineIataCodes.add(airline.iata);
        if (airline?.icao) airlineIcaoCodes.add(airline.icao);
      });

      // Parallelize airport and airline queries using Promise.allSettled
      // This allows both queries to run simultaneously and handles failures gracefully
      const airportCodesArray = Array.from(airportCodes);
      const airlineIataCodesArray = Array.from(airlineIataCodes);
      const airlineIcaoCodesArray = Array.from(airlineIcaoCodes);

      const [airportsResult, airlinesResult] = await Promise.allSettled([
        // Query airport table for all relevant airports
        airportCodesArray.length > 0
          ? prisma.airport.findMany({
              where: {
                OR: [
                  { iataCode: { in: airportCodesArray } },
                  { ident: { in: airportCodesArray } },
                ],
              },
            })
          : Promise.resolve([]),
        // Query airline table for all relevant airlines
        airlineIataCodesArray.length > 0 || airlineIcaoCodesArray.length > 0
          ? prisma.airline.findMany({
              where: {
                OR: [
                  { iata: { in: airlineIataCodesArray } },
                  { icao: { in: airlineIcaoCodesArray } },
                ],
              },
              select: {
                iata: true,
                icao: true,
                image: true,
              },
            })
          : Promise.resolve([]),
      ]);

      // Extract results, defaulting to empty arrays if queries failed
      const airports =
        airportsResult.status === 'fulfilled' ? airportsResult.value : [];
      const airlines =
        airlinesResult.status === 'fulfilled' ? airlinesResult.value : [];

      // Log errors if any occurred (but continue processing)
      if (airportsResult.status === 'rejected') {
        console.error('Error fetching airports:', airportsResult.reason);
      }
      if (airlinesResult.status === 'rejected') {
        console.error('Error fetching airlines:', airlinesResult.reason);
      }

      // Create a map for quick airport lookup by code
      const airportMap = new Map<string, any>();
      airports.forEach((airport) => {
        if (airport.iataCode) airportMap.set(airport.iataCode, airport);
        if (airport.ident) airportMap.set(airport.ident, airport);
      });

      // Create maps for quick airline lookup by code
      const airlineByIata = new Map<string, any>();
      const airlineByIcao = new Map<string, any>();
      airlines.forEach((airline) => {
        if (airline.iata) airlineByIata.set(airline.iata, airline);
        if (airline.icao) airlineByIcao.set(airline.icao, airline);
      });

      // Transform the flights to match the expected response format
      const transformedFlights = flights.map((flight) => {
        const flightData = flight as any;

        // Handle aircraft data
        const stored = flight.aircraft as any | undefined;
        const aircraft = stored
          ? {
              modeS: stored.modeS ?? stored.hexIcao,
              age: (stored.ageYears ?? stored.age)?.toString(),
              image: stored.image?.url ?? stored.image,
              attribution:
                stored.attribution ?? stored.image?.htmlAttributions?.join(' '),
              isFreighter: stored.isFreighter ?? false,
              model: stored.typeName ?? stored.modelCode ?? stored.model,
              registration:
                stored.reg ?? stored.registration ?? stored.registrataion,
              aircraft_id: stored.aircraft_id ?? stored.id?.toString(),
              payload: stored.payload,
              deliveryDate: stored.deliveryDate
                ? new Date(stored.deliveryDate).toISOString()
                : undefined,
              firstFlightDate: stored.firstFlightDate
                ? new Date(stored.firstFlightDate).toISOString()
                : undefined,
            }
          : undefined;

        // Fill in missing airport location data
        const departure = flightData.departure;
        const arrival = flightData.arrival;
        const airlineData = flightData.airline;

        if (departure?.airport) {
          const depAirport =
            airportMap.get(departure.airport.iata) ||
            airportMap.get(departure.airport.icao);

          if (depAirport) {
            departure.airport.location = departure.airport.location || {};
            if (
              !departure.airport.location.lat ||
              departure.airport.location.lat === 0
            ) {
              departure.airport.location.lat = Number(depAirport.lat);
            }
            if (
              !departure.airport.location.lon ||
              departure.airport.location.lon === 0
            ) {
              departure.airport.location.lon = Number(depAirport.long);
            }
            if (
              !departure.airport.name ||
              departure.airport.name === 'Unknown'
            ) {
              departure.airport.name = depAirport.name;
            }
            if (
              !departure.airport.municipalityName ||
              departure.airport.municipalityName === 'Unknown'
            ) {
              departure.airport.municipalityName =
                depAirport.municipality || depAirport.name;
            }
          }
        }

        if (arrival?.airport) {
          const arrAirport =
            airportMap.get(arrival.airport.iata) ||
            airportMap.get(arrival.airport.icao);

          if (arrAirport) {
            arrival.airport.location = arrival.airport.location || {};
            if (
              !arrival.airport.location.lat ||
              arrival.airport.location.lat === 0
            ) {
              arrival.airport.location.lat = Number(arrAirport.lat);
            }
            if (
              !arrival.airport.location.lon ||
              arrival.airport.location.lon === 0
            ) {
              arrival.airport.location.lon = Number(arrAirport.long);
            }
            if (!arrival.airport.name || arrival.airport.name === 'Unknown') {
              arrival.airport.name = arrAirport.name;
            }
            if (
              !arrival.airport.municipalityName ||
              arrival.airport.municipalityName === 'Unknown'
            ) {
              arrival.airport.municipalityName =
                arrAirport.municipality || arrAirport.name;
            }
          }
        }

        // Enrich airline with image from airline table when available
        if (airlineData) {
          const matchedAirline =
            (airlineData.iata && airlineByIata.get(airlineData.iata)) ||
            (airlineData.icao && airlineByIcao.get(airlineData.icao));
          if (matchedAirline?.image) {
            airlineData.image = airlineData.image || matchedAirline.image;
          }
        }

        return {
          ...flight,
          aircraft,
          departure,
          arrival,
          airline: airlineData,
        };
      });

      return {
        success: true,
        total: transformedFlights.length,
        flights: transformedFlights,
      };
    } catch (error) {
      console.error('Error fetching tracked flights:', error);
      throw new HttpException(
        'Failed to fetch tracked flights',
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }
}
