<?php

namespace Tests\Feature;

use App\Livewire\Issues\IssueChecklistManager;
use App\Livewire\Issues\IssueDetail;
use App\Livewire\Issues\IssueForm;
use App\Models\Feature;
use App\Models\Issue;
use App\Models\IssueChecklistTemplate;
use App\Models\Layer;
use App\Models\Phase;
use App\Models\Project;
use App\Models\User;
use App\Notifications\IssueCommentedNotification;
use App\Notifications\IssueStatusChangedNotification;
use App\Notifications\IssueTaskAssignedNotification;
use App\Notifications\IssueTaskOverdueNotification;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Str;
use Livewire\Livewire;
use Spatie\Permission\Models\Permission;
use Tests\TestCase;

class IssuesEnhancementsTest extends TestCase
{
    use RefreshDatabase;

    private User $user;

    private User $assignee;

    private Project $project;

    protected function setUp(): void
    {
        parent::setUp();
        foreach (['view issues', 'create issues', 'edit issues', 'delete issues', 'upload media'] as $p) {
            Permission::findOrCreate($p);
        }

        $this->user = User::factory()->create();
        $this->user->givePermissionTo(['view issues', 'create issues', 'edit issues', 'delete issues', 'upload media']);
        $this->assignee = User::factory()->create();

        $this->project = Project::create([
            'reference' => 'ENH-1',
            'name' => 'Proyecto Enh',
            'address' => 'Calle 1',
            'lat' => 40.0,
            'lng' => -3.0,
            'start_date' => now()->toDateString(),
            'end_date_estimated' => now()->addMonths(3)->toDateString(),
            'status' => 'in_progress',
            'created_by' => $this->user->id,
        ]);
        $this->project->users()->attach($this->user->id, ['role_in_project' => 'supervisor']);
        $this->project->users()->attach($this->assignee->id, ['role_in_project' => 'worker']);
    }

    private function makeIssue(array $attrs = []): Issue
    {
        return Issue::create(array_merge([
            'project_id' => $this->project->id,
            'title' => 'Incidencia enh',
            'status' => 'open',
            'priority' => 'medium',
            'reported_by' => $this->user->id,
        ], $attrs));
    }

    private function makeFeature(): Feature
    {
        $phase = Phase::create([
            'project_id' => $this->project->id, 'name' => 'F', 'order' => 1,
            'color' => '#000', 'progress_percent' => 0,
        ]);
        $layer = Layer::create([
            'project_id' => $this->project->id, 'phase_id' => $phase->id,
            'name' => 'L', 'color' => '#111', 'uploaded_by' => $this->user->id,
        ]);

        return Feature::create([
            'layer_id' => $layer->id, 'name' => 'Muro norte',
            'geometry' => ['type' => 'Point', 'coordinates' => [-3.0, 40.0]],
            'progress' => 0, 'status' => 'planned',
        ]);
    }

    // ── Notifications ────────────────────────────────────────────────────────────

    public function test_assigning_a_task_notifies_the_assignee(): void
    {
        Notification::fake();
        $issue = $this->makeIssue();

        Livewire::actingAs($this->user)
            ->test(IssueDetail::class, ['project' => $this->project, 'issue' => $issue])
            ->set('newTaskTitle', 'Sanear zona')
            ->set('newTaskAssignee', $this->assignee->id)
            ->call('addTask');

        Notification::assertSentTo($this->assignee, IssueTaskAssignedNotification::class);
    }

    public function test_commenting_notifies_the_assignee(): void
    {
        Notification::fake();
        $issue = $this->makeIssue(['assigned_to' => $this->assignee->id]);

        Livewire::actingAs($this->user)
            ->test(IssueDetail::class, ['project' => $this->project, 'issue' => $issue])
            ->set('newComment', 'Revisado en obra')
            ->call('addComment');

        Notification::assertSentTo($this->assignee, IssueCommentedNotification::class);
    }

