<?php

namespace App\Services\Operation;

use App\Models\DayTour\DayTourDate;
use App\Models\Sale\Sale;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;

/**
 * Bir kalkışın yolcu manifestosunu düz satırlara çevirir ve dışa aktarımda
 * hangi kolonların yer alacağını yöneten kolon kayıtçısını (registry) sunar.
 *
 * Tasarım: her kolonun tek bir tanımı vardır (etiket + grup + çözücü). Ekrandaki
 * tablo, kolon seçim modalı ve Excel/PDF çıktısı aynı kaynaktan beslenir; yeni
 * bir alan eklemek için tek yer değişir.
 */
class PassengerManifestService
{
    /** Kolon grupları — seçim modalında başlık olarak kullanılır. */
    public const GROUPS = [
        'passenger' => 'Yolcu Bilgileri',
        'identity' => 'Kimlik & Pasaport',
        'contact' => 'İletişim',
        'reservation' => 'Rezervasyon',
        'travel' => 'Seyahat',
        'payment' => 'Ödeme',
        'operation' => 'Operasyon',
        'flight' => 'Uçuş',
    ];

    /**
     * Hazır çıktı şablonları — "ödemelere göre", "biletlere göre" gibi.
     * `flight` anahtarı yalnızca uçaklı turlarda önerilir.
     */
    public const PRESETS = [
        'default' => [
            'label' => 'Standart Liste',
            'icon' => 'fa-list',
            'columns' => ['no', 'passenger_name', 'sale_code', 'customer_name', 'room_number', 'seat_number', 'special_request'],
        ],
        'payment' => [
            'label' => 'Ödemelere Göre',
            'icon' => 'fa-wallet',
            'columns' => ['no', 'passenger_name', 'sale_code', 'customer_name', 'sale_status', 'payment_status', 'total_amount', 'paid_amount', 'remaining_amount'],
        ],
        'ticket' => [
            'label' => 'Bilet Numaralarına Göre',
            'icon' => 'fa-plane-departure',
            'flight_only' => true,
            'columns' => ['no', 'passenger_name', 'national_id', 'passport_number', 'ticket_number', 'ticket_pnr', 'flight_no', 'seat_number', 'baggage_info'],
        ],
        'travel' => [
            'label' => 'Seyahat Bilgileriyle',
            'icon' => 'fa-route',
            'columns' => ['no', 'passenger_name', 'sale_code', 'tour_name', 'departure_date', 'departure_time', 'return_date', 'duration', 'route'],
        ],
        'rooming' => [
            'label' => 'Oda Listesi',
            'icon' => 'fa-bed',
            'columns' => ['no', 'passenger_name', 'gender', 'sale_code', 'room_number', 'special_request'],
        ],
        'contact' => [
            'label' => 'İletişim Listesi',
            'icon' => 'fa-address-book',
            'columns' => ['no', 'passenger_name', 'phone', 'email', 'customer_name', 'customer_phone'],
        ],
        'passport' => [
            'label' => 'Pasaport / Vize',
            'icon' => 'fa-passport',
            'columns' => ['no', 'passenger_name', 'birth_date', 'gender', 'national_id', 'passport_number', 'passport_expiry', 'visa_status', 'visa_expiry'],
        ],
    ];

