<?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;

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();
    $this->manager = User::factory()->for($this->tenant)->create(['is_active' => true]);
});

test('PATCH /api/deals/{id} 401 без auth', function () {
    $deal = Deal::factory()->for($this->tenant)->for($this->project)->create();
    auth()->logout();
    $this->patchJson('/api/deals/'.$deal->id, [])->assertStatus(401);
});

test('PATCH /api/deals/{id} 404 чужая сделка', function () {
    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();

    $this->patchJson('/api/deals/'.$foreign->id, [
        'comment' => 'leak',
    ])->assertStatus(404);
});

test('PATCH /api/deals/{id} обновляет comment + пишет deal.commented в ActivityLog', function () {
    $deal = Deal::factory()->for($this->tenant)->for($this->project)->create(['comment' => 'old']);

    $r = $this->patchJson('/api/deals/'.$deal->id, [
        'comment' => 'Дозвонился, перезвоню после 14:00',
    ]);
    $r->assertStatus(200);
    expect($r->json('deal.comment'))->toBe('Дозвонился, перезвоню после 14:00');

    DB::statement('SET app.current_tenant_id = '.$this->tenant->id);
    $deal->refresh();
    expect($deal->comment)->toBe('Дозвонился, перезвоню после 14:00');

    $log = ActivityLog::where('deal_id', $deal->id)->where('event', 'deal.commented')->first();
    expect($log)->not->toBeNull();
    expect($log->context['text'])->toBe('Дозвонился, перезвоню после 14:00');
});

test('PATCH /api/deals/{id} обновляет manager_id + пишет deal.assigned + ставит assigned_at', function () {
    $deal = Deal::factory()->for($this->tenant)->for($this->project)->create([
        'manager_id' => null,
        'assigned_at' => null,
    ]);

    $r = $this->patchJson('/api/deals/'.$deal->id, [
        'manager_id' => $this->manager->id,
    ]);
    $r->assertStatus(200);

    DB::statement('SET app.current_tenant_id = '.$this->tenant->id);
    $deal->refresh();
    expect($deal->manager_id)->toBe($this->manager->id);
    expect($deal->assigned_at)->not->toBeNull();

    $log = ActivityLog::where('deal_id', $deal->id)->where('event', 'deal.assigned')->first();
    expect($log)->not->toBeNull();
    expect($log->context['to'])->toBe($this->manager->id);
});

test('PATCH /api/deals/{id} обновляет status + пишет deal.status_changed source=manual', function () {
    $deal = Deal::factory()->for($this->tenant)->for($this->project)->create(['status' => 'new']);

    $r = $this->patchJson('/api/deals/'.$deal->id, [
        'status' => 'won',
    ]);
    $r->assertStatus(200);

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

    $log = ActivityLog::where('deal_id', $deal->id)->where('event', 'deal.status_changed')->first();
    expect($log)->not->toBeNull();
    expect($log->context)->toMatchArray(['from' => 'new', 'to' => 'won', 'source' => 'manual']);
});

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

    $r = $this->patchJson('/api/deals/'.$deal->id, [
        'status' => 'not_a_real_slug',
    ]);
    $r->assertStatus(422);

    DB::statement('SET app.current_tenant_id = '.$this->tenant->id);
    $deal->refresh();
    expect($deal->status)->toBe('new'); // не изменился
});

test('PATCH /api/deals/{id} 422 на manager_id чужого tenant\'а', function () {
    DB::statement('SET app.current_tenant_id = '.$this->otherTenant->id);
    $foreignManager = User::factory()->for($this->otherTenant)->create(['is_active' => true]);

    $deal = Deal::factory()->for($this->tenant)->for($this->project)->create();

    $r = $this->patchJson('/api/deals/'.$deal->id, [
        'manager_id' => $foreignManager->id,
    ]);
    $r->assertStatus(422);
});

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

    $r = $this->patchJson('/api/deals/'.$deal->id, [
        'status' => 'won', // не меняем
        'comment' => 'same', // не меняем
    ]);
    $r->assertStatus(200);

    DB::statement('SET app.current_tenant_id = '.$this->tenant->id);
    expect(ActivityLog::where('deal_id', $deal->id)->count())->toBe(0);
});

test('PATCH /api/deals/{id} комбинированно — comment + status одним запросом → 2 ActivityLog', function () {
    $deal = Deal::factory()->for($this->tenant)->for($this->project)->create([
        'status' => 'new',
        'comment' => null,
    ]);

    $r = $this->patchJson('/api/deals/'.$deal->id, [
        'comment' => 'Заметка',
        'status' => 'in_progress',
    ]);
    $r->assertStatus(200);

    DB::statement('SET app.current_tenant_id = '.$this->tenant->id);
    $events = ActivityLog::where('deal_id', $deal->id)->orderBy('id')->get();
    expect($events)->toHaveCount(2);
    $eventNames = $events->pluck('event')->all();
    expect($eventNames)->toContain('deal.commented');
    expect($eventNames)->toContain('deal.status_changed');
});
