<?php

declare(strict_types=1);

use App\Jobs\ImportLeadsJob;
use App\Models\ImportLog;
use App\Models\ImportUnknownStatus;
use App\Models\Tenant;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Facades\Storage;

uses(DatabaseTransactions::class);

beforeEach(function (): void {
    $this->tenant = Tenant::factory()->create();
    $this->user = User::factory()->for($this->tenant)->create();
    // Устанавливаем контекст тенанта на уровне outer-транзакции DatabaseTransactions.
    // Middleware SetTenantContext использует SET LOCAL внутри savepoint'а — без этой
    // строки RLS-фильтрация активна только внутри HTTP-запроса, но прямые DB-запросы
    // в тестах (count, factory) видят все тенанты. Паттерн из DealIndexTest.php.
    DB::statement('SET app.current_tenant_id = '.$this->tenant->id);
    $this->actingAs($this->user);
});

test('POST /api/imports принимает CSV, создаёт import_log, диспатчит job', function (): void {
    Queue::fake();
    Storage::fake('local');

    $csv = "\xEF\xBB\xBF".'id,Проект,Тег проекта,Телефон,Создано,Напоминание,Комментарий,Состояние,Имя'."\n".
        '9001,Окна,окна,79161112233,2023/05/10 10:00:00,,,Новые,';

    $response = $this->postJson('/api/imports', [
        'file' => UploadedFile::fake()->createWithContent('leads.csv', $csv),
    ]);

    $response->assertStatus(201)
        ->assertJsonPath('data.status', 'pending')
        ->assertJsonPath('data.filename', 'leads.csv');

    Queue::assertPushed(ImportLeadsJob::class);
    // Defense-in-depth: superuser на dev обходит RLS (BYPASSRLS), поэтому явно
    // фильтруем по tenant_id — паттерн из DealIndexTest / DealController.
    expect(ImportLog::query()->where('tenant_id', $this->tenant->id)->count())->toBe(1);
});

test('POST /api/imports отвергает не-CSV файл', function (): void {
    Storage::fake('local');

    $response = $this->postJson('/api/imports', [
        'file' => UploadedFile::fake()->create('image.png', 10, 'image/png'),
    ]);

    $response->assertStatus(422)->assertJsonValidationErrorFor('file');
});

test('POST /api/imports требует авторизации', function (): void {
    app('auth')->forgetGuards();

    $this->postJson('/api/imports', [])->assertStatus(401);
});

test('GET /api/imports возвращает только import_log своего тенанта', function (): void {
    DB::statement('SET app.current_tenant_id = '.$this->tenant->id);
    ImportLog::factory()->count(2)->create([
        'tenant_id' => $this->tenant->id,
        'user_id' => $this->user->id,
    ]);

    $this->getJson('/api/imports')
        ->assertStatus(200)
        ->assertJsonCount(2, 'data');
});

test('GET /api/imports/{id} отдаёт прогресс', function (): void {
    DB::statement('SET app.current_tenant_id = '.$this->tenant->id);
    $log = ImportLog::factory()->create([
        'tenant_id' => $this->tenant->id,
        'user_id' => $this->user->id,
        'status' => 'processing',
        'rows_added' => 10,
    ]);

    $this->getJson("/api/imports/{$log->id}")
        ->assertStatus(200)
        ->assertJsonPath('data.status', 'processing')
        ->assertJsonPath('data.rows_added', 10);
});

test('GET /api/imports/unknown-statuses возвращает незамапленные статусы', function (): void {
    DB::statement('SET app.current_tenant_id = '.$this->tenant->id);
    ImportUnknownStatus::create([
        'tenant_id' => $this->tenant->id, 'status_ru' => 'Архив', 'occurrences' => 3,
    ]);
    ImportUnknownStatus::create([
        'tenant_id' => $this->tenant->id, 'status_ru' => 'Спам', 'occurrences' => 1,
        'mapped_to_slug' => 'lost', 'resolved_at' => now(),
    ]);

    $this->getJson('/api/imports/unknown-statuses')
        ->assertStatus(200)
        ->assertJsonCount(1, 'data')
        ->assertJsonPath('data.0.status_ru', 'Архив');
});

test('POST /api/imports/unknown-statuses/resolve проставляет маппинг', function (): void {
    DB::statement('SET app.current_tenant_id = '.$this->tenant->id);
    $unknown = ImportUnknownStatus::create([
        'tenant_id' => $this->tenant->id, 'status_ru' => 'Архив', 'occurrences' => 3,
    ]);

    $this->postJson('/api/imports/unknown-statuses/resolve', [
        'mappings' => [['status_ru' => 'Архив', 'slug' => 'lost']],
    ])->assertStatus(200);

    expect($unknown->refresh()->mapped_to_slug)->toBe('lost')
        ->and($unknown->resolved_at)->not->toBeNull();
});

test('resolve отвергает несуществующий slug', function (): void {
    DB::statement('SET app.current_tenant_id = '.$this->tenant->id);

    $this->postJson('/api/imports/unknown-statuses/resolve', [
        'mappings' => [['status_ru' => 'Архив', 'slug' => 'нет-такого']],
    ])->assertStatus(422);
});

test('GET /api/imports/{id} отвергает import_log чужого тенанта (403)', function (): void {
    $otherTenant = Tenant::factory()->create();
    $otherUser = User::factory()->for($otherTenant)->create();
    $foreignLog = ImportLog::factory()->create([
        'tenant_id' => $otherTenant->id,
        'user_id' => $otherUser->id,
    ]);

    // Авторизован пользователь $this->tenant; запрашиваем чужой import_log.
    // abort_if(tenant_id mismatch, 403) в ImportController::show — defense-in-depth.
    $this->getJson("/api/imports/{$foreignLog->id}")->assertStatus(403);
});
