<?php

declare(strict_types=1);

use App\Models\Deal;
use App\Models\Project;
use App\Models\Tenant;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

/**
 * Plan 1/5 Task 4: lead_charges ledger — tenant-scoped RLS, FK to partitioned deals.
 *
 * Покрывает:
 * - Структура таблицы (8 колонок).
 * - Composite FK на (deals.id, deals.received_at) DEFERRABLE INITIALLY DEFERRED
 *   (deals партиционирована — обычный FK не работает; deferred нужен для
 *   atomic INSERT deal+charge в одной транзакции).
 * - RLS-изоляция: charge tenant'а A не виден из tenant'а B.
 *
 * RLS-проверка использует testing_rls_user (NOLOGIN, NO BYPASSRLS) — postgres
 * superuser обходит RLS даже при FORCE. Паттерн заимствован из RlsSmokeTest.
 *
 * Spec: docs/superpowers/specs/2026-05-10-supplier-integration-design.md §7.4.
 */
uses(DatabaseTransactions::class);

beforeEach(function () {
    // Идемпотентное создание testing-роли (роль — глобальный объект, переживает ROLLBACK).
    DB::unprepared(<<<'SQL'
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'testing_rls_user') THEN
        CREATE ROLE testing_rls_user NOLOGIN;
        GRANT USAGE ON SCHEMA public TO testing_rls_user;
        GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO testing_rls_user;
        GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO testing_rls_user;
    END IF;
END
$$;
SQL);

    // Идемпотентный grant на новые таблицы/последовательности (lead_charges создан
    // после возможного предшествующего создания роли — ALL TABLES в beforeEach
    // RlsSmokeTest применяется только при первом CREATE ROLE).
    DB::statement('GRANT SELECT, INSERT ON TABLE lead_charges TO testing_rls_user');
    DB::statement('GRANT USAGE, SELECT ON SEQUENCE lead_charges_id_seq TO testing_rls_user');
});

test('lead_charges table exists with required columns', function () {
    expect(Schema::hasTable('lead_charges'))->toBeTrue();

    foreach ([
        'id',
        'tenant_id',
        'deal_id',
        'deal_received_at',
        'tier_no',
        'price_per_lead_kopecks',
        'charged_at',
        'created_at',
    ] as $col) {
        expect(Schema::hasColumn('lead_charges', $col))->toBeTrue("column {$col} missing");
    }
});

test('lead_charges has FK to deals (composite id, received_at)', function () {
    $fk = DB::selectOne(
        "SELECT pg_get_constraintdef(c.oid) AS def
         FROM pg_constraint c
         JOIN pg_class t ON c.conrelid = t.oid
         WHERE t.relname = 'lead_charges' AND c.contype = 'f'
           AND pg_get_constraintdef(c.oid) LIKE '%deals%'
         LIMIT 1"
    );

    expect($fk)->not->toBeNull();
    expect($fk->def)
        ->toContain('deals')
        ->toContain('received_at')
        ->toContain('id');
});

test('lead_charges enforces RLS on tenant_id', function () {
    // Готовим tenant + project + deal под superuser (BYPASSRLS), чтобы FK прошёл.
    $a = Tenant::factory()->create();
    $b = Tenant::factory()->create();
    $projectA = Project::factory()->for($a)->create();
    $dealA = Deal::factory()->for($a)->for($projectA)->create();

    // Переключаемся на NO-BYPASSRLS роль для проверки изоляции.
    DB::statement('SET LOCAL ROLE testing_rls_user');
    DB::statement("SET LOCAL app.current_tenant_id = '{$a->id}'");

    DB::table('lead_charges')->insert([
        'tenant_id' => $a->id,
        'deal_id' => $dealA->id,
        'deal_received_at' => $dealA->received_at,
        'tier_no' => 1,
        'price_per_lead_kopecks' => 6000,
        'charged_at' => now(),
        'created_at' => now(),
    ]);

    // Под tenant A — видно
    $countA = DB::table('lead_charges')->count();

    // Переключаемся на tenant B — RLS должен скрыть
    DB::statement("SET LOCAL app.current_tenant_id = '{$b->id}'");
    $countB = DB::table('lead_charges')->count();

    expect($countA)->toBe(1);
    expect($countB)->toBe(0, 'tenant B must not see tenant A charges (RLS leak)');
});
