<?php

namespace App\Filament\Imports;

use App\Models\Airport;
use App\Models\Fare;
use App\Models\Flight;
use App\Models\FlightBundle;
use App\Models\Subfleet;
use App\Services\AirportService;
use App\Services\FareService;
use App\Services\FlightService;
use App\Support\Days;
use App\Support\FlightTimeParser;
use App\Support\Utils;
use Filament\Actions\Imports\Exceptions\RowImportFailedException;
use Filament\Actions\Imports\ImportColumn;
use Filament\Actions\Imports\Importer;
use Filament\Actions\Imports\Models\Import;
use Filament\Forms\Components\Select;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Number;

/**
 * @property Flight $record
 */
class FlightImporter extends Importer
{
    protected static ?string $model = Flight::class;

    public static function getColumns(): array
    {
        return [
            ImportColumn::make('airline')
                ->requiredMapping()
                ->relationship(resolveUsing: 'icao')
                ->rules(['required']),

            ImportColumn::make('flight_number')
                ->requiredMapping()
                ->numeric()
                ->rules(['required', 'integer']),

            ImportColumn::make('callsign')
                ->rules(['max:4']),

            ImportColumn::make('route_code')
                ->rules(['max:5']),

            ImportColumn::make('route_leg')
                ->numeric()
                ->rules(['nullable', 'integer']),

            ImportColumn::make('dpt_airport')
                ->requiredMapping()
                ->fillRecordUsing(function (Flight $record, string $state): void {
                    $record->dpt_airport_id = self::processAirport($state)->id;
                })
                ->rules(['required']),

            ImportColumn::make('arr_airport')
                ->requiredMapping()
                ->fillRecordUsing(function (Flight $record, string $state): void {
                    $record->arr_airport_id = self::processAirport($state)->id;
                })
                ->rules(['required']),

            ImportColumn::make('bundle_id')
                ->numeric()
                ->rules(['nullable', 'integer', 'exists:flight_bundles,id'])
                ->fillRecordUsing(function (Flight $record, ?string $state, FlightImporter $importer): void {
                    if ($state !== null && $state !== '') {
                        $record->bundle_id = (int) $state;

                        return;
                    }

                    $fallback = $importer->getImportBundleId();

                    if ($fallback === null) {
                        throw new RowImportFailedException('No flight bundle resolved for row; ensure a bundle named "Default" exists or select one in the import options.');
                    }

                    $record->bundle_id = $fallback;
                }),

            ImportColumn::make('alt_airport')
                ->fillRecordUsing(function (Flight $record, ?string $state): void {
                    if ($state) {
                        $record->alt_airport_id = self::processAirport($state)->id;
                    }
                }),

            ImportColumn::make('dpt_time')
                ->rules(['max:10'])
                ->fillRecordUsing(function (Flight $record, ?string $state): void {
                    if ($state !== null && $state !== '') {
                        $record->setAttribute('departure_time', FlightTimeParser::parse($state));
                    }
                }),

            ImportColumn::make('arr_time')
                ->rules(['max:10'])
                ->fillRecordUsing(function (Flight $record, ?string $state): void {
                    if ($state !== null && $state !== '') {
                        $record->setAttribute('arrival_time', FlightTimeParser::parse($state));
                    }
                }),

            ImportColumn::make('level')
                ->numeric()
                ->rules(['nullable', 'integer']),

            ImportColumn::make('distance')
                ->numeric()
                ->rules(['nullable', 'integer']),

            ImportColumn::make('flight_time')
                ->numeric()
                ->rules(['nullable', 'integer']),

            ImportColumn::make('flight_type')
                ->requiredMapping()
                ->rules(['required', 'max:1']),

            ImportColumn::make('load_factor')
                ->numeric()
                ->rules(['nullable', 'integer']),

            ImportColumn::make('load_factor_variance')
                ->numeric()
                ->rules(['nullable', 'integer']),

            ImportColumn::make('route')
                ->fillRecordUsing(function (Flight $record, ?string $state): void {
                    $record->route = strtoupper($state);
                }),

            ImportColumn::make('pilot_pay')
                ->numeric()
                ->rules(['nullable', 'integer']),

            ImportColumn::make('notes'),

            ImportColumn::make('days')
                ->fillRecordUsing(function (Flight $record, ?string $state): void {
                    $record->days = self::setDays($state);
                }),

            ImportColumn::make('start_date')
                ->rules(['nullable', 'date']),

            ImportColumn::make('end_date')
                ->rules(['nullable', 'date']),

            ImportColumn::make('enabled')
                ->requiredMapping()
                ->boolean()
                ->rules(['required', 'boolean']),

            ImportColumn::make('event')
                ->relationship(),

            ImportColumn::make('user')
                ->relationship(),

            ImportColumn::make('owner_type')
                ->rules(['max:191']),

            ImportColumn::make('owner_id')
                ->rules(['max:36']),

            ImportColumn::make('subfleets')
                ->fillRecordUsing(function (): void {}),

            ImportColumn::make('fares')
                ->fillRecordUsing(function (): void {}),

            ImportColumn::make('fields')
                ->fillRecordUsing(function (): void {}),
        ];
    }

