<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Mail\ReservationEmail;
use App\Models\Reservation;
use App\Models\Payment;
use App\Models\Guest;
use App\Models\Room;
use App\Models\Document;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;

class ReservationManagementController extends Controller
{
    /**
     * Recalculate total_price and remaining_amount from guests, tour_date, extras, coupon.
     */
    private function recalcReservationTotal(Reservation $reservation): void
    {
        $reservation->load(['guests', 'tourDate', 'coupon']);
        $tourDate = $reservation->tourDate;
        if (!$tourDate) {
            return;
        }
        $total = 0.0;

        foreach ($reservation->guests as $guest) {
            $type = strtolower((string)($guest->type ?? 'adult'));
            $price = match ($type) {
                'adult', 'student' => (float)($tourDate->adult_discounted_price ?? $tourDate->adult_price ?? 0),
                'child' => (float)($tourDate->child_discounted_price ?? $tourDate->child_price ?? 0),
                'baby' => (float)($tourDate->baby_discounted_price ?? $tourDate->baby_price ?? 0),
                default => (float)($tourDate->adult_discounted_price ?? $tourDate->adult_price ?? 0),
            };
            $total += $price;

            $extras = $guest->selected_extras;
            if (is_array($extras)) {
                foreach ($extras as $e) {
                    $total += (float)($e['amount'] ?? $e['price'] ?? 0);
                }
            }
        }

        if ($reservation->coupon_id && $reservation->coupon) {
            $c = $reservation->coupon;
            if ($c->discount_type === 'percentage') {
                $total -= ($total * (float)$c->discount_value) / 100;
            } else {
                $total -= (float)$c->discount_value;
            }
        }

        $total = round(max(0, $total), 2);
        $paid = (float)$reservation->paid_amount;
        $remaining = round(max(0, $total - $paid), 2);
        $reservation->update(['total_price' => $total, 'remaining_amount' => $remaining]);
    }

    /**
     * Get all reservations (with filters)
     */
    public function index(Request $request): JsonResponse
    {
        $query = Reservation::with(['user', 'tour', 'tourDate', 'payments', 'invoice']);

        // Brand filter
        if ($request->has('brand_id')) {
            $query->where('brand_id', $request->brand_id);
        }

        // Status filter
        if ($request->has('status')) {
            $query->where('status', $request->status);
        }

        // Tour filter
        if ($request->has('tour_id')) {
            $query->where('tour_id', $request->tour_id);
        }

        // Tour date filter
        if ($request->has('tour_date_id')) {
            $query->where('tour_date_id', $request->tour_date_id);
        }

        // Search
        if ($request->has('search')) {
            $search = $request->search;
            $query->where(function ($q) use ($search) {
                $q->where('reservation_code', 'like', "%{$search}%")
                  ->orWhere('easy_code', 'like', "%{$search}%")
                  ->orWhereHas('user', function ($q) use ($search) {
                      $q->where('name', 'like', "%{$search}%")
                        ->orWhere('email', 'like', "%{$search}%");
                  });
            });
        }

        // Date range
        if ($request->has('date_from')) {
            $query->where('created_at', '>=', $request->date_from);
        }
        if ($request->has('date_to')) {
            $query->where('created_at', '<=', $request->date_to);
        }

        $reservations = $query->orderBy('created_at', 'desc')
            ->paginate($request->get('per_page', 50));

        return response()->json([
            'data' => $reservations
        ]);
    }

    /**
     * Get pending reservations
     */
    public function pending(Request $request): JsonResponse
    {
        $reservations = Reservation::with(['user', 'tour', 'tourDate'])
            ->whereIn('status', ['pending', 'awaiting_approval'])
            ->orderBy('created_at', 'asc')
            ->paginate($request->get('per_page', 50));

        return response()->json([
            'data' => $reservations
        ]);
    }

    /**
     * Get single reservation
     */
    public function show(int $id): JsonResponse
    {
        $reservation = Reservation::with([
            'user',
            'tour.dates' => fn($q) => $q->orderBy('start_date'),
            'tourDate',
            'guests',
            'rooms',
            'payments.user',
            'payments.cashDesk',
            'documents',
            'approvedBy',
            'invoice'
        ])->findOrFail($id);

        return response()->json([
            'reservation' => $reservation
        ]);
    }