    /**
     * Kolon tanımları.
     *
     * @return array<string, array{label: string, group: string, width?: int, flight?: bool}>
     */
    public function columns(): array
    {
        return [
            'no' => ['label' => '#', 'group' => 'passenger', 'width' => 6],
            'passenger_name' => ['label' => 'Yolcu Adı', 'group' => 'passenger', 'width' => 26],
            'gender' => ['label' => 'Cinsiyet', 'group' => 'passenger', 'width' => 10],
            'birth_date' => ['label' => 'Doğum Tarihi', 'group' => 'passenger', 'width' => 14],
            'age' => ['label' => 'Yaş', 'group' => 'passenger', 'width' => 7],
            'pax_type' => ['label' => 'Yolcu Tipi', 'group' => 'passenger', 'width' => 12],
            'relationship' => ['label' => 'Yakınlık', 'group' => 'passenger', 'width' => 12],

            'national_id' => ['label' => 'TC Kimlik No', 'group' => 'identity', 'width' => 16],
            'passport_number' => ['label' => 'Pasaport No', 'group' => 'identity', 'width' => 16],
            'passport_expiry' => ['label' => 'Pasaport Geçerlilik', 'group' => 'identity', 'width' => 18],
            'visa_status' => ['label' => 'Vize Durumu', 'group' => 'identity', 'width' => 14],
            'visa_expiry' => ['label' => 'Vize Bitiş', 'group' => 'identity', 'width' => 14],

            'phone' => ['label' => 'Telefon', 'group' => 'contact', 'width' => 16],
            'email' => ['label' => 'E-posta', 'group' => 'contact', 'width' => 24],

            'sale_code' => ['label' => 'Rezervasyon No', 'group' => 'reservation', 'width' => 15],
            'customer_name' => ['label' => 'Müşteri', 'group' => 'reservation', 'width' => 24],
            'customer_phone' => ['label' => 'Müşteri Telefonu', 'group' => 'reservation', 'width' => 16],
            'sale_status' => ['label' => 'Satış Durumu', 'group' => 'reservation', 'width' => 14],
            'agent_name' => ['label' => 'Satış Temsilcisi', 'group' => 'reservation', 'width' => 20],
            'sale_date' => ['label' => 'Satış Tarihi', 'group' => 'reservation', 'width' => 14],

            // Seyahatin kendisi — kalkış geneli, her satırda aynı değer.
            // Liste otele/havayoluna tek başına verildiğinde de anlamlı olsun diye
            // satır bazında tekrarlanır.
            'tour_name' => ['label' => 'Tur / Ürün', 'group' => 'travel', 'width' => 28],
            'tour_code' => ['label' => 'Tur Kodu', 'group' => 'travel', 'width' => 12],
            'tour_type' => ['label' => 'Tur Tipi', 'group' => 'travel', 'width' => 15],
            'departure_code' => ['label' => 'Kalkış Kodu', 'group' => 'travel', 'width' => 13],
            'departure_date' => ['label' => 'Kalkış Tarihi', 'group' => 'travel', 'width' => 14],
            'departure_time' => ['label' => 'Kalkış Saati', 'group' => 'travel', 'width' => 12],
            'return_date' => ['label' => 'Dönüş Tarihi', 'group' => 'travel', 'width' => 14],
            'return_time' => ['label' => 'Dönüş Saati', 'group' => 'travel', 'width' => 12],
            'duration' => ['label' => 'Süre', 'group' => 'travel', 'width' => 14],
            'route' => ['label' => 'Güzergâh', 'group' => 'travel', 'width' => 24],
            'transportation' => ['label' => 'Ulaşım', 'group' => 'travel', 'width' => 12],

            'payment_status' => ['label' => 'Ödeme Durumu', 'group' => 'payment', 'width' => 14],
            'total_amount' => ['label' => 'Toplam Tutar', 'group' => 'payment', 'width' => 14],
            'paid_amount' => ['label' => 'Ödenen', 'group' => 'payment', 'width' => 14],
            'remaining_amount' => ['label' => 'Kalan', 'group' => 'payment', 'width' => 14],
            'currency' => ['label' => 'Para Birimi', 'group' => 'payment', 'width' => 10],

            'room_number' => ['label' => 'Oda No', 'group' => 'operation', 'width' => 10],
            'seat_number' => ['label' => 'Koltuk No', 'group' => 'operation', 'width' => 10],
            'pickup_location' => ['label' => 'Biniş Noktası', 'group' => 'operation', 'width' => 20],
            'special_request' => ['label' => 'Özel İstek', 'group' => 'operation', 'width' => 26],
            'notes' => ['label' => 'Operasyon Notu', 'group' => 'operation', 'width' => 26],

            'ticket_number' => ['label' => 'Bilet No', 'group' => 'flight', 'width' => 18, 'flight' => true],
            'ticket_pnr' => ['label' => 'PNR', 'group' => 'flight', 'width' => 12, 'flight' => true],
            'flight_no' => ['label' => 'Uçuş No', 'group' => 'flight', 'width' => 12, 'flight' => true],
            'baggage_info' => ['label' => 'Bagaj', 'group' => 'flight', 'width' => 14, 'flight' => true],
        ];
    }

