<?php

namespace App\Services\Operation;

use App\Models\DayTour\DayTourDate;
use App\Models\InternationalTour\InternationalTourDate;
use App\Models\Product\DayTripTourDate;
use App\Models\Sale\Sale;
use App\Models\Tour\TourDate;
use Illuminate\Database\Eloquent\Model;

/**
 * Tur kalkışlarının kapasite/envanterini satışlardan türeten servis.
 *
 * Yaklaşım: artımlı mutasyon yerine **idempotent yeniden sayım (recount)**.
 * Bir kalkışa bağlı tüm satışların yolcu sayıları duruma göre toplanır
 * (rezerve vs satılmış) ve tarih modelinin kapasite kolonlarına yazılır.
 * Böylece satış güncelleme/iptal/silme senaryolarında sayaçlar asla kaymaz.
 */
class DepartureInventoryService
{
    /** getProductDates() front-end date_type değerleri → model sınıfı */
    public const FRONTEND_TYPE_MAP = [
        'tour_date' => TourDate::class,             // kültür turu
        'international_date' => InternationalTourDate::class,
        'day_tour_date' => DayTourDate::class,      // günübirlik
        'day_trip_date' => DayTripTourDate::class,  // legacy günübirlik
    ];

    /** Product.productable_type → ilgili tarih modeli sınıfı */
    public const PRODUCTABLE_DATE_MAP = [
        \App\Models\Tour\Tour::class => TourDate::class,
        \App\Models\InternationalTour\InternationalTour::class => InternationalTourDate::class,
        \App\Models\DayTour\DayTour::class => DayTourDate::class,
        \App\Models\Product\DayTripTour::class => DayTripTourDate::class,
    ];

    /** "Satılmış" sayılan satış durumları */
    public const SOLD_STATUSES = ['confirmed', 'in_progress', 'completed'];

    /** "Rezerve" sayılan (henüz kesinleşmemiş) satış durumları */
    public const RESERVED_STATUSES = ['potential', 'offer_sent', 'revised', 'pending'];

    /**
     * Front-end date_type değerini model sınıfına çevirir.
     */
    public function classForFrontendType(?string $frontendType): ?string
    {
        return self::FRONTEND_TYPE_MAP[$frontendType] ?? null;
    }

    /**
     * Ürünün productable_type'ından tarih modeli sınıfını çevirir.
     */
    public function dateClassForProductable(?string $productableType): ?string
    {
        return self::PRODUCTABLE_DATE_MAP[$productableType] ?? null;
    }

    /**
     * Tam sınıf adı veya front-end tipi + id'den tarih modelini çözer.
     */
    public function resolve(?string $typeOrClass, ?int $id): ?Model
    {
        if (!$typeOrClass || !$id) {
            return null;
        }

        $class = class_exists($typeOrClass)
            ? $typeOrClass
            : $this->classForFrontendType($typeOrClass);

        if (!$class || !class_exists($class)) {
            return null;
        }

        return $class::find($id);
    }

    /**
     * Bir satışa bağlı tarih modelini yeniden sayar (varsa).
     */
    public function recountForSale(Sale $sale): void
    {
        $date = $this->resolve($sale->tour_date_type, $sale->tour_date_id);
        if ($date) {
            $this->recount($date);
        }
    }

    /**
     * Bir tarih modeline bağlı tüm satışları toplayıp kapasite kolonlarını günceller.
     */
    public function recount(Model $date): void
    {
        [$sold, $reserved] = $this->tallySales($date);

        match (true) {
            $date instanceof TourDate,
            $date instanceof InternationalTourDate => $this->applyCapacityColumns($date, $sold, $reserved),
            $date instanceof DayTourDate => $this->applySeatColumns($date, $sold, $reserved),
            $date instanceof DayTripTourDate => $this->applyDayTripColumns($date, $sold, $reserved),
            default => null,
        };
    }

