<?php

namespace App\Http\Controllers;

use App\Models\Aircraft;
use App\Models\Airline;
use App\Models\Airport;
use App\Models\FlightClass;
use App\Models\FlightReason;
use App\Models\ImportedFlight;
use App\Models\Notification;
use App\Models\SeatType;
use App\Models\User;
use App\Models\UserAction;
use App\Models\UserFlight;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Inertia\Inertia;

class FlightImportController extends Controller
{
    private function formatTime(?string $time): string
    {
        if (!$time) return '00:00';

        [$hours, $minutes] = explode(':', $time);
        return str_pad($hours, 2, '0', STR_PAD_LEFT) . ':' . $minutes;
    }

    private function selectOptions($model): array
    {
        return $model::orderBy('id')
            ->get(['id', 'name'])
            ->map(fn($item) => ['value' => $item->id, 'title' => $item->name])
            ->values()
            ->toArray();
    }

    public function getPossibleAircraft(string $aircraftQuery): array
    {
        preg_match('/\((\w+)\)/', $aircraftQuery, $matches);
        $designator = $matches[1] ?? null;

        if (!$designator) return [];

        return Aircraft::where('designator', 'ilike', $designator)
            ->orderByDesc('preferred')
            ->orderBy('model_full_name')
            ->limit(10)
            ->get(['id', 'manufacturer_code', 'model_full_name', 'designator', 'preferred'])
            ->map(fn($a) => ['value' => $a->id, 'title' => $a->display_name])
            ->values()
            ->toArray();
    }