    #[\Override]
    public function resolveRecord(): Flight
    {
        return Flight::withTrashed()->firstOrNew([
            'flight_number'  => $this->data['flight_number'],
            'dpt_airport_id' => $this->data['dpt_airport'],
            'arr_airport_id' => $this->data['arr_airport'],
            'route_code'     => $this->data['route_code'],
            'route_leg'      => $this->data['route_leg'],
            'days'           => self::setDays($this->data['days']),
        ], [
            'id' => Utils::generateNewId(),
        ]);
    }

    public static function getCompletedNotificationBody(Import $import): string
    {
        $body = 'Your flight import has completed and '.Number::format($import->successful_rows).' '.str('row')->plural($import->successful_rows).' imported.';

        if (($failedRowsCount = $import->getFailedRowsCount()) !== 0) {
            $body .= ' '.Number::format($failedRowsCount).' '.str('row')->plural($failedRowsCount).' failed to import.';
        }

        return $body;
    }

    protected function beforeUpdate(): void
    {
        if ($this->record->trashed()) {
            $this->record->restore();
        }
    }

    protected function afterSave(): void
    {
        if (array_key_exists('subfleets', $this->data) && $this->data['subfleets'] != '') {
            $this->processSubfleets($this->record, $this->data['subfleets']);
        }

        if (array_key_exists('fares', $this->data) && $this->data['fares'] != '') {
            $this->processFares($this->record, $this->data['fares']);
        }

        if (array_key_exists('fields', $this->data) && $this->data['fields'] != '') {
            $this->processFields($this->record, $this->data['fields']);
        }
    }

    private static function setDays(?string $state): int
    {
        if (in_array($state, [null, '', '0'], true)) {
            return 0;
        }

        $days = [];
        if (str_contains($state, '1')) {
            $days[] = Days::MONDAY;
        }

        if (str_contains($state, '2')) {
            $days[] = Days::TUESDAY;
        }

        if (str_contains($state, '3')) {
            $days[] = Days::WEDNESDAY;
        }

        if (str_contains($state, '4')) {
            $days[] = Days::THURSDAY;
        }

        if (str_contains($state, '5')) {
            $days[] = Days::FRIDAY;
        }

        if (str_contains($state, '6')) {
            $days[] = Days::SATURDAY;
        }

        if (str_contains($state, '7')) {
            $days[] = Days::SUNDAY;
        }

        return Days::getDaysMask($days);
    }

    private function processSubfleets(Flight $flight, string $col): void
    {
        $count = 0;
        $subfleets = Utils::parseMultiColumnValues($col);
        foreach ($subfleets as $subfleet_type) {
            $subfleet_type = trim((string) $subfleet_type);
            if ($subfleet_type === '') {
                continue;
            }

            if ($subfleet_type === '0') {
                continue;
            }

            $subfleet = Subfleet::firstOrCreate(
                ['type' => $subfleet_type],
                [
                    'name'       => $subfleet_type,
                    'airline_id' => $flight->airline_id,
                ]
            );

            $subfleet->save();

            // sync
            $flight->subfleets()->syncWithoutDetaching([$subfleet->id]);
            $count++;
        }

        Log::info('Subfleets added/processed: '.$count);
    }

    private function processFares(Flight $flight, ?string $state): void
    {
        $fares = Utils::parseMultiColumnValues($state);
        foreach ($fares as $fare_code => $fare_attributes) {
            if (\is_int($fare_code)) {
                $fare_code = $fare_attributes;
                $fare_attributes = [];
            }

            $fare = Fare::firstOrCreate(['code' => $fare_code], ['name' => $fare_code]);
            app(FareService::class)->setForFlight($flight, $fare, $fare_attributes);
            $fare->save();
        }
    }

    /**
     * Parse all of the subfields
     */
    private function processFields(Flight $flight, ?string $col): void
    {
        $pass_fields = [];
        $fields = Utils::parseMultiColumnValues($col);

        foreach ($fields as $field_name => $field_value) {
            $pass_fields[] = [
                'name'  => $field_name,
                'value' => $field_value,
            ];
        }

        app(FlightService::class)->updateCustomFields($flight, $pass_fields);
    }

    private static function processAirport(string $id): Airport
    {
        $airport = app(AirportService::class)->lookupAirportIfNotFound($id);

        if (!$airport) {
            throw new RowImportFailedException('Could not find airport '.$id);
        }

        return $airport;
    }

    /**
     * Filament import-options form: lets the admin pick a target bundle for rows
     * whose `bundle_id` column is empty. Defaults to the seeded "Default" bundle.
     */
    #[\Override]
    public static function getOptionsFormComponents(): array
    {
        return [
            Select::make('bundle_id')
                ->label(__('filament.flights.fields.bundle'))
                ->relationship('bundle', 'name', modifyQueryUsing: fn ($query) => $query->orderBy('name'))
                ->default(fn (): ?int => FlightBundle::query()->where('name', 'Default')->value('id'))
                ->preload()
                ->required(),
        ];
    }

    /**
     * Resolve the bundle id to use for a row whose CSV `bundle_id` is empty.
     * Reads from the import-options form first, falls back to the "Default" bundle.
     */
    public function getImportBundleId(): ?int
    {
        $optionId = $this->getOptions()['bundle_id'] ?? null;

        if ($optionId !== null && $optionId !== '') {
            return (int) $optionId;
        }

        $id = FlightBundle::query()->where('name', 'Default')->value('id');

        return $id === null ? null : (int) $id;
    }
}