    /** Kolonlar gruplanmış hâlde (seçim modalı için). */
    public function groupedColumns(bool $isFlight = true): array
    {
        $out = [];
        foreach ($this->columns() as $key => $def) {
            if (!empty($def['flight']) && !$isFlight) {
                continue; // uçaklı tur değilse uçuş kolonları hiç sunulmaz
            }
            $out[$def['group']][$key] = $def;
        }

        return $out;
    }

    /** Uçaklı tur mu? (uçuş kolonları/hazır şablonu yalnızca burada anlamlı) */
    public function isFlightDeparture(Model $date, $tour = null): bool
    {
        if ($date instanceof DayTourDate) {
            return false; // günübirlik turda ulaşım kolonu yok
        }

        return ($tour->transportation_method ?? null) === 'air';
    }

    /** Uçaklı tura göre kullanılabilir hazır şablonlar. */
    public function presets(bool $isFlight = true): array
    {
        return array_filter(
            self::PRESETS,
            fn ($p) => $isFlight || empty($p['flight_only'])
        );
    }

    /** İstenen kolon anahtarlarını doğrular; boşsa standart şablona döner. */
    public function normalizeColumns(?array $keys, bool $isFlight = true): array
    {
        $valid = $this->columns();
        $keys = array_values(array_filter((array) $keys, function ($k) use ($valid, $isFlight) {
            if (!isset($valid[$k])) {
                return false;
            }

            return $isFlight || empty($valid[$k]['flight']);
        }));

        return $keys ?: self::PRESETS['default']['columns'];
    }

    /** Ulaşım şekli etiketleri (tur formundaki seçeneklerle aynı). */
    private const TRANSPORT_LABELS = [
        'air' => 'Uçak',
        'road' => 'Karayolu',
        'bus' => 'Otobüs',
        'minibus' => 'Minibüs',
        'private_vehicle' => 'Özel Araç',
        'train' => 'Tren',
        'ferry' => 'Feribot',
        'other' => 'Diğer',
    ];

    /**
     * Kalkışın seyahat bilgileri — tüm satırlarda aynı olan "bu yolculuk nedir"
     * verisi (tur, tarihler, süre, güzergâh, ulaşım).
     *
     * Süre ve saatler önce kalkış kaydından okunur: aynı turun iki kalkışı farklı
     * gün sayısına sahip olabilir; tur kartındaki genel değer yalnızca yedektir.
     *
     * @param array $vm OperationController::buildVM çıktısı (tarih/tip zaten çözülmüş)
     */
    public function trip(?Model $date, $tour = null, array $vm = []): array
    {
        $start = $vm['start'] ?? null;
        $end = $vm['end'] ?? null;

        return [
            'tour_name' => $vm['tour_name'] ?? ($tour->name ?? null),
            'tour_code' => $vm['tour_code'] ?? ($tour->code ?? null),
            'tour_type' => $vm['type_label'] ?? null,
            'departure_code' => $date->code ?? null,
            'departure_date' => $start?->format('d.m.Y'),
            'departure_time' => $vm['start_time'] ?? $this->timeText($date->start_time ?? null),
            'return_date' => $end?->format('d.m.Y'),
            'return_time' => $this->timeText($date->end_time ?? null),
            'duration' => $this->durationText($date, $tour, $start, $end),
            'route' => $this->routeText($tour),
            'transportation' => $this->transportText($tour),
        ];
    }

