<?php

declare(strict_types=1);

use App\Jobs\Supplier\DeleteSupplierProjectJob;
use App\Models\Project;
use App\Models\SupplierProject;
use App\Models\Tenant;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Str;
use Tests\Concerns\SharesSupplierPdo;

uses(DatabaseTransactions::class, SharesSupplierPdo::class);

// ---------------------------------------------------------------------------
// Stage 4 / Task 4.2 — R-17 migration (spec §4.4.1): one-time artisan command
// to clean up orphan supplier_projects rows created by the now-removed
// buildUniqueKey divergence.
//
// Before R-17 fix: SMS projects with keyword produced two diverging unique_keys:
//   B2 row: unique_key='sender+keyword'
//   B3 row: unique_key='sender' (no keyword) — ORPHAN after unification
//
// After fix all platforms use unique_key='sender+keyword'. Existing orphans
// (B3 rows keyed under sender alone) need migration:
//   - no sibling at 'sender+keyword' for same tenant → UPDATE row's unique_key
//   - has sibling → mark for deletion (dispatch DeleteSupplierProjectJob, which
//     also removes the donor from supplier portal + cascades pivot cleanup)
// ---------------------------------------------------------------------------

it('R-17 migrate: orphan SMS row with no sibling → UPDATE unique_key to sender+keyword', function (): void {
    $sender = '7913'.fake()->numerify('#######');
    $keyword = 'KW'.Str::random(5);

    $tenant = Tenant::factory()->create();
    $project = Project::factory()->for($tenant)->asSmsSignal([$sender], $keyword)->create([
        'is_active' => true,
        'daily_limit_target' => 5,
    ]);

    // Pre-existing orphan: B3 supplier_project keyed under sender alone (legacy buildUniqueKey).
    $orphanId88001 = DB::connection('pgsql_supplier')->table('supplier_projects')->insertGetId([
        'platform' => 'B3',
        'signal_type' => 'sms',
        'unique_key' => $sender, // orphan key (no '+keyword')
        'subject_code' => null,
        'supplier_external_id' => '88001',
        'current_limit' => 5,
        'current_workdays' => json_encode([1, 2, 3, 4, 5]),
        'current_regions' => null,
        'sync_status' => 'ok',
        'last_synced_at' => now(),
    ]);
    DB::connection('pgsql_supplier')->table('project_supplier_links')->insert([
        'project_id' => $project->id,
        'supplier_project_id' => $orphanId88001,
        'platform' => 'B3',
    ]);

    $exitCode = $this->artisan('supplier:rekey-orphans')->run();
    expect($exitCode)->toBe(0);

    // Orphan now has unified key.
    $sp = SupplierProject::on('pgsql_supplier')->where('supplier_external_id', '88001')->first();
    expect($sp)->not->toBeNull();
    expect($sp->unique_key)->toBe($sender.'+'.$keyword);
});

it('R-17 migrate: orphan SMS row WITH sibling at sender+keyword → dispatch DeleteSupplierProjectJob for orphan', function (): void {
    Queue::fake();

    $sender = '7923'.fake()->numerify('#######');
    $keyword = 'KW'.Str::random(5);

    $tenant = Tenant::factory()->create();
    $project = Project::factory()->for($tenant)->asSmsSignal([$sender], $keyword)->create([
        'is_active' => true,
        'daily_limit_target' => 5,
    ]);

    // Sibling B2 row at unified key.
    $siblingId = DB::connection('pgsql_supplier')->table('supplier_projects')->insertGetId([
        'platform' => 'B2',
        'signal_type' => 'sms',
        'unique_key' => $sender.'+'.$keyword,
        'subject_code' => null,
        'supplier_external_id' => '88002',
        'current_limit' => 5,
        'current_workdays' => json_encode([1, 2, 3, 4, 5]),
        'current_regions' => null,
        'sync_status' => 'ok',
        'last_synced_at' => now(),
    ]);
    DB::connection('pgsql_supplier')->table('project_supplier_links')->insert([
        'project_id' => $project->id,
        'supplier_project_id' => $siblingId,
        'platform' => 'B2',
    ]);

    // Orphan B3 row under sender alone.
    $orphanId = DB::connection('pgsql_supplier')->table('supplier_projects')->insertGetId([
        'platform' => 'B3',
        'signal_type' => 'sms',
        'unique_key' => $sender, // orphan
        'subject_code' => null,
        'supplier_external_id' => '88003',
        'current_limit' => 5,
        'current_workdays' => json_encode([1, 2, 3, 4, 5]),
        'current_regions' => null,
        'sync_status' => 'ok',
        'last_synced_at' => now(),
    ]);
    DB::connection('pgsql_supplier')->table('project_supplier_links')->insert([
        'project_id' => $project->id,
        'supplier_project_id' => $orphanId,
        'platform' => 'B3',
    ]);

    $exitCode = $this->artisan('supplier:rekey-orphans')->run();
    expect($exitCode)->toBe(0);

    Queue::assertPushed(DeleteSupplierProjectJob::class, function ($job) use ($orphanId) {
        return in_array($orphanId, $job->supplierProjectIds, true);
    });
});

it('R-17 migrate: --dry-run reports orphans without modifying anything', function (): void {
    Queue::fake();

    $sender = '7933'.fake()->numerify('#######');
    $keyword = 'KW'.Str::random(5);

    $tenant = Tenant::factory()->create();
    $project = Project::factory()->for($tenant)->asSmsSignal([$sender], $keyword)->create([
        'is_active' => true,
        'daily_limit_target' => 5,
    ]);

    $dryOrphanId = DB::connection('pgsql_supplier')->table('supplier_projects')->insertGetId([
        'platform' => 'B3',
        'signal_type' => 'sms',
        'unique_key' => $sender, // orphan
        'subject_code' => null,
        'supplier_external_id' => '88004',
        'current_limit' => 5,
        'current_workdays' => json_encode([1, 2, 3, 4, 5]),
        'current_regions' => null,
        'sync_status' => 'ok',
        'last_synced_at' => now(),
    ]);
    DB::connection('pgsql_supplier')->table('project_supplier_links')->insert([
        'project_id' => $project->id,
        'supplier_project_id' => $dryOrphanId,
        'platform' => 'B3',
    ]);

    $exitCode = $this->artisan('supplier:rekey-orphans', ['--dry-run' => true])->run();
    expect($exitCode)->toBe(0);

    // Unchanged.
    $sp = SupplierProject::on('pgsql_supplier')->where('supplier_external_id', '88004')->first();
    expect($sp->unique_key)->toBe($sender);
    Queue::assertNothingPushed();
});