    /**
     * Update reservation
     */
    public function update(Request $request, int $id): JsonResponse
    {
        $reservation = Reservation::findOrFail($id);

        $validator = \Illuminate\Support\Facades\Validator::make($request->all(), [
            'status' => 'sometimes|in:pending,awaiting_approval,approved,payment_pending,payment_completed,confirmed,cancelled',
            'admin_notes' => 'nullable|string',
            'contact_info' => 'sometimes|array',
            'flight_preference' => 'nullable|string',
            'flight_type' => 'nullable|string',
            'airline' => 'nullable|string',
            'custom_flight_notes' => 'nullable|string',
            'cancellation_policy' => 'sometimes|in:none,partial,full',
            'tour_date_id' => 'nullable|integer|exists:tour_dates,id',
            'rooms' => 'sometimes|array',
            'rooms.*.room_type' => 'nullable|string',
            'rooms.*.hotel_type' => 'nullable|string',
            'rooms.*.adults' => 'nullable|integer',
            'rooms.*.students' => 'nullable|integer',
            'rooms.*.children' => 'nullable|integer',
            'rooms.*.babies' => 'nullable|integer',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'message' => 'Validation error',
                'errors' => $validator->errors()
            ], 422);
        }

        $reservation->update($request->only([
            'status', 'admin_notes', 'contact_info',
            'flight_preference', 'flight_type', 'airline', 'custom_flight_notes',
            'cancellation_policy', 'tour_date_id',
        ]));

        if ($request->has('rooms') && is_array($request->rooms)) {
            $reservation->rooms()->delete();
            foreach ($request->rooms as $roomData) {
                $reservation->rooms()->create([
                    'room_type' => $roomData['room_type'] ?? null,
                    'hotel_type' => $roomData['hotel_type'] ?? null,
                    'adults' => (int)($roomData['adults'] ?? 0),
                    'students' => (int)($roomData['students'] ?? 0),
                    'children' => (int)($roomData['children'] ?? 0),
                    'babies' => (int)($roomData['babies'] ?? 0),
                ]);
            }
        }

        $this->recalcReservationTotal($reservation->fresh());

