<?php

namespace App\Http\Controllers\Agency\Operation;

use App\Http\Controllers\Controller;
use App\Models\Sale\Sale;
use App\Models\Sale\SaleRoom;
use App\Services\Operation\OperationService;
use App\Services\Operation\RoomingService;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

/**
 * Operasyon kartındaki "Odalama" sekmesinin yazma uçları.
 *
 * Odaların şeması satış modülüne aittir (`sale_rooms` / `SaleRoom`); buradaki
 * işlemler kalkış bağlamında yetki kontrolünden geçer: oda, kalkışa bağlı ve
 * bu acenteye ait bir satışın odası olmak zorundadır.
 *
 * Odalama yalnızca konaklamalı kalkışlarda anlamlıdır (OperationService::isOvernight);
 * günübirlik kalkışlarda uçlar 422 döner. Özel tur (is_private) ayrımı etkilemez.
 */
class RoomingController extends Controller
{
    public function __construct(
        private OperationService $ops,
        private RoomingService $rooming,
    ) {
    }

    private function agencyId(): int
    {
        return Auth::user()->currentAgencyId();
    }

    /** Tip anahtarı + id'den kalkışı çözer; yoksa 404, başka acenteninse 403. */
    private function resolveOrFail(string $type, int $id): Model
    {
        $date = $this->ops->resolveByKey($type, $id);
        abort_if(!$date, 404);

        $tour = $this->ops->tourOf($date);
        abort_if(!$tour || (int) $tour->agency_id !== $this->agencyId(), 403, 'Bu işlem için yetkiniz yok.');

        return $date;
    }

    /** Konaklamalı kalkış + kalkışa bağlı satış; ikisi de sağlanmazsa null. */
    private function saleOfDeparture(Model $date, int $saleId): ?Sale
    {
        return Sale::byAgency($this->agencyId())
            ->where('tour_date_type', get_class($date))
            ->where('tour_date_id', $date->id)
            ->whereNotIn('status', ['cancelled', 'refunded'])
            ->with(['passengers', 'rooms'])
            ->find($saleId);
    }

    private function guardOvernight(Model $date): ?\Illuminate\Http\JsonResponse
    {
        if ($this->ops->isOvernight($date)) {
            return null;
        }

        return response()->json([
            'success' => false,
            'message' => 'Bu kalkış günübirlik; odalama yapılamaz.',
        ], 422);
    }

    /** Yolcuyu odaya yerleştirir; room_id boşsa odadan çıkarır. */
    public function assign(Request $request, string $type, int $id)
    {
        $date = $this->resolveOrFail($type, $id);
        if ($stop = $this->guardOvernight($date)) {
            return $stop;
        }

        $data = $request->validate([
            'sale_id' => 'required|integer',
            'passenger_id' => 'required|integer',
            'room_id' => 'nullable|integer',
        ]);

        $sale = $this->saleOfDeparture($date, $data['sale_id']);
        if (!$sale) {
            return response()->json(['success' => false, 'message' => 'Rezervasyon bu kalkışta bulunamadı.'], 404);
        }

        try {
            $room = $this->rooming->assign(
                $sale,
                (int) $data['passenger_id'],
                $data['room_id'] ?: null,
                $this->ops->startDateOf($date),
            );
        } catch (\RuntimeException $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()], 422);
        }

        return response()->json([
            'success' => true,
            'message' => $room
                ? '"' . $this->rooming->labelOf($room) . '" odasına yerleştirildi.'
                : 'Yolcu odadan çıkarıldı.',
            'room_id' => $room?->id,
            'room_label' => $room ? $this->rooming->labelOf($room) : null,
        ]);
    }

    /** Satışta oda tanımlanmamışsa operasyon tarafından yeni oda açar. */
    public function storeRoom(Request $request, string $type, int $id)
    {
        $date = $this->resolveOrFail($type, $id);
        if ($stop = $this->guardOvernight($date)) {
            return $stop;
        }

        $data = $request->validate([
            'sale_id' => 'required|integer',
            'room_type' => ['required', 'string', 'in:' . implode(',', array_keys(SaleRoom::ROOM_TYPES))],
            'label' => 'nullable|string|max:60',
            'capacity' => 'nullable|integer|min:1|max:12',
            'extra_bed' => 'nullable|boolean',
            'notes' => 'nullable|string|max:500',
        ]);

        $sale = $this->saleOfDeparture($date, $data['sale_id']);
        if (!$sale) {
            return response()->json(['success' => false, 'message' => 'Rezervasyon bu kalkışta bulunamadı.'], 404);
        }

        $room = $this->rooming->createRoom($sale, $data);

        return response()->json([
            'success' => true,
            'message' => '"' . $this->rooming->labelOf($room) . '" eklendi.',
            'room_id' => $room->id,
        ]);
    }

    public function updateRoom(Request $request, string $type, int $id, int $room)
    {
        $date = $this->resolveOrFail($type, $id);
        if ($stop = $this->guardOvernight($date)) {
            return $stop;
        }

        $data = $request->validate([
            'room_type' => ['nullable', 'string', 'in:' . implode(',', array_keys(SaleRoom::ROOM_TYPES))],
            'label' => 'nullable|string|max:60',
            'capacity' => 'nullable|integer|min:1|max:12',
            'extra_bed' => 'nullable|boolean',
            'notes' => 'nullable|string|max:500',
        ]);

        $model = $this->roomOfDeparture($date, $room);
        if (!$model) {
            return response()->json(['success' => false, 'message' => 'Oda bu kalkışta bulunamadı.'], 404);
        }

        $this->rooming->updateRoom($model, $data);

        return response()->json([
            'success' => true,
            'message' => 'Oda güncellendi.',
            'room_label' => $this->rooming->labelOf($model),
        ]);
    }

    public function destroyRoom(string $type, int $id, int $room)
    {
        $date = $this->resolveOrFail($type, $id);
        if ($stop = $this->guardOvernight($date)) {
            return $stop;
        }

        $model = $this->roomOfDeparture($date, $room);
        if (!$model) {
            return response()->json(['success' => false, 'message' => 'Oda bu kalkışta bulunamadı.'], 404);
        }

        $this->rooming->deleteRoom($model);

        return response()->json(['success' => true, 'message' => 'Oda silindi; içindeki yolcular atanmamışa döndü.']);
    }

    /** Oda gerçekten bu kalkışın bir satışına mı ait? (yetki kontrolü) */
    private function roomOfDeparture(Model $date, int $roomId): ?SaleRoom
    {
        $room = SaleRoom::find($roomId);
        if (!$room) {
            return null;
        }

        return $this->saleOfDeparture($date, (int) $room->sale_id) ? $room : null;
    }
}