    /**
     * Kalkışın tüm yolcularını düz satırlara çevirir.
     *
     * Ayrı yolcu kaydı girilmemiş rezervasyonlar da atlanmaz; kişi sayısı kadar
     * "isimsiz" satır üretilir ki manifesto gerçek kişi sayısını göstersin.
     *
     * @param Collection<Sale> $sales
     * @param array $trip Kalkış geneli seyahat bilgisi (bkz. trip())
     * @return Collection<array<string, mixed>>
     */
    public function rows(Collection $sales, ?Carbon $departureDate = null, array $trip = []): Collection
    {
        $rows = collect();
        $i = 1;

        foreach ($sales as $sale) {
            if ($sale->passengers->count()) {
                foreach ($sale->passengers as $p) {
                    $rows->push($this->row($i++, $sale, $p, $departureDate) + $trip);
                }
                continue;
            }

            $count = max(1, (int) $sale->total_participants);
            for ($n = 1; $n <= $count; $n++) {
                $rows->push($this->row($i++, $sale, null, $departureDate, $n, $count) + $trip);
            }
        }

        return $rows;
    }

    private function timeText($raw): ?string
    {
        if (!$raw) {
            return null;
        }

        try {
            return Carbon::parse($raw)->format('H:i');
        } catch (\Throwable $e) {
            return null;
        }
    }

    /** "4 gece 5 gün" · "12 saat" · "Günübirlik" */
    private function durationText(?Model $date, $tour, ?Carbon $start, ?Carbon $end): ?string
    {
        $nights = (int) ($date->duration_nights ?? 0);
        $days = (int) ($date->duration_days ?? 0);

        // Kalkışta yazmıyorsa gerçek tarihlerden, o da yoksa tur kartından
        if (!$nights && $start && $end) {
            $nights = (int) $start->copy()->startOfDay()->diffInDays($end->copy()->startOfDay());
        }
        if (!$nights) {
            $nights = (int) ($tour->nights ?? 0);
        }
        if (!$days) {
            $days = $nights > 0 ? $nights + 1 : (int) ($tour->days ?? 0);
        }

        if ($nights > 0) {
            return $nights . ' gece ' . max($days, $nights + 1) . ' gün';
        }

        // Günübirlik: bu kalkışın kendi saat aralığı, yoksa turun genel süresi
        $hours = 0;
        $from = $this->timeText($date->start_time ?? null);
        $to = $this->timeText($date->end_time ?? null);
        if ($from && $to) {
            $hours = (int) round(Carbon::parse($from)->diffInMinutes(Carbon::parse($to)) / 60);
        }
        if ($hours <= 0) {
            $hours = (int) ($tour->duration_hours ?? 0);
        }

        return $hours > 0 ? $hours . ' saat' : 'Günübirlik';
    }

    /** "İstanbul → Roma" (şehir tanımlıysa). */
    private function routeText($tour): ?string
    {
        if (!$tour || !method_exists($tour, 'departureCity')) {
            return null;
        }

        $from = $tour->departureCity?->city;
        $to = $tour->arrivalCity?->city;

        if ($from && $to) {
            return $from === $to ? $from : $from . ' → ' . $to;
        }

        return $from ?: $to;
    }

    private function transportText($tour): ?string
    {
        $m = $tour->transportation_method ?? null;

        return $m ? (self::TRANSPORT_LABELS[$m] ?? $m) : null;
    }

