<?php

use Carbon\Carbon;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('booking_room_days', function (Blueprint $table): void {
            $table->id();
            $table->foreignId('room_id')->constrained()->cascadeOnDelete();
            $table->foreignId('booking_id')->nullable()->constrained()->cascadeOnDelete();
            $table->foreignId('booking_block_id')->nullable()->constrained('booking_blocks')->cascadeOnDelete();
            $table->date('date');
            $table->string('source', 50)->default('booking');
            $table->timestamps();

            $table->unique(['room_id', 'date'], 'booking_room_days_room_date_unique');
            $table->index(['booking_id', 'date']);
            $table->index(['booking_block_id', 'date']);
            $table->index(['date', 'room_id']);
        });

        $this->backfillExistingBookings();
        $this->backfillExistingBookingBlocks();
    }

    public function down(): void
    {
        Schema::dropIfExists('booking_room_days');
    }

    private function backfillExistingBookings(): void
    {
        $activeStatuses = ['draft', 'pending_payment', 'awaiting_prepayment', 'payment_uploaded', 'payment_rejected', 'confirmed'];

        DB::table('bookings')
            ->whereIn('status', $activeStatuses)
            ->whereNotNull('room_id')
            ->whereNotNull('arrival_date')
            ->whereNotNull('departure_date')
            ->orderBy('id')
            ->chunkById(100, function ($bookings): void {
                foreach ($bookings as $booking) {
                    $arrival = Carbon::parse($booking->arrival_date)->startOfDay();
                    $departure = Carbon::parse($booking->departure_date)->startOfDay();

                    if ($departure->lessThanOrEqualTo($arrival)) {
                        continue;
                    }

                    $cursor = $arrival->copy();
                    while ($cursor->lessThan($departure)) {
                        DB::table('booking_room_days')->insertOrIgnore([
                            'room_id' => (int) $booking->room_id,
                            'booking_id' => (int) $booking->id,
                            'booking_block_id' => null,
                            'date' => $cursor->toDateString(),
                            'source' => (string) ($booking->source ?: 'booking'),
                            'created_at' => now(),
                            'updated_at' => now(),
                        ]);

                        $cursor->addDay();
                    }
                }
            });
    }

    private function backfillExistingBookingBlocks(): void
    {
        DB::table('booking_blocks')
            ->whereNotNull('room_id')
            ->whereNotNull('block_date')
            ->orderBy('id')
            ->chunkById(100, function ($blocks): void {
                foreach ($blocks as $block) {
                    DB::table('booking_room_days')->insertOrIgnore([
                        'room_id' => (int) $block->room_id,
                        'booking_id' => null,
                        'booking_block_id' => (int) $block->id,
                        'date' => Carbon::parse($block->block_date)->toDateString(),
                        'source' => (string) ($block->source ?: 'booking_block'),
                        'created_at' => now(),
                        'updated_at' => now(),
                    ]);
                }
            });
    }
};
