<?php

namespace App\Services;

use App\Data\BookingData;
use App\Data\BookingResponseData;
use App\Data\RentalAgreementData;
use App\Models\Booking;
use App\Models\Property;
use App\Models\RentalAgreement;
use App\Models\User;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Pagination\LengthAwarePaginator;

class BookingService
{
    /**
     * Create a new booking
     */
    public function createBooking(BookingData $bookingData, User $tenant): Booking
    {
        // Get the property to find the landlord
        $property = Property::findOrFail($bookingData->property_id);
        
        // Validate that the property is available
        if ($property->status !== Property::STATUS_AVAILABLE) {
            throw new \Exception('Property is not available for booking');
        }

        // Check if tenant is not trying to book their own property
        if ($property->landlord_id === $tenant->id) {
            throw new \Exception('Cannot book your own property');
        }

        // Check for overlapping bookings
        $this->validateNoOverlappingBookings($bookingData);

        // Create the booking
        $booking = Booking::create([
            'property_id' => $bookingData->property_id,
            'tenant_id' => $tenant->id,
            'landlord_id' => $property->landlord_id,
            'check_in_date' => $bookingData->check_in_date,
            'check_out_date' => $bookingData->check_out_date,
            'status' => Booking::STATUS_PENDING,
        ]);

        return $booking->load(['property', 'tenant', 'landlord']);
    }

    /**
     * Update booking status (for landlords)
     */
    public function updateBookingStatus(int $bookingId, int $status, User $landlord): Booking
    {
        $booking = Booking::where('id', $bookingId)
            ->where('landlord_id', $landlord->id)
            ->firstOrFail();

        // Validate status transition
        $this->validateStatusTransition($booking->status, $status);

        $booking->update(['status' => $status]);

        // If approved, create rental agreement
        if ($status === Booking::STATUS_APPROVED) {
            $this->createRentalAgreement($booking);
        }

        return $booking->load(['property', 'tenant', 'landlord', 'rentalAgreement']);
    }

    /**
     * Cancel a booking (for tenants)
     */
    public function cancelBooking(int $bookingId, User $tenant): Booking
    {
        $booking = Booking::where('id', $bookingId)
            ->where('tenant_id', $tenant->id)
            ->firstOrFail();

        if (!$booking->isPending()) {
            throw new \Exception('Only pending bookings can be cancelled');
        }

        $booking->update(['status' => Booking::STATUS_CANCELLED]);

        return $booking->load(['property', 'tenant', 'landlord']);
    }

    /**
     * Get booking by ID
     */
    public function getBookingById(int $bookingId): ?Booking
    {
        return Booking::with(['property', 'tenant', 'landlord', 'rentalAgreement'])
            ->find($bookingId);
    }

    /**
     * Get tenant's bookings
     */
    public function getTenantBookings(User $tenant, array $filters = []): LengthAwarePaginator
    {
        $query = Booking::with(['property', 'landlord', 'rentalAgreement'])
            ->where('tenant_id', $tenant->id);

        // Apply filters
        if (isset($filters['status'])) {
            $query->where('status', $filters['status']);
        }

        if (isset($filters['property_id'])) {
            $query->where('property_id', $filters['property_id']);
        }

        if (isset($filters['check_in_from'])) {
            $query->where('check_in_date', '>=', $filters['check_in_from']);
        }

        if (isset($filters['check_in_to'])) {
            $query->where('check_in_date', '<=', $filters['check_in_to']);
        }

        return $query->orderBy('created_at', 'desc')
            ->paginate($filters['per_page'] ?? 15);
    }

    /**
     * Get landlord's bookings
     */
    public function getLandlordBookings(User $landlord, array $filters = []): LengthAwarePaginator
    {
        $query = Booking::with(['property', 'tenant', 'rentalAgreement'])
            ->where('landlord_id', $landlord->id);

        // Apply filters
        if (isset($filters['status'])) {
            $query->where('status', $filters['status']);
        }

        if (isset($filters['property_id'])) {
            $query->where('property_id', $filters['property_id']);
        }

        if (isset($filters['tenant_id'])) {
            $query->where('tenant_id', $filters['tenant_id']);
        }

        if (isset($filters['check_in_from'])) {
            $query->where('check_in_date', '>=', $filters['check_in_from']);
        }

        if (isset($filters['check_in_to'])) {
            $query->where('check_in_date', '<=', $filters['check_in_to']);
        }

        return $query->orderBy('created_at', 'desc')
            ->paginate($filters['per_page'] ?? 15);
    }