    /**
     * Kalkışa bağlı satışların yolcu sayısını [satılmış, rezerve] olarak döndürür.
     * İptal/iade satışlar hariç tutulur.
     */
    public function tallySales(Model $date): array
    {
        $paxExpr = 'COALESCE(adult_count,0) + COALESCE(child_count,0) + COALESCE(infant_count,0)';

        $row = Sale::where('tour_date_type', get_class($date))
            ->where('tour_date_id', $date->getKey())
            ->selectRaw("
                SUM(CASE WHEN status IN ('" . implode("','", self::SOLD_STATUSES) . "') THEN $paxExpr ELSE 0 END) as sold,
                SUM(CASE WHEN status IN ('" . implode("','", self::RESERVED_STATUSES) . "') THEN $paxExpr ELSE 0 END) as reserved
            ")
            ->first();

        return [(int) ($row->sold ?? 0), (int) ($row->reserved ?? 0)];
    }

    /** cultural_tour_dates / international_tour_dates */
    protected function applyCapacityColumns(Model $date, int $sold, int $reserved): void
    {
        $total = (int) ($date->total_capacity ?? 0);

        $fill = [
            'sold_count' => $sold,
            'reserved_count' => $reserved,
            'remaining_capacity' => max(0, $total - $sold - $reserved),
        ];

        // Min katılımcıya ulaşınca otomatik kesin kalkış (yalnızca seçenek işaretliyse)
        if ($date->auto_guarantee_on_min && !$date->guarantee_departure) {
            $min = (int) ($date->min_participants ?? 0);
            if ($min > 0 && ($sold + $reserved) >= $min) {
                $fill['guarantee_departure'] = true;
            }
        }

        $date->forceFill($fill)->saveQuietly();

        // Otomatik görev: bayrak bu yoldan flip'lendiyse (auto_guarantee_on_min,
        // OperationService::markGuaranteed'den geçmez). Çift ateşleme dedupe ile tek göreve çöker.
        if (!empty($fill['guarantee_departure'])) {
            app(\App\Services\Task\AutoTaskService::class)->fireSafe('departure_guaranteed', $date, [
                'agency_id' => $date->tour?->agency_id,
                'tour' => $date->tour?->name ?? 'Tur',
                'date' => $date->start_date ? \Illuminate\Support\Carbon::parse($date->start_date)->format('d.m.Y') : '',
                'mode_label' => 'otomatik',
            ]);
        }
    }

    /** day_tour_dates (koltuk terminolojisi) */
    protected function applySeatColumns(DayTourDate $date, int $sold, int $reserved): void
    {
        $max = (int) ($date->max_participants ?? $date->dayTour?->max_participants ?? 0);

        $fill = [
            'booked_seats' => $sold,
            'reserved_seats' => $reserved,
            'available_seats' => max(0, $max - $sold - $reserved),
        ];

        // Min katılımcıya ulaşınca otomatik kesin kalkış (yalnızca seçenek işaretliyse).
        // Günübirlikte guarantee_departure yok; karşılığı is_guaranteed + guaranteed_at + status.
        if ($date->auto_guarantee_on_min && !$date->is_guaranteed) {
            $min = (int) ($date->dayTour?->min_participants ?? 0);
            if ($min > 0 && ($sold + $reserved) >= $min) {
                $fill['is_guaranteed'] = true;
                $fill['guaranteed_at'] = now();
                if ($date->status === 'available') {
                    $fill['status'] = 'guaranteed';
                }
            }
        }

        $date->forceFill($fill)->saveQuietly();

        // Otomatik görev: günübirlik kalkış bu yoldan kesinleştiyse
        if (!empty($fill['is_guaranteed'])) {
            app(\App\Services\Task\AutoTaskService::class)->fireSafe('departure_guaranteed', $date, [
                'agency_id' => $date->dayTour?->agency_id,
                'tour' => $date->dayTour?->name ?? 'Tur',
                'date' => $date->tour_date ? \Illuminate\Support\Carbon::parse($date->tour_date)->format('d.m.Y') : '',
                'mode_label' => 'otomatik',
            ]);
        }
    }

    /** day_trip_tour_dates (legacy — tek sold_count kolonu) */
    protected function applyDayTripColumns(DayTripTourDate $date, int $sold, int $reserved): void
    {
        $total = (int) ($date->total_capacity ?? 0);
        $used = $sold + $reserved;

        $date->forceFill([
            'sold_count' => $used,
            'remaining_capacity' => max(0, $total - $used),
        ])->saveQuietly();
    }
}