    public function getPossibleAirports(string $airportQuery) {
        preg_match('/\((\w{3})\/(\w{4})\)/', $airportQuery, $matches);
        $iata = $matches[1] ?? null;
        $icao = $matches[2] ?? null;

        if (!$iata && !$icao) {
            return [];
        }

        $airports = Airport::with('region.country')
            ->where(function ($q) use ($iata, $icao) {
            $q->where('iata_code', 'ilike', $iata)
                ->orWhere('icao_code', 'ilike', $icao);
        })
            ->orderByRaw("
            CASE
                WHEN iata_code = ? AND icao_code = ? THEN 0
                WHEN iata_code = ? THEN 1
                WHEN icao_code = ? THEN 2
                ELSE 3
            END
        ", [$iata, $icao, $iata, $icao])
            ->limit(10)
            ->orderBy('id')
            ->get(['id', 'name', 'municipality', 'iata_code', 'icao_code', 'region_id'])
            ->map(fn(Airport $airport) => [
                'value' => $airport->id,
                'title' => $airport->display_name,
                'country_code' => strtolower($airport?->region->country->code ?? ''),
            ])
            ->values()
            ->toArray();

        return $airports;
    }

    public function getPossibleAirlines(string $airlineQuery) {
        preg_match('/\((\w{2,3})\/(\w{3,4})\)/', $airlineQuery, $matches);
        $iata = $matches[1] ?? null;
        $icao = $matches[2] ?? null;

        if(!$iata && !$icao){
            return [];
        }

        $airlines = Airline::when($iata || $icao, function ($query) use ($iata, $icao) {
            $query->orderByRaw("
            CASE
                WHEN \"iata_code\" = ? AND \"icao_code\" = ? THEN 0
                WHEN \"iata_code\" = ? THEN 1
                WHEN \"icao_code\" = ? THEN 2
                ELSE 3
            END
        ", [$iata, $icao, $iata, $icao])
                ->where(function ($q) use ($iata, $icao) {
                    $q->where('iata_code', $iata)
                        ->orWhere('icao_code', $icao);
                });
        })
            ->orderByDesc('active')
            ->limit(10)
            ->get(['id', 'name', 'iata_code', 'icao_code', 'internal_name'])
            ->map(fn($airline) => [
                'value' => $airline->id,
                'title' => $airline->display_name,
                'logo_url' => $airline->logo_url,
            ])
            ->values()
            ->toArray();


            return $airlines;
    }


    public function showFr24Import()
    {
        if (Auth::user()->importedFlights()->exists()) {
            return to_route('reconcile');
        }

        return Inertia::render('Fr24Import');
    }

    // FlightImportController.php

    public function reconcile(Request $request)
    {
        $user = Auth::user();

        $flightToReconcile = ImportedFlight::where('user_id', $user->id)->orderBy('date', 'asc')->first();

        if (!$flightToReconcile) {
            return to_route('import.fr24');
        }

        $date = $flightToReconcile->date
            ? Carbon::createFromFormat('Y-m-d', $flightToReconcile->date)->format('Y-m-d')
            : null;

        $flight = [
            'imported_flight_id' => $flightToReconcile->id,
            'flight_classes'     => $this->selectOptions(FlightClass::class),
            'flight_reasons'     => $this->selectOptions(FlightReason::class),
            'seat_types'         => $this->selectOptions(SeatType::class),
            'flight_number'      => $flightToReconcile->flight_number ?? '',
            'date'               => $date ?? '',
            'dep_time'           => $this->formatTime($flightToReconcile->dep_time),
            'arr_time'           => $this->formatTime($flightToReconcile->arr_time),
            'duration'           => $this->formatTime($flightToReconcile->duration),
            'registration'       => $flightToReconcile->registration ?? '',
            'note'               => $flightToReconcile->note ?? '',
            'flight_class'       => $flightToReconcile->flight_class !== null ? (int) $flightToReconcile->flight_class : null,
            'seat_type'          => $flightToReconcile->seat_type !== null ? (int) $flightToReconcile->seat_type : null,
            'flight_reason'      => $flightToReconcile->flight_reason !== null ? (int) $flightToReconcile->flight_reason : null,
            'seat_number'        => $flightToReconcile->seat_number ?? '',
            'airline_options'    => $this->getPossibleAirlines($flightToReconcile->airline ?? ''),
            'to_options'         => $this->getPossibleAirports($flightToReconcile->to ?? ''),
            'from_options'       => $this->getPossibleAirports($flightToReconcile->from ?? ''),
            'aircraft_options'   => $this->getPossibleAircraft($flightToReconcile->aircraft ?? ''),
        ];

        return Inertia::render('ReconcileFlight', [
            'flight' => $flight,
            'key'    => $flight['imported_flight_id'],
        ]);
    }

    public function save(Request $request)
    {
        $validated = $request->validate([
            'date'             => 'required|date',
            'imported_flight_id' => 'required|exists:imported_flights,id',
            'flight_number'    => 'nullable|string',
            'from_id'          => 'required|integer|exists:airports,id',
            'to_id'            => 'required|integer|exists:airports,id',
            'dep_time'         => 'nullable|date_format:H:i',
            'arr_time'         => 'nullable|date_format:H:i',
            'duration'         => 'required|date_format:H:i',
            'airline_id'       => 'nullable|integer|exists:airlines,id',
            'aircraft_id'      => 'nullable|integer|exists:aircraft,id',
            'registration'     => 'nullable|string',
            'seat_number'      => 'nullable|string',
            'seat_type_id'     => 'required|integer|exists:seat_types,id',
            'flight_class_id'  => 'required|integer|exists:flight_classes,id',
            'flight_reason_id' => 'required|integer|exists:flight_reasons,id',
            'note'             => 'nullable|string',
        ], [
            'imported_flight_id.required' => 'The flight you are trying to reconcile needs to be reimported or refreshed.',
            'from_id.required'       => 'Please select a departure airport.',
            'to_id.required'         => 'Please select an arrival airport.',
            'dep_time.date_format'   => 'Departure time must be in HH:MM format, i.e: 01:25',
            'arr_time.date_format'   => 'Arrival time must be in HH:MM format, i.e: 12:25',
            'duration.date_format'   => 'Must be in HH:MM format, e.g: 03:37',
            'duration.required'      => 'A duration is required to be able to accurately calculate the arrival date',
        ]);

        $user = Auth::user();

        $departureAirport = Airport::find($validated['from_id']);
        $arrivalAirport   = Airport::find($validated['to_id']);

        // Parse departure in local airport timezone, then convert to UTC
        $depTime   = $validated['dep_time'] ?? '00:00';
        $departure = Carbon::createFromFormat(
            'Y-m-d H:i',
            $validated['date'] . ' ' . $depTime,
            $departureAirport->timezone
        )->utc();

        // Calculate duration-based arrival in UTC
        [$durationHours, $durationMinutes] = explode(':', $validated['duration']);
        $durationArrival = $departure->copy()
            ->addHours((int) $durationHours)
            ->addMinutes((int) $durationMinutes);

        // If arrival time provided, parse it in arrival airport timezone and convert to UTC
        if (!empty($validated['arr_time'])) {
            $arrival = Carbon::createFromFormat(
                'Y-m-d H:i',
                $validated['date'] . ' ' . $validated['arr_time'],
                $arrivalAirport->timezone
            )->utc();

            // If arrival is not after departure, fall back to duration-based arrival
            if ($arrival->lte($departure)) {
                $arrival = $durationArrival;
            }
        } else {
            $arrival = $durationArrival;
        }

        $newFlight = UserFlight::create([
            'user_id'               => $user->id,
            'departure_date'        => $departure,
            'arrival_date'          => $arrival,
            'flight_number'         => $validated['flight_number'],
            'departure_airport_id'  => $validated['from_id'],
            'arrival_airport_id'    => $validated['to_id'],
            'airline_id'            => $validated['airline_id'] ?? null,
            'aircraft_id'           => $validated['aircraft_id'] ?? null,
            'aircraft_registration' => $validated['registration'] ?? null,
            'seat_number'           => $validated['seat_number'] ?? null,
            'seat_type_id'          => $validated['seat_type_id'] ?? 0,
            'flight_class_id'       => $validated['flight_class_id'] ?? 0,
            'flight_reason_id'      => $validated['flight_reason_id'] ?? 0,
            'note'                  => $validated['note'] ?? null,
        ]);

        UserAction::create([
            'user_id'        => $newFlight->user_id,
            'type'           => $newFlight->departure_date->isFuture() ? 'flight_booked' : 'flight_imported',
            'data'           => [
                'flight' => $newFlight->snapshot($newFlight->id),
            ],
        ]);


        ImportedFlight::destroy($validated['imported_flight_id']);
        return to_route('reconcile');
    }

    private function validateCsvFormat(string $path): ?string
    {
        $handle = fopen($path, 'r');

        // Must have empty first line
        $firstLine = fgetcsv($handle);
        if (!empty(array_filter($firstLine))) {
            fclose($handle);
            return 'CSV must match the MyFlightRadar24 export format.';
        }

        // Validate headers
        $expectedHeaders = [
            'date', 'flight_number', 'from', 'to', 'dep_time', 'arr_time',
            'duration', 'airline', 'aircraft', 'registration', 'seat_number',
            'seat_type', 'flight_class', 'flight_reason', 'note',
            'dep_id', 'arr_id', 'airline_id', 'aircraft_id'
        ];

        $headers = array_map(
            fn($h) => strtolower(trim(str_replace([' ', '"'], ['_', ''], $h))),
            fgetcsv($handle)
        );

        if ($headers !== $expectedHeaders) {
            fclose($handle);
            return 'CSV headers do not match the expected format.';
        }

        // Validate data rows
        $row = 1;
        while (($data = fgetcsv($handle)) !== false) {
            $row++;

            if (count($data) !== count($expectedHeaders)) {
                fclose($handle);
                return "Row {$row} has the wrong number of columns.";
            }

            $combined = array_combine($expectedHeaders, $data);

            if (empty($combined['date']) || empty($combined['from']) || empty($combined['to'])) {
                fclose($handle);
                return "Row {$row} is missing a required field (date, from, or to).";
            }

            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $combined['date'])) {
                fclose($handle);
                return "Row {$row} has an invalid date format. Expected YYYY-MM-DD, got '{$combined['date']}'.";
            }
        }

        fclose($handle);
        return null;
    }

