<?php

declare(strict_types=1);

/**
 * Тесты сервиса анонимизации ПДн (152-ФЗ право на удаление, ст.21) для сделок:
 * хирургическая чистка телефона субъекта в скалярной колонке и JSONB phones,
 * сохранение телефонов со-контактов, no-op при удалении только по email.
 * Решения: (1) хирургично — матч по скаляру И JSONB, чистим только элемент
 * субъекта; (2) email-путь для deals — подтверждённый no-op (нет email-поля).
 * См. спеку F-P1b: docs/superpowers/specs/2026-06-16-pd-erasure-deals-jsonb-surgical.md
 */

use App\Services\Pd\PdErasureService;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Tests\Concerns\SharesSupplierPdo;

// PdErasureService пишет pd_processing_log + sees данные через pgsql_supplier.
// Без SharesSupplierPdo эти записи коммитятся (DatabaseTransactions откатывает
// только default pgsql) и отравляют глобальный audit:verify-chains в других тестах.
uses(DatabaseTransactions::class, SharesSupplierPdo::class);

// ---------------------------------------------------------------------------
// Helpers — данные вставляются через pgsql_supplier (committed), т.к. сервис
// читает/пишет тем же BYPASSRLS-соединением. Телефоны уникальны на прогон.
// ---------------------------------------------------------------------------

/** Создать stub saas_admin_users через pgsql_supplier и вернуть id. */
function pdSvcStubAdmin(): int
{
    return (int) DB::connection('pgsql_supplier')->table('saas_admin_users')->insertGetId([
        'email' => 'pd-svc-stub-'.uniqid().'@system.local',
        'full_name' => 'PdErasureService Test Stub',
        'password_hash' => '$2y$04$system-stub-not-loginable',
        'role' => 'super_admin',
        'is_active' => false,
        'sso_provider' => 'local',
        'is_break_glass' => false,
    ]);
}

/** Создать тенант через pgsql_supplier и вернуть id. */
function pdSvcCreateTenant(): int
{
    return (int) DB::connection('pgsql_supplier')->table('tenants')->insertGetId([
        'subdomain' => 'pd-svc-'.uniqid(),
        'organization_name' => 'PdErasureService Test Org',
        'contact_email' => 'pd-svc@test.local',
        'status' => 'active',
        'balance_rub' => '0.00',
        'balance_leads' => 0,
        'is_trial' => false,
        'chargeback_unrecovered_rub' => '0.00',
        'created_at' => now(),
    ]);
}

// ---------------------------------------------------------------------------
// Tests — описания содержат "PdErasureService" для точечного --filter.
// ---------------------------------------------------------------------------

it('PdErasureService surgically removes subject phone from a co-contact deal JSONB without touching scalar owner data (F-P1b)', function (): void {
    $adminId = pdSvcStubAdmin();
    $tenantId = pdSvcCreateTenant();

    $victimPhone = '+790'.random_int(1000000, 9999999);
    $ownerPhone = '+791'.random_int(1000000, 9999999);
    $coContactPhone = '+792'.random_int(1000000, 9999999);

    // Сделка принадлежит другому человеку (ownerPhone); телефон субъекта —
    // лишь дополнительный в JSONB phones рядом с телефоном со-контакта.
    $dealId = (int) DB::connection('pgsql_supplier')->table('deals')->insertGetId([
        'tenant_id' => $tenantId,
        'project_id' => 1,
        'phone' => $ownerPhone,
        'phones' => json_encode([$victimPhone, $coContactPhone]),
        'contact_name' => 'Владелец',
        'received_at' => now(),
        'created_at' => now(),
    ]);

    $counts = app(PdErasureService::class)
        ->eraseSubject(null, $victimPhone, $tenantId, $adminId, null);

    expect($counts['deals'])->toBeGreaterThanOrEqual(1);

    $deal = DB::connection('pgsql_supplier')->table('deals')->where('id', $dealId)->first();

    // Скалярные ПДн владельца сделки НЕ трогаются.
    expect($deal->phone)->toBe($ownerPhone);
    expect($deal->contact_name)->toBe('Владелец');

    // Телефон субъекта вычищен из массива, со-контакт сохранён (хирургично).
    $phones = json_decode($deal->phones ?? '[]', true);
    expect($phones)->not->toContain($victimPhone);
    expect($phones)->toContain($coContactPhone);
});

it('PdErasureService preserves co-contact phones when erasing the deal owner (F-P1b)', function (): void {
    $adminId = pdSvcStubAdmin();
    $tenantId = pdSvcCreateTenant();

    $victimPhone = '+790'.random_int(1000000, 9999999);
    $coContactPhone = '+792'.random_int(1000000, 9999999);

    // Субъект — владелец сделки (скалярный phone), плюс его номер и со-контакт
    // в JSONB phones.
    $dealId = (int) DB::connection('pgsql_supplier')->table('deals')->insertGetId([
        'tenant_id' => $tenantId,
        'project_id' => 1,
        'phone' => $victimPhone,
        'phones' => json_encode([$victimPhone, $coContactPhone]),
        'contact_name' => 'Жертва',
        'received_at' => now(),
        'created_at' => now(),
    ]);

    $counts = app(PdErasureService::class)
        ->eraseSubject(null, $victimPhone, $tenantId, $adminId, null);

    expect($counts['deals'])->toBeGreaterThanOrEqual(1);

    $deal = DB::connection('pgsql_supplier')->table('deals')->where('id', $dealId)->first();

    // Скалярные ПДн владельца-субъекта анонимизированы.
    expect($deal->phone)->toBe('+7000XXXXXXX');
    expect($deal->contact_name)->toBe('Удалено');

    // Телефон субъекта убран из массива, со-контакт сохранён.
    $phones = json_decode($deal->phones ?? '[]', true);
    expect($phones)->not->toContain($victimPhone);
    expect($phones)->toContain($coContactPhone);
});

it('PdErasureService does not touch deals when only email is given — deals have no email (F-P1b no-op)', function (): void {
    $adminId = pdSvcStubAdmin();
    $tenantId = pdSvcCreateTenant();

    $dealPhone = '+790'.random_int(1000000, 9999999);
    $dealId = (int) DB::connection('pgsql_supplier')->table('deals')->insertGetId([
        'tenant_id' => $tenantId,
        'project_id' => 1,
        'phone' => $dealPhone,
        'phones' => json_encode([$dealPhone]),
        'contact_name' => 'Нетронутый',
        'received_at' => now(),
        'created_at' => now(),
    ]);

    // Удаление только по email — в deals нет email, сделки не сопоставляются.
    $counts = app(PdErasureService::class)
        ->eraseSubject('email-only-'.uniqid().'@example.com', null, $tenantId, $adminId, null);

    expect($counts['deals'])->toBe(0);

    $deal = DB::connection('pgsql_supplier')->table('deals')->where('id', $dealId)->first();
    expect($deal->phone)->toBe($dealPhone);
    expect($deal->contact_name)->toBe('Нетронутый');
});
