<?php

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::table('carts', function (Blueprint $table) {
            $table->foreignId('provider_id')
                ->nullable()
                ->after('client_id')
                ->constrained('providers')
                ->restrictOnDelete();
        });

        DB::statement(<<<'SQL'
            UPDATE carts
            SET provider_id = homogeneous.provider_id
            FROM (
                SELECT cart_items.cart_id, MIN(schedules.provider_id) AS provider_id
                FROM cart_items
                INNER JOIN schedules ON schedules.id = cart_items.schedule_id
                WHERE schedules.provider_id IS NOT NULL
                GROUP BY cart_items.cart_id
                HAVING COUNT(DISTINCT schedules.provider_id) = 1
                   AND COUNT(*) = COUNT(schedules.provider_id)
            ) AS homogeneous
            WHERE carts.id = homogeneous.cart_id
        SQL);

        Schema::table('payments', function (Blueprint $table) {
            $table->foreignId('schedule_id')->nullable()->change();
            $table->foreignId('cart_id')
                ->nullable()
                ->after('schedule_id')
                ->constrained('carts')
                ->restrictOnDelete();
        });

        DB::statement(<<<'SQL'
            ALTER TABLE payments
            ADD CONSTRAINT payments_exactly_one_payable_check
            CHECK (num_nonnulls(schedule_id, cart_id) = 1)
        SQL);

        DB::statement(<<<'SQL'
            CREATE UNIQUE INDEX payments_one_active_per_cart
            ON payments (cart_id)
            WHERE cart_id IS NOT NULL
              AND deleted_at IS NULL
              AND status IN ('pending', 'processing', 'authorized', 'paid')
        SQL);

        DB::statement(<<<'SQL'
            CREATE UNIQUE INDEX cart_items_one_cart_per_schedule
            ON cart_items (schedule_id)
        SQL);
    }

    public function down(): void
    {
        DB::statement('DROP INDEX IF EXISTS payments_one_active_per_cart');
        DB::statement('DROP INDEX IF EXISTS cart_items_one_cart_per_schedule');
        DB::statement('ALTER TABLE payments DROP CONSTRAINT IF EXISTS payments_exactly_one_payable_check');
        DB::statement(<<<'SQL'
            UPDATE payments
            SET schedule_id = first_item.schedule_id
            FROM (
                SELECT cart_id, MIN(schedule_id) AS schedule_id
                FROM cart_items
                GROUP BY cart_id
            ) AS first_item
            WHERE payments.cart_id = first_item.cart_id
              AND payments.schedule_id IS NULL
        SQL);

        Schema::table('payments', function (Blueprint $table) {
            $table->dropConstrainedForeignId('cart_id');
            $table->foreignId('schedule_id')->nullable(false)->change();
        });

        Schema::table('carts', function (Blueprint $table) {
            $table->dropConstrainedForeignId('provider_id');
        });
    }
};