    public function store(Request $request)
    {
        try {
            $request->validate([
                'csv' => ['required', 'file', 'mimes:csv,txt', 'max:10240'],
            ]);

            $path = $request->file('csv')->getRealPath();

            $validationError = $this->validateCsvFormat($path);
            if ($validationError) {
                return response()->json(['message' => $validationError], 422);
            }

            $handle = fopen($path, 'r');

            fgetcsv($handle); // skip blank first line

            // Read and normalise header row
            $headers = array_map(
                fn($h) => strtolower(trim(str_replace(' ', '_', $h))),
                fgetcsv($handle)
            );

            \Log::debug('CSV headers', $headers);

            $map = [
                'date'          => 'date',
                'flight_number' => 'flight_number',
                'from'          => 'from',
                'to'            => 'to',
                'dep_time'      => 'dep_time',
                'arr_time'      => 'arr_time',
                'duration'      => 'duration',
                'airline'       => 'airline',
                'aircraft'      => 'aircraft',
                'registration'  => 'registration',
                'seat_number'   => 'seat_number',
                'seat_type'     => 'seat_type',
                'flight_class'  => 'flight_class',
                'flight_reason' => 'flight_reason',
                'note'          => 'note',
            ];

            $userId = Auth::id();
            $imported = 0;

            while (($row = fgetcsv($handle)) !== false) {
                if ($imported === 0) {
                    \Log::debug('First row', $row);
                    \Log::debug('Header count: ' . count($headers) . ', Row count: ' . count($row));
                }

                if (count($row) !== count($headers)) {
                    continue; // skip malformed rows
                }

                $raw = array_combine($headers, $row);
                $data = ['user_id' => $userId];

                foreach ($map as $csvKey => $column) {
                    $data[$column] = $raw[$csvKey] ?? null;
                }

                ImportedFlight::create($data);
                $imported++;
            }

            fclose($handle);

            return response()->json(['imported' => $imported]);
        } catch (\Exception $e) {
            return response()->json(['message' => $e->getMessage()], 500);
        }
    }
}
