<?php

declare(strict_types=1);

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

/**
 * Тесты POST /api/deals/transition — bulk status-update для DealsView bulk-actions.
 *
 * Покрывает: validation (422 на missing/неизвестный slug), RLS+app-фильтр
 * (чужие сделки НЕ обновляются), ActivityLog event=deal.status_changed,
 * 401 без auth, NO-OP не пишет audit entry, partial update (несколько id
 * принадлежат tenant'у, один — нет → updated < requested).
 */
uses(DatabaseTransactions::class);

beforeEach(function () {
    $this->tenant = Tenant::factory()->create();
    $this->otherTenant = Tenant::factory()->create();

    $this->user = User::factory()->for($this->tenant)->create();
    $this->actingAs($this->user);

    DB::statement('SET app.current_tenant_id = '.$this->tenant->id);
    $this->project = Project::factory()->for($this->tenant)->create();
});

test('POST /api/deals/transition — 422 без обязательных полей', function () {
    $this->postJson('/api/deals/transition', [])->assertStatus(422);
});

test('POST /api/deals/transition — 401 без auth', function () {
    auth()->logout();
    $this->postJson('/api/deals/transition', ['ids' => [1], 'status' => 'new'])->assertStatus(401);
});

test('POST /api/deals/transition — 422 на неизвестный status slug', function () {
    $deal = Deal::factory()->for($this->tenant)->for($this->project)->create(['status' => 'new']);

    $r = $this->postJson('/api/deals/transition', [
        'ids' => [$deal->id],
        'status' => 'not_a_real_slug',
    ]);

    $r->assertStatus(422);
    expect($r->json('errors.status.0'))->toContain('lead_statuses');

    // Сделка осталась в исходном статусе.
    $deal->refresh();
    expect($deal->status)->toBe('new');
});

test('POST /api/deals/transition — обновляет статус и пишет ActivityLog', function () {
    $deals = Deal::factory()->count(3)->for($this->tenant)->for($this->project)->create(['status' => 'new']);

    $r = $this->postJson('/api/deals/transition', [
        'ids' => $deals->pluck('id')->all(),
        'status' => 'won',
    ]);

    $r->assertStatus(200)->assertJson([
        'updated' => 3,
        'requested' => 3,
        'status' => 'won',
    ]);

    foreach ($deals as $d) {
        $d->refresh();
        expect($d->status)->toBe('won');
    }

    $activity = ActivityLog::where('tenant_id', $this->tenant->id)
        ->where('event', ActivityLog::EVENT_DEAL_STATUS_CHANGED)
        ->get();
    expect($activity)->toHaveCount(3);
    expect($activity->first()->context)->toMatchArray([
        'from' => 'new',
        'to' => 'won',
        'source' => 'bulk',
    ]);
});

test('POST /api/deals/transition — NO-OP не пишет ActivityLog', function () {
    $deal = Deal::factory()->for($this->tenant)->for($this->project)->create(['status' => 'won']);

    $r = $this->postJson('/api/deals/transition', [
        'ids' => [$deal->id],
        'status' => 'won',
    ]);

    $r->assertStatus(200)->assertJson(['updated' => 0, 'requested' => 1]);

    expect(ActivityLog::where('deal_id', $deal->id)
        ->where('event', ActivityLog::EVENT_DEAL_STATUS_CHANGED)
        ->count())->toBe(0);
});

test('POST /api/deals/transition — defense-in-depth не апдейтит чужие сделки', function () {
    $own = Deal::factory()->for($this->tenant)->for($this->project)->create(['status' => 'new']);

    DB::statement('SET app.current_tenant_id = '.$this->otherTenant->id);
    $foreignProject = Project::factory()->for($this->otherTenant)->create();
    $foreign = Deal::factory()->for($this->otherTenant)->for($foreignProject)->create(['status' => 'new']);

    // Передаём оба id — чужой не должен обновиться.
    $r = $this->postJson('/api/deals/transition', [
        'ids' => [$own->id, $foreign->id],
        'status' => 'won',
    ]);

    $r->assertStatus(200)->assertJson([
        'updated' => 1,
        'requested' => 2,
    ]);

    DB::statement('SET app.current_tenant_id = '.$this->tenant->id);
    $own->refresh();
    expect($own->status)->toBe('won');

    DB::statement('SET app.current_tenant_id = '.$this->otherTenant->id);
    $foreign->refresh();
    expect($foreign->status)->toBe('new');
});

test('POST /api/deals/transition — 422 если ids пустой массив', function () {
    $this->postJson('/api/deals/transition', [
        'ids' => [],
        'status' => 'won',
    ])->assertStatus(422);
});
