<?php

declare(strict_types=1);

use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;

/**
 * Initial schema migration — загружает db/schema.sql целиком.
 *
 * Источник истины: <repo>/db/schema.sql v8.5 (54 таблицы + 12 партиций +
 * 91 индекс + 35 RLS-политик + 12 триггеров + 4 функции).
 *
 * Стратегия: raw SQL через DB::unprepared(), без переписывания на Laravel
 * Schema Builder — db/schema.sql остаётся source of truth (правило
 * Pravila_raboty_Claude §4.2 — правки только через CHANGELOG_schema.md).
 */
return new class extends Migration
{
    /**
     * Run OUTSIDE Laravel's migration transaction. This migration loads the full
     * schema via DB::unprepared(), then calls partitions:create-months which opens
     * a SECOND connection (pgsql_supplier) for partition DDL. That connection cannot
     * see uncommitted DDL from this migration's transaction (PostgreSQL READ
     * COMMITTED), so the schema must be committed first. This is a full schema
     * (re)build — no partial rollback is meaningful.
     */
    public $withinTransaction = false;

    public function up(): void
    {
        $schemaPath = dirname(base_path()).DIRECTORY_SEPARATOR.'db'.DIRECTORY_SEPARATOR.'schema.sql';

        if (! is_readable($schemaPath)) {
            throw new RuntimeException("schema.sql не найден или недоступен для чтения: {$schemaPath}");
        }

        $sql = file_get_contents($schemaPath);

        if ($sql === false || $sql === '') {
            throw new RuntimeException("schema.sql пуст или не читается: {$schemaPath}");
        }

        DB::unprepared($sql);

        // Доп. явные ALTER: при загрузке schema.sql через DB::unprepared() PDO
        // иногда тихо проглатывает ошибки на FK к partitioned-таблицам.
        // Если FK уже есть — ловим duplicate-error и игнорируем.

        // FK `webhook_dedup_keys → deals(id, received_at)` (CTO-17, v8.6+).
        // Воспроизводится на свежей testing DB. Тест ON DELETE CASCADE без него падает.
        try {
            DB::statement(<<<'SQL'
                ALTER TABLE webhook_dedup_keys
                ADD FOREIGN KEY (deal_id, deal_received_at)
                REFERENCES deals (id, received_at)
                ON DELETE CASCADE
                DEFERRABLE INITIALLY DEFERRED
            SQL);
        } catch (Throwable $e) {
            if (! str_contains($e->getMessage(), 'already exists')
                && ! str_contains($e->getMessage(), 'duplicate')) {
                throw $e;
            }
        }

        // FK `lead_charges → deals(id, received_at)` (Plan 1/5 Task 4, v8.15+).
        // Composite + DEFERRABLE добавлен в самом конце schema.sql (после section 5
        // с deals), поэтому DB::unprepared его успешно применил — повторный ALTER
        // здесь не нужен. Если в будущем PDO начнёт глотать FK на partitioned —
        // повторить паттерн webhook_dedup_keys с try/catch ('уже существует' RU + EN).

        // v8.31 (hole #2): создаём начальные партиции для 9 партиционированных таблиц
        // (deals, supplier_lead_costs + 7 audit-таблиц). Без этого первый INSERT
        // упадёт с "no partition found for row". Cron partitions:create-months
        // поддерживает их далее (текущий + ahead, default 2 месяца вперёд).
        Artisan::call('partitions:create-months', ['--ahead' => 2]);
    }

    public function down(): void
    {
        throw new RuntimeException(
            'Initial schema migration необратима. Используй `php artisan migrate:fresh` '
            .'для drop+recreate всей public-схемы из db/schema.sql.'
        );
    }
};