        return response()->json([
            'message' => 'Reservation updated successfully',
            'reservation' => $reservation->fresh(['tour', 'tourDate', 'guests', 'rooms', 'payments']),
        ]);
    }

    /**
     * Get reservations for a tour date (with guests and rooms) for Muavin/Odalama/Biletleme Excel exports.
     */
    public function forTourDate(Request $request): JsonResponse
    {
        $tourDateId = $request->get('tour_date_id');
        if (!$tourDateId) {
            return response()->json(['message' => 'tour_date_id gerekli', 'data' => []], 422);
        }
        $reservations = Reservation::with([
            'user',
            'tour',
            'tourDate',
            'guests',
            'rooms',
        ])
            ->where('tour_date_id', (int) $tourDateId)
            ->whereNotIn('status', ['cancelled'])
            ->orderBy('created_at')
            ->get();
        return response()->json(['data' => $reservations]);
    }

    /**
     * Get reports
     */
    public function report(Request $request): JsonResponse
    {
        $query = Reservation::query();

        // Date range
        if ($request->has('date_from')) {
            $query->where('created_at', '>=', $request->date_from);
        }
        if ($request->has('date_to')) {
            $query->where('created_at', '<=', $request->date_to);
        }

        // Brand filter
        if ($request->has('brand_id')) {
            $query->where('brand_id', $request->brand_id);
        }

        $stats = [
            'total_reservations' => (clone $query)->count(),
            'total_revenue' => (clone $query)->sum('total_price'),
            'total_paid' => (clone $query)->sum('paid_amount'),
            'by_status' => (clone $query)->selectRaw('status, count(*) as count')
                ->groupBy('status')
                ->pluck('count', 'status'),
            'by_brand' => (clone $query)->join('brands', 'reservations.brand_id', '=', 'brands.id')
                ->selectRaw('brands.name, count(*) as count, sum(total_price) as revenue')
                ->groupBy('brands.id', 'brands.name')
                ->get(),
        ];

        return response()->json($stats);
    }

    /**
     * Add manual payment to reservation
     */
    public function addPayment(Request $request, int $id): JsonResponse
    {
        $validator = \Illuminate\Support\Facades\Validator::make($request->all(), [
            'amount' => 'required|numeric|min:0.01',
            'method' => 'sometimes|in:bank_transfer,cash',
            'notes' => 'nullable|string',
            'cash_desk_id' => 'required|exists:cash_desks,id',
        ]);
        if ($validator->fails()) {
            return response()->json(['message' => 'Validation error', 'errors' => $validator->errors()], 422);
        }

        $reservation = Reservation::findOrFail($id);
        $amount = (float) $request->amount;
        $method = $request->get('method', 'bank_transfer');

        try {
            DB::beginTransaction();
            Payment::create([
                'reservation_id' => $reservation->id,
                'user_id' => $request->user()?->id,
                'cash_desk_id' => $request->cash_desk_id,
                'payment_method' => $method,
                'amount' => $amount,
                'status' => 'completed',
                'easy_code' => $reservation->easy_code,
                'notes' => $request->notes,
                'paid_at' => now(),
            ]);

            // Kasa bakiyesini güncelle
            $cashDesk = \App\Models\CashDesk::findOrFail($request->cash_desk_id);
            $cashDesk->increment('balance', $amount);
            $newPaid = (float) $reservation->paid_amount + $amount;
            $newRemaining = max(0, (float) $reservation->total_price - $newPaid);
            $reservation->update([
                'paid_amount' => $newPaid,
                'remaining_amount' => $newRemaining,
            ]);
            DB::commit();
            return response()->json([
                'message' => 'Ödeme kaydedildi.',
                'reservation' => $reservation->fresh(['payments']),
            ]);
        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json(['message' => 'Ödeme kaydedilemedi', 'error' => $e->getMessage()], 500);
        }
    }

    /**
     * Return HTML preview of the reservation email (for modal preview)
     */
    public function emailPreview(Request $request, int $id)
    {
        $type = $request->get('type', 'generic');
        if (!in_array($type, ['approval', 'payment_reminder', 'generic'], true)) {
            $type = 'generic';
        }
        $reservation = Reservation::with(['user', 'tour', 'tourDate', 'guests'])->findOrFail($id);
        $html = view('emails.reservation', [
            'reservation' => $reservation,
            'type' => $type,
            'customSubject' => $request->get('custom_subject'),
            'customBody' => $request->get('custom_body'),
        ])->render();
        return response($html, 200, [
            'Content-Type' => 'text/html; charset=utf-8',
        ]);
    }

    /**
     * Send templated email for reservation (approval, payment_reminder, generic)
     */
    public function sendEmail(Request $request, int $id): JsonResponse
    {
        $validator = \Illuminate\Support\Facades\Validator::make($request->all(), [
            'type' => 'required|in:approval,payment_reminder,generic',
            'custom_subject' => 'nullable|string',
            'custom_body' => 'nullable|string',
        ]);
        if ($validator->fails()) {
            return response()->json(['message' => 'Validation error', 'errors' => $validator->errors()], 422);
        }

        $reservation = Reservation::with(['user', 'tour', 'tourDate', 'guests'])->findOrFail($id);
        $email = $reservation->user?->email ?? ($reservation->contact_info['email'] ?? $reservation->contact_info['Email'] ?? null);
        if (!$email) {
            return response()->json(['message' => 'Rezervasyonda e-posta adresi bulunamadı.'], 422);
        }

        try {
            Mail::to($email)->send(new ReservationEmail(
                $reservation,
                $request->type,
                $request->custom_subject,
                $request->custom_body
            ));
            return response()->json(['message' => 'E-posta gönderildi.']);
        } catch (\Exception $e) {
            return response()->json(['message' => 'E-posta gönderilemedi', 'error' => $e->getMessage()], 500);
        }
    }

    /**
     * Create guest for reservation
     */
    public function storeGuest(Request $request, int $id): JsonResponse
    {
        $reservation = Reservation::with('rooms')->findOrFail($id);
        $validator = \Illuminate\Support\Facades\Validator::make($request->all(), [
            'first_name' => 'required|string',
            'last_name' => 'required|string',
            'type' => 'sometimes|string',
            'room_id' => 'nullable|exists:rooms,id',
            'gender' => 'nullable|string',
            'date_of_birth' => 'nullable|string',
            'passport_number' => 'nullable|string',
            'pickup_point_id' => 'nullable|integer',
            'seat_number' => 'nullable|string',
            'extra_seat_number' => 'nullable|string',
            'selected_extras' => 'nullable|array',
            'selected_extras.*.name' => 'required_with:selected_extras|string',
            'selected_extras.*.amount' => 'nullable|numeric',
        ]);
        if ($validator->fails()) {
            return response()->json(['message' => 'Validation error', 'errors' => $validator->errors()], 422);
        }

        $roomId = $request->room_id;
        if (!$roomId && $reservation->rooms->isNotEmpty()) {
            $roomId = $reservation->rooms->first()->id;
        }
        if (!$roomId) {
            $room = Room::create([
                'reservation_id' => $reservation->id,
                'room_type' => 'double',
                'adults' => 1,
                'students' => 0,
                'children' => 0,
                'babies' => 0,
            ]);
            $roomId = $room->id;
        }

        $selectedExtras = $request->selected_extras;
        if (is_array($selectedExtras)) {
            $selectedExtras = array_values(array_map(function ($e) {
                return [
                    'id' => isset($e['id']) ? (is_numeric($e['id']) ? (int) $e['id'] : $e['id']) : null,
                    'name' => $e['name'] ?? null,
                    'amount' => (float)($e['amount'] ?? $e['price'] ?? 0),
                ];
            }, $selectedExtras));
        } else {
            $selectedExtras = null;
        }

        // DB gender enum: male, female, other. Frontend may send Erkek/Kadın.
        $gender = $request->gender;
        if ($gender !== null && $gender !== '') {
            $g = mb_strtolower(trim((string) $gender));
            if (in_array($g, ['erkek', 'e', 'm'])) {
                $gender = 'male';
            } elseif (in_array($g, ['kadın', 'kadin', 'k', 'f'])) {
                $gender = 'female';
            } elseif (!in_array($g, ['male', 'female', 'other'])) {
                $gender = null;
            }
        } else {
            $gender = null;
        }

        $guest = Guest::create([
            'reservation_id' => $reservation->id,
            'room_id' => $roomId,
            'first_name' => $request->first_name,
            'last_name' => $request->last_name,
            'type' => $request->get('type', 'adult'),
            'gender' => $gender,
            'date_of_birth' => $request->date_of_birth ?: null,
            'passport_number' => $request->passport_number,
            'pickup_point_id' => ($request->pickup_point_id && (int)$request->pickup_point_id > 0) ? (int)$request->pickup_point_id : null,
            'seat_number' => $request->seat_number ?: null,
            'extra_seat_number' => $request->extra_seat_number ?: null,
            'selected_extras' => $selectedExtras,
            'created_by_name' => $request->user()?->name ?? 'Admin',
        ]);
        $this->recalcReservationTotal($reservation->fresh());
        return response()->json(['message' => 'Katılımcı eklendi.', 'guest' => $guest->fresh()]);
    }

    /**
     * Update guest
     */
    public function updateGuest(Request $request, int $reservationId, int $guestId): JsonResponse
    {
        $guest = Guest::where('reservation_id', $reservationId)->findOrFail($guestId);
        $validator = \Illuminate\Support\Facades\Validator::make($request->all(), [
            'first_name' => 'sometimes|string',
            'last_name' => 'sometimes|string',
            'type' => 'sometimes|string',
            'gender' => 'nullable|string',
            'date_of_birth' => 'nullable|string',
            'passport_number' => 'nullable|string',
            'seat_number' => 'nullable|string',
            'extra_seat_number' => 'nullable|string',
            'departure_seat_number' => 'nullable|string',
            'return_seat_number' => 'nullable|string',
            'notes' => 'nullable|string',
        ]);
        if ($validator->fails()) {
            return response()->json(['message' => 'Validation error', 'errors' => $validator->errors()], 422);
        }

        $data = $request->only([
            'first_name', 'last_name', 'type', 'date_of_birth',
            'passport_number', 'seat_number', 'extra_seat_number',
            'departure_seat_number', 'return_seat_number', 'notes',
        ]);
        $gender = $request->gender;
        if ($gender !== null && $gender !== '') {
            $g = mb_strtolower(trim((string) $gender));
            if (in_array($g, ['erkek', 'e', 'm'])) {
                $data['gender'] = 'male';
            } elseif (in_array($g, ['kadın', 'kadin', 'k', 'f'])) {
                $data['gender'] = 'female';
            } elseif (in_array($g, ['male', 'female', 'other'])) {
                $data['gender'] = $g;
            } else {
                $data['gender'] = null;
            }
        } else {
            $data['gender'] = null;
        }
        $guest->update($data);
        $this->recalcReservationTotal(Reservation::findOrFail($reservationId));
        return response()->json(['message' => 'Katılımcı güncellendi.', 'guest' => $guest->fresh()]);
    }

    /**
     * Delete guest
     */
    public function destroyGuest(int $reservationId, int $guestId): JsonResponse
    {
        $guest = Guest::where('reservation_id', $reservationId)->findOrFail($guestId);
        $guest->delete();
        $this->recalcReservationTotal(Reservation::findOrFail($reservationId));
        return response()->json(['message' => 'Katılımcı silindi.']);
    }

    /**
     * Regenerate documents for reservation (creates placeholder records; actual PDF generation can be queued)
     */
    public function regenerateDocuments(int $id): JsonResponse
    {
        $reservation = Reservation::findOrFail($id);
        Document::where('reservation_id', $reservation->id)->delete();
        Document::create([
            'reservation_id' => $reservation->id,
            'type' => 'voucher',
            'file_path' => 'pending',
            'generated_at' => now(),
        ]);
        return response()->json(['message' => 'Belgeler yeniden oluşturuluyor.', 'reservation' => $reservation->fresh('documents')]);
    }

    /**
     * Create proforma invoice (placeholder; actual PDF can be queued)
     */
    public function createProforma(int $id): JsonResponse
    {
        $reservation = Reservation::findOrFail($id);
        Document::create([
            'reservation_id' => $reservation->id,
            'type' => 'invoice',
            'file_path' => 'proforma_pending',
            'generated_at' => now(),
        ]);
        return response()->json(['message' => 'Proforma fatura oluşturuldu.', 'reservation' => $reservation->fresh('documents')]);
    }
}