    /** Tek bir manifesto satırı. */
    private function row(int $no, Sale $sale, $p, ?Carbon $departureDate, int $seq = 1, int $of = 1): array
    {
        $pivot = $p->pivot ?? null;
        $customer = $sale->customer;
        $birth = $p && $p->birth_date ? Carbon::parse($p->birth_date) : null;
        $ref = $departureDate ?: now();

        return [
            // Satır kimliği — tabloda inline düzenleme bu ikiliyle hedeflenir
            '_sale_id' => $sale->id,
            '_passenger_id' => $p->id ?? null,
            '_customer_id' => $sale->customer_id, // yolcuya tıklayınca müşteri kartı açılır
            '_named' => (bool) $p,

            // Ham kimlik değerleri — manifestodaki inline düzenleme kutuları
            // etiketlenmiş değil, kaydedilebilir ham değeri gösterir
            '_first_name' => $p->first_name ?? null,
            '_last_name' => $p->last_name ?? null,
            '_gender' => $p->gender ?? null,

            'no' => $no,
            'passenger_name' => $p
                ? ($p->full_name ?: trim(($p->first_name ?? '') . ' ' . ($p->last_name ?? '')))
                : ('İsimsiz yolcu ' . $seq . '/' . $of),
            'gender' => $this->genderLabel($p->gender ?? null),
            'birth_date' => $birth?->format('d.m.Y'),
            // Carbon 3'te diffInYears ondalıklı döner; yaş tamamlanmış yıl sayısıdır
            'age' => $birth ? (int) $birth->diffInYears($ref) : null,
            'pax_type' => $birth ? $this->paxType($birth, $ref) : null,
            'relationship' => $p->relationship ?? null,

            'national_id' => $p->national_id ?? null,
            'passport_number' => $p->passport_number ?? null,
            'passport_expiry' => $p && $p->passport_expiry ? Carbon::parse($p->passport_expiry)->format('d.m.Y') : null,
            'visa_status' => $p->visa_status ?? null,
            'visa_expiry' => $p && $p->visa_expiry ? Carbon::parse($p->visa_expiry)->format('d.m.Y') : null,

            'phone' => $p->phone ?? null,
            'email' => $p->email ?? null,

            'sale_code' => $sale->sale_code,
            'customer_name' => trim(($customer->first_name ?? '') . ' ' . ($customer->last_name ?? '')) ?: null,
            'customer_phone' => $customer->phone ?? null,
            'sale_status' => $sale->status_label,
            'agent_name' => $sale->assignedAgent
                ? trim($sale->assignedAgent->first_name . ' ' . $sale->assignedAgent->last_name)
                : null,
            'sale_date' => $sale->created_at?->format('d.m.Y'),

            'payment_status' => $sale->payment_status_label,
            'total_amount' => (float) $sale->total_amount,
            'paid_amount' => (float) $sale->paid_amount,
            'remaining_amount' => (float) $sale->remaining_amount,
            'currency' => $sale->currency,

            'room_number' => $pivot->room_number ?? null,
            'seat_number' => $pivot->seat_number ?? null,
            'pickup_location' => $sale->pickup_location,
            'special_request' => $pivot->special_request ?? null,
            'notes' => $pivot->notes ?? null,

            'ticket_number' => $pivot->ticket_number ?? null,
            'ticket_pnr' => $pivot->ticket_pnr ?? null,
            'flight_no' => $pivot->flight_no ?? $sale->flight_number,
            'baggage_info' => $pivot->baggage_info ?? null,
        ];
    }

    /** Seçili kolonlara göre başlık + veri matrisi (Excel/PDF ortak). */
    public function matrix(Collection $rows, array $columnKeys): array
    {
        $defs = $this->columns();
        $headings = array_map(fn ($k) => $defs[$k]['label'] ?? $k, $columnKeys);

        $data = $rows->map(function ($row) use ($columnKeys) {
            $line = [];
            foreach ($columnKeys as $k) {
                $v = $row[$k] ?? null;
                $line[] = $v === null ? '' : $v;
            }

            return $line;
        })->values()->all();

        return [$headings, $data];
    }

    private function genderLabel(?string $g): ?string
    {
        return match ($g) {
            'male' => 'Erkek',
            'female' => 'Kadın',
            'other' => 'Diğer',
            default => null,
        };
    }

    /** Yaşa göre yetişkin / çocuk / bebek. */
    private function paxType(Carbon $birth, Carbon $ref): string
    {
        $age = (int) $birth->diffInYears($ref);

        return match (true) {
            $age < 2 => 'Bebek',
            $age < 12 => 'Çocuk',
            default => 'Yetişkin',
        };
    }
}