    /**
     * Get all bookings (for admins)
     */
    public function getAllBookings(array $filters = []): LengthAwarePaginator
    {
        $query = Booking::with(['property', 'tenant', 'landlord', 'rentalAgreement']);

        // Apply filters
        if (isset($filters['status'])) {
            $query->where('status', $filters['status']);
        }

        if (isset($filters['property_id'])) {
            $query->where('property_id', $filters['property_id']);
        }

        if (isset($filters['tenant_id'])) {
            $query->where('tenant_id', $filters['tenant_id']);
        }

        if (isset($filters['landlord_id'])) {
            $query->where('landlord_id', $filters['landlord_id']);
        }

        if (isset($filters['check_in_from'])) {
            $query->where('check_in_date', '>=', $filters['check_in_from']);
        }

        if (isset($filters['check_in_to'])) {
            $query->where('check_in_date', '<=', $filters['check_in_to']);
        }

        return $query->orderBy('created_at', 'desc')
            ->paginate($filters['per_page'] ?? 15);
    }

    /**
     * Create rental agreement for approved booking
     */
    private function createRentalAgreement(Booking $booking): RentalAgreement
    {
        return RentalAgreement::create([
            'booking_id' => $booking->id,
            'property_id' => $booking->property_id,
            'tenant_id' => $booking->tenant_id,
            'landlord_id' => $booking->landlord_id,
            'start_date' => $booking->check_in_date,
            'end_date' => $booking->check_out_date,
            'monthly_rent' => $booking->property->monthly_rent,
            'deposit_amount' => $booking->property->deposit_amount,
            'is_active' => true,
        ]);
    }

    /**
     * Validate no overlapping bookings
     */
    private function validateNoOverlappingBookings(BookingData $bookingData): void
    {
        $query = Booking::where('property_id', $bookingData->property_id)
            ->whereIn('status', [
                Booking::STATUS_PENDING,
                Booking::STATUS_APPROVED,
                Booking::STATUS_COMPLETED
            ]);

        if ($bookingData->check_out_date) {
            // Check for overlapping date ranges
            $query->where(function ($q) use ($bookingData) {
                $q->whereBetween('check_in_date', [$bookingData->check_in_date, $bookingData->check_out_date])
                  ->orWhereBetween('check_out_date', [$bookingData->check_in_date, $bookingData->check_out_date])
                  ->orWhere(function ($q2) use ($bookingData) {
                      $q2->where('check_in_date', '<=', $bookingData->check_in_date)
                         ->where('check_out_date', '>=', $bookingData->check_out_date);
                  });
            });
        } else {
            // For open-ended bookings, check if any existing booking overlaps
            $query->where(function ($q) use ($bookingData) {
                $q->where('check_in_date', '<=', $bookingData->check_in_date)
                  ->where(function ($q2) use ($bookingData) {
                      $q2->whereNull('check_out_date')
                         ->orWhere('check_out_date', '>=', $bookingData->check_in_date);
                  });
            });
        }

        if ($query->exists()) {
            throw new \Exception('Property is already booked for the selected dates');
        }
    }

    /**
     * Validate status transition
     */
    private function validateStatusTransition(int $currentStatus, int $newStatus): void
    {
        $validTransitions = [
            Booking::STATUS_PENDING => [
                Booking::STATUS_APPROVED,
                Booking::STATUS_REJECTED,
                Booking::STATUS_CANCELLED
            ],
            Booking::STATUS_APPROVED => [
                Booking::STATUS_CANCELLED,
                Booking::STATUS_COMPLETED
            ],
            Booking::STATUS_REJECTED => [
                Booking::STATUS_PENDING // Allow resubmission
            ],
            Booking::STATUS_CANCELLED => [
                Booking::STATUS_PENDING // Allow resubmission
            ],
        ];

        if (!in_array($newStatus, $validTransitions[$currentStatus] ?? [])) {
            throw new \Exception('Invalid status transition');
        }
    }

    /**
     * Format booking for response
     */
    public function formatBookingForResponse(Booking $booking): array
    {
        return BookingResponseData::fromBooking($booking)->toArray();
    }

    /**
     * Format bookings for response
     */
    public function formatBookingsForResponse(LengthAwarePaginator $bookings): array
    {
        // Format each booking for response
        $formattedBookings = [];
        foreach ($bookings->items() as $booking) {
            $formattedBookings[] = $this->formatBookingForResponse($booking);
        }

        return [
            'data' => $formattedBookings,
            'pagination' => [
                'current_page' => $bookings->currentPage(),
                'per_page' => $bookings->perPage(),
                'total' => $bookings->total(),
                'last_page' => $bookings->lastPage(),
                'from' => $bookings->firstItem(),
                'to' => $bookings->lastItem(),
            ]
        ];
    }
}
