<?php

namespace Tests\Feature\CRMCore;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Modules\CRMCore\App\Models\Lead;
use Modules\CRMCore\App\Models\LeadSource;
use Modules\CRMCore\App\Models\LeadStatus;
use Tests\TestCase;
use Tests\Traits\CreatesCRMCoreEntities;
use Tests\Traits\CreatesUsers;

class LeadCrudTest extends TestCase
{
    use CreatesCRMCoreEntities;
    use CreatesUsers;
    use RefreshDatabase;

    protected function setUp(): void
    {
        parent::setUp();

        if (! moduleExists('CRMCore')) {
            $this->markTestSkipped('CRMCore module is not enabled');
        }

        // Required for view rendering (app_logo, etc.)
        $this->ensureSettingsExist();
    }

    // ==========================================
    // Create Tests
    // ==========================================

    public function test_can_create_lead(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $this->createDefaultLeadStatus();
        $source = $this->createLeadSource();

        $response = $this->postJson(route('leads.store'), [
            'title' => 'New Business Opportunity',
            'description' => 'Potential enterprise client',
            'contact_name' => 'John Smith',
            'contact_email' => 'john@enterprise.com',
            'contact_phone' => '+1234567890',
            'company_name' => 'Enterprise Corp',
            'value' => 50000.00,
            'lead_source_id' => $source->id,
        ]);

        $response->assertStatus(200);
        $response->assertJson(['code' => 200]);

        $this->assertDatabaseHas('leads', [
            'title' => 'New Business Opportunity',
            'contact_email' => 'john@enterprise.com',
        ]);
    }

    public function test_create_lead_uses_default_status(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $defaultStatus = $this->createDefaultLeadStatus();

        $response = $this->postJson(route('leads.store'), [
            'title' => 'Auto Status Lead',
        ]);

        $response->assertStatus(200);

        $lead = Lead::where('title', 'Auto Status Lead')->first();
        $this->assertNotNull($lead);
        $this->assertEquals($defaultStatus->id, $lead->lead_status_id);
    }

    public function test_create_lead_with_assignment(): void
    {
        $admin = $this->createAdmin();
        $assignee = $this->createAdmin();
        $this->actingAs($admin);

        $this->createDefaultLeadStatus();

        $response = $this->postJson(route('leads.store'), [
            'title' => 'Assigned Lead',
            'assigned_to_user_id' => $assignee->id,
        ]);

        $response->assertStatus(200);

        $lead = Lead::where('title', 'Assigned Lead')->first();
        $this->assertEquals($assignee->id, $lead->assigned_to_user_id);
    }

    public function test_create_lead_requires_title(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $response = $this->postJson(route('leads.store'), [
            'contact_name' => 'No Title Lead',
        ]);

        $response->assertStatus(422);
        $response->assertJsonValidationErrors(['title']);
    }

    public function test_create_lead_validates_email_format(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $this->createDefaultLeadStatus();

        $response = $this->postJson(route('leads.store'), [
            'title' => 'Invalid Email Lead',
            'contact_email' => 'not-an-email',
        ]);

        $response->assertStatus(422);
        $response->assertJsonValidationErrors(['contact_email']);
    }

    public function test_create_lead_validates_unique_email(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $this->createDefaultLeadStatus();

        // Create first lead
        $this->createLead([
            'contact_email' => 'unique@example.com',
            'created_by_id' => $admin->id,
        ]);

        // Try to create second lead with same email
        $response = $this->postJson(route('leads.store'), [
            'title' => 'Duplicate Email Lead',
            'contact_email' => 'unique@example.com',
        ]);

        $response->assertStatus(422);
        $response->assertJsonValidationErrors(['contact_email']);
    }

    // ==========================================
    // Read Tests
    // ==========================================

    public function test_can_view_lead_index(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $this->createDefaultLeadStatus();

        $response = $this->get(route('leads.index'));

        $response->assertStatus(200);
        $response->assertViewIs('crmcore::leads.index');
    }

    public function test_can_get_lead_ajax(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $lead = $this->createLead(['created_by_id' => $admin->id]);

        $response = $this->getJson(route('leads.getLeadAjax', $lead->id));

        $response->assertStatus(200);
        $response->assertJsonFragment(['id' => $lead->id]);
    }

    public function test_can_view_lead_details(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $lead = $this->createLeadWithSource(['created_by_id' => $admin->id]);

        $response = $this->get(route('leads.show', $lead->id));

        $response->assertStatus(200);
        $response->assertViewIs('crmcore::leads.show');
        $response->assertViewHas('lead');
    }

    public function test_can_get_datatable_data(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $this->createLead(['title' => 'Lead A', 'created_by_id' => $admin->id]);
        $this->createLead(['title' => 'Lead B', 'created_by_id' => $admin->id]);

        $response = $this->postJson(route('leads.dataTableAjax'));

        $response->assertStatus(200);
        $response->assertJsonStructure(['data']);
    }

    // ==========================================
    // Update Tests
    // ==========================================

    public function test_can_update_lead(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $lead = $this->createLead(['created_by_id' => $admin->id]);

        $response = $this->putJson(route('leads.update', $lead->id), [
            'title' => 'Updated Lead Title',
            'value' => 75000.00,
        ]);

        $response->assertStatus(200);

        $lead->refresh();
        $this->assertEquals('Updated Lead Title', $lead->title);
        $this->assertEquals(75000.00, $lead->value);
    }

    public function test_can_update_lead_status(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $newStatus = $this->createLeadStatus(['name' => 'Qualified']);
        $lead = $this->createLead(['created_by_id' => $admin->id]);

        $response = $this->putJson(route('leads.update', $lead->id), [
            'title' => $lead->title,
            'lead_status_id' => $newStatus->id,
        ]);

        $response->assertStatus(200);

        $lead->refresh();
        $this->assertEquals($newStatus->id, $lead->lead_status_id);
    }

