<?php

declare(strict_types=1);

use App\Services\MonthlyPartitionManager;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Tests\Concerns\SharesSupplierPdo;

uses(DatabaseTransactions::class, SharesSupplierPdo::class);
// ensureMonth теперь делает CREATE через pgsql_supplier (см. MonthlyPartitionManager::DDL_CONNECTION).
// Без SharesSupplierPdo DDL уйдёт мимо test-транзакции и партиции протечь в test DB.

function partitionExists(string $name): bool
{
    return DB::selectOne(
        "SELECT 1 AS ok FROM pg_class WHERE relname = ? AND relkind = 'r'",
        [$name],
    ) !== null;
}

// ---------------------------------------------------------------------------
// Existing tests (deals — business table, received_at key)
// ---------------------------------------------------------------------------

test('ensureRange создаёт месячные партиции deals под диапазон', function (): void {
    $manager = app(MonthlyPartitionManager::class);

    $created = $manager->ensureRange(
        'deals',
        Carbon::parse('2024-02-15'),
        Carbon::parse('2024-04-03'),
    );

    expect($created)->toBeGreaterThanOrEqual(3)
        ->and(partitionExists('deals_y2024_m02'))->toBeTrue()
        ->and(partitionExists('deals_y2024_m03'))->toBeTrue()
        ->and(partitionExists('deals_y2024_m04'))->toBeTrue();
});

test('ensureRange идемпотентна — повторный вызов не падает', function (): void {
    $manager = app(MonthlyPartitionManager::class);

    $manager->ensureRange('deals', Carbon::parse('2024-02-15'), Carbon::parse('2024-02-20'));
    $secondRun = $manager->ensureRange('deals', Carbon::parse('2024-02-15'), Carbon::parse('2024-02-20'));

    expect($secondRun)->toBe(0); // всё уже существует
});

test('ensureRange отвергает неизвестную таблицу', function (): void {
    app(MonthlyPartitionManager::class)->ensureRange('orders', now(), now());
})->throws(InvalidArgumentException::class);

// ---------------------------------------------------------------------------
// Hole #2 tests: audit tables (created_at key)
// ---------------------------------------------------------------------------

test('ensureMonth создаёт партицию auth_log (created_at)', function (): void {
    $manager = app(MonthlyPartitionManager::class);
    $month = Carbon::parse('2024-03-01');

    $manager->ensureMonth('auth_log', $month);

    expect(partitionExists('auth_log_y2024_m03'))->toBeTrue();
});

test('ensureMonth создаёт партицию activity_log (created_at)', function (): void {
    $manager = app(MonthlyPartitionManager::class);
    $manager->ensureMonth('activity_log', Carbon::parse('2024-03-01'));

    expect(partitionExists('activity_log_y2024_m03'))->toBeTrue();
});

test('ensureMonth создаёт партицию tenant_operations_log (created_at)', function (): void {
    $manager = app(MonthlyPartitionManager::class);
    $manager->ensureMonth('tenant_operations_log', Carbon::parse('2024-03-01'));

    expect(partitionExists('tenant_operations_log_y2024_m03'))->toBeTrue();
});

test('ensureMonth создаёт партицию balance_transactions (created_at)', function (): void {
    $manager = app(MonthlyPartitionManager::class);
    $manager->ensureMonth('balance_transactions', Carbon::parse('2024-03-01'));

    expect(partitionExists('balance_transactions_y2024_m03'))->toBeTrue();
});

test('ensureMonth создаёт партицию pd_processing_log (created_at)', function (): void {
    $manager = app(MonthlyPartitionManager::class);
    $manager->ensureMonth('pd_processing_log', Carbon::parse('2024-03-01'));

    expect(partitionExists('pd_processing_log_y2024_m03'))->toBeTrue();
});

test('ensureMonth создаёт партицию saas_admin_audit_log (created_at)', function (): void {
    $manager = app(MonthlyPartitionManager::class);
    $manager->ensureMonth('saas_admin_audit_log', Carbon::parse('2024-03-01'));

    expect(partitionExists('saas_admin_audit_log_y2024_m03'))->toBeTrue();
});

test('partitionName возвращает правильный формат', function (): void {
    $manager = app(MonthlyPartitionManager::class);

    expect($manager->partitionName('auth_log', Carbon::parse('2026-05-15')))
        ->toBe('auth_log_y2026_m05');

    expect($manager->partitionName('deals', Carbon::parse('2024-01-01')))
        ->toBe('deals_y2024_m01');
});

test('listPartitions возвращает созданные партиции', function (): void {
    $manager = app(MonthlyPartitionManager::class);
    $manager->ensureMonth('auth_log', Carbon::parse('2024-04-01'));
    $manager->ensureMonth('auth_log', Carbon::parse('2024-05-01'));

    $partitions = $manager->listPartitions('auth_log');

    expect($partitions)->toContain('auth_log_y2024_m04')
        ->toContain('auth_log_y2024_m05');
});

// ---------------------------------------------------------------------------
// Slepok routing: project_routing_snapshots (snapshot_date key, Этап 2, 27.05.2026)
// ---------------------------------------------------------------------------

test('PARTITIONED_TABLES включает project_routing_snapshots с ключом snapshot_date', function (): void {
    expect(MonthlyPartitionManager::PARTITIONED_TABLES)
        ->toHaveKey('project_routing_snapshots')
        ->and(MonthlyPartitionManager::PARTITIONED_TABLES['project_routing_snapshots'])
        ->toBe('snapshot_date');
});

test('ensureMonth создаёт партицию project_routing_snapshots (snapshot_date)', function (): void {
    $manager = app(MonthlyPartitionManager::class);
    $manager->ensureMonth('project_routing_snapshots', Carbon::parse('2024-07-01'));

    expect(partitionExists('project_routing_snapshots_y2024_m07'))->toBeTrue();
});

// ---------------------------------------------------------------------------
// migrate:fresh resilience: ensureMonth must SKIP (not throw) a partitioned
// table whose parent does not exist yet. During migrate:fresh the initial
// schema-load migration runs partitions:create-months before later delta
// migrations create their own partitioned tables (project_routing_snapshots,
// lead_region_resolution_log). Those migrations create their own partitions;
// the manager must skip the not-yet-existing parent rather than crash the run.
// ---------------------------------------------------------------------------
test('ensureMonth пропускает таблицу без существующего родителя (migrate:fresh resilience)', function (): void {
    $manager = app(MonthlyPartitionManager::class);

    // Drop a known partitioned parent within the test transaction (rolled back at end),
    // reproducing the "parent not created yet" ordering case.
    DB::connection(MonthlyPartitionManager::DDL_CONNECTION)
        ->statement('DROP TABLE IF EXISTS lead_region_resolution_log CASCADE');

    $result = $manager->ensureMonth('lead_region_resolution_log', Carbon::parse('2024-03-01'));

    // Guard returns false (skipped) instead of throwing "relation does not exist".
    expect($result)->toBeFalse();
});