    public function test_resolving_notifies_stakeholders(): void
    {
        Notification::fake();
        $issue = $this->makeIssue(['assigned_to' => $this->assignee->id]);

        Livewire::actingAs($this->user)
            ->test(IssueDetail::class, ['project' => $this->project, 'issue' => $issue])
            ->call('verifyResolve');

        Notification::assertSentTo($this->assignee, IssueStatusChangedNotification::class);
        $this->assertEquals('resolved', $issue->fresh()->status);
    }

    // ── Checklist templates ────────────────────────────────────────────────────────

    public function test_checklist_manager_creates_a_template(): void
    {
        Livewire::actingAs($this->user)
            ->test(IssueChecklistManager::class, ['project' => $this->project])
            ->set('name', 'Reparación grieta')
            ->set('items', ['Picar', 'Sellar', 'Pintar'])
            ->call('save');

        $this->assertDatabaseHas('issue_checklist_templates', [
            'project_id' => $this->project->id, 'name' => 'Reparación grieta',
        ]);
    }

    public function test_applying_a_template_creates_the_tasks(): void
    {
        $issue = $this->makeIssue();
        $template = IssueChecklistTemplate::create([
            'project_id' => $this->project->id,
            'name' => 'Plantilla',
            'items' => ['Picar', 'Sellar', 'Pintar'],
        ]);

        Livewire::actingAs($this->user)
            ->test(IssueDetail::class, ['project' => $this->project, 'issue' => $issue])
            ->set('applyTemplateId', $template->id)
            ->call('applyTemplate');

        $this->assertEquals(3, $issue->tasks()->count());
        $this->assertEqualsCanonicalizing(['Picar', 'Sellar', 'Pintar'], $issue->tasks()->pluck('title')->all());
    }

    // ── Overdue alerts ───────────────────────────────────────────────────────────

    public function test_overdue_command_notifies_assignee_once(): void
    {
        Notification::fake();
        $issue = $this->makeIssue();
        $task = $issue->tasks()->create([
            'title' => 'Tarea vencida',
            'assigned_to' => $this->assignee->id,
            'due_date' => now()->subDays(2)->toDateString(),
            'is_done' => false,
            'uuid' => (string) Str::uuid(),
        ]);

        $this->artisan('issues:notify-overdue')->assertSuccessful();
        $this->artisan('issues:notify-overdue')->assertSuccessful(); // idempotent

        Notification::assertSentTo($this->assignee, IssueTaskOverdueNotification::class);
        Notification::assertCount(1);
        $this->assertNotNull($task->fresh()->overdue_notified_at);
    }

    // ── Issue type / category ──────────────────────────────────────────────────────

    public function test_create_form_shows_the_type_field(): void
    {
        $this->actingAs($this->user)
            ->get(route('projects.issues.create', $this->project))
            ->assertOk()
            ->assertSee('Tipo de incidencia')
            ->assertSee('Defecto')
            ->assertSee('Seguridad');
    }

    public function test_editing_an_issue_persists_the_type(): void
    {
        $issue = $this->makeIssue(['type' => 'defect']);

        Livewire::actingAs($this->user)
            ->test(IssueForm::class, ['project' => $this->project, 'issue' => $issue])
            ->assertSet('type', 'defect')
            ->set('type', 'safety')
            ->call('save');

        $this->assertEquals('safety', $issue->fresh()->type);
    }

    // ── Report issue from a map feature ──────────────────────────────────────────

    public function test_issue_form_prelinks_feature_from_query(): void
    {
        $feature = $this->makeFeature();

        // The create form mounts during the GET request, where it reads ?feature=.
        $this->actingAs($this->user)
            ->get(route('projects.issues.create', ['project' => $this->project, 'feature' => $feature->id]))
            ->assertOk()
            ->assertSee('Muro norte')
            ->assertSee('Vinculada al elemento del mapa');
    }
}