    public function test_can_reassign_lead(): void
    {
        $admin = $this->createAdmin();
        $newAssignee = $this->createAdmin();
        $this->actingAs($admin);

        $lead = $this->createLead([
            'assigned_to_user_id' => $admin->id,
            'created_by_id' => $admin->id,
        ]);

        $response = $this->putJson(route('leads.update', $lead->id), [
            'title' => $lead->title,
            'assigned_to_user_id' => $newAssignee->id,
        ]);

        $response->assertStatus(200);

        $lead->refresh();
        $this->assertEquals($newAssignee->id, $lead->assigned_to_user_id);
    }

    public function test_update_validates_email_uniqueness_except_self(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $lead1 = $this->createLead([
            'contact_email' => 'lead1@example.com',
            'created_by_id' => $admin->id,
        ]);
        $lead2 = $this->createLead([
            'contact_email' => 'lead2@example.com',
            'created_by_id' => $admin->id,
        ]);

        // Should be able to keep own email
        $response = $this->putJson(route('leads.update', $lead1->id), [
            'title' => 'Same Email',
            'contact_email' => 'lead1@example.com',
        ]);
        $response->assertStatus(200);

        // Should not be able to use other lead's email
        $response = $this->putJson(route('leads.update', $lead1->id), [
            'title' => 'Stolen Email',
            'contact_email' => 'lead2@example.com',
        ]);
        $response->assertStatus(422);
    }

    // ==========================================
    // Kanban Update Tests
    // ==========================================

    public function test_can_update_kanban_stage(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $newStatus = $this->createLeadStatus(['name' => 'Contacted']);
        $lead = $this->createLead(['created_by_id' => $admin->id]);

        // Route uses POST, not PUT
        $response = $this->postJson(route('leads.updateKanbanStage', $lead->id), [
            'lead_status_id' => $newStatus->id,
        ]);

        $response->assertStatus(200);
        $response->assertJson(['code' => 200]);

        $lead->refresh();
        $this->assertEquals($newStatus->id, $lead->lead_status_id);
    }

    public function test_kanban_update_validates_status(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $lead = $this->createLead(['created_by_id' => $admin->id]);

        // Route uses POST, not PUT
        $response = $this->postJson(route('leads.updateKanbanStage', $lead->id), [
            'lead_status_id' => 99999, // Non-existent status
        ]);

        $response->assertStatus(422);
    }

    // ==========================================
    // Delete Tests
    // ==========================================

    public function test_can_delete_lead(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $lead = $this->createLead(['created_by_id' => $admin->id]);
        $leadId = $lead->id;

        $response = $this->deleteJson(route('leads.destroy', $lead->id));

        $response->assertStatus(200);
        $response->assertJson(['code' => 200]);

        $this->assertSoftDeleted('leads', ['id' => $leadId]);
    }

    // ==========================================
    // Kanban Data Tests
    // ==========================================

    public function test_kanban_data_excludes_final_status_leads(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $activeStatus = $this->createDefaultLeadStatus();
        $finalStatus = $this->createConvertedLeadStatus();

        // Create leads in different statuses
        $this->createLead([
            'lead_status_id' => $activeStatus->id,
            'created_by_id' => $admin->id,
        ]);
        $this->createLead([
            'lead_status_id' => $finalStatus->id,
            'created_by_id' => $admin->id,
        ]);

        $response = $this->getJson(route('leads.kanbanAjax'));

        $response->assertStatus(200);

        // Should only include leads in non-final statuses
        $data = $response->json();
        $this->assertArrayNotHasKey($finalStatus->id, $data);
    }

    // ==========================================
    // Relationship Tests
    // ==========================================

    public function test_lead_belongs_to_status(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $lead = $this->createLead(['created_by_id' => $admin->id]);

        $this->assertNotNull($lead->leadStatus);
        $this->assertInstanceOf(LeadStatus::class, $lead->leadStatus);
    }

    public function test_lead_belongs_to_source(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $lead = $this->createLeadWithSource(['created_by_id' => $admin->id]);

        $this->assertNotNull($lead->leadSource);
        $this->assertInstanceOf(LeadSource::class, $lead->leadSource);
    }

    public function test_lead_can_have_assigned_user(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $lead = $this->createLead([
            'assigned_to_user_id' => $admin->id,
            'created_by_id' => $admin->id,
        ]);

        $this->assertNotNull($lead->assignedToUser);
        $this->assertEquals($admin->id, $lead->assignedToUser->id);
    }

    // ==========================================
    // Search Tests
    // ==========================================

    public function test_can_search_leads_for_select(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $this->createLead([
            'title' => 'Searchable Lead ABC',
            'created_by_id' => $admin->id,
        ]);
        $this->createLead([
            'title' => 'Another Lead XYZ',
            'created_by_id' => $admin->id,
        ]);

        $response = $this->getJson(route('leads.selectSearch', ['q' => 'ABC']));

        $response->assertStatus(200);
        $response->assertJsonStructure(['results', 'pagination']);

        $results = $response->json('results');
        $this->assertCount(1, $results);
    }

    // ==========================================
    // Soft Delete Tests
    // ==========================================

    public function test_deleted_leads_not_in_index(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $lead = $this->createLead(['created_by_id' => $admin->id]);
        $lead->delete();

        $response = $this->postJson(route('leads.dataTableAjax'));

        $response->assertStatus(200);
        $this->assertEmpty($response->json('data'));
    }
}
