<?php

namespace Tests\Feature\CRMCore;

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

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

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

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

    // ==========================================
    // Stage Transition Tests
    // ==========================================

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

        $pipelineData = $this->createPipelineWithStages();
        $pipeline = $pipelineData['pipeline'];
        $stages = $pipelineData['stages'];

        $deal = $this->createDeal([
            'pipeline_id' => $pipeline->id,
            'deal_stage_id' => $stages['qualification']->id,
            'created_by_id' => $admin->id,
        ]);

        // Move to next stage
        $deal->update(['deal_stage_id' => $stages['meeting']->id]);

        $this->assertEquals($stages['meeting']->id, $deal->fresh()->deal_stage_id);
    }

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

        $pipelineData = $this->createPipelineWithStages();
        $pipeline = $pipelineData['pipeline'];
        $stages = $pipelineData['stages'];

        $deal = $this->createDeal([
            'pipeline_id' => $pipeline->id,
            'deal_stage_id' => $stages['proposal']->id,
            'created_by_id' => $admin->id,
        ]);

        // Move back
        $deal->update(['deal_stage_id' => $stages['qualification']->id]);

        $this->assertEquals($stages['qualification']->id, $deal->fresh()->deal_stage_id);
    }

    // ==========================================
    // Won/Lost Stage Tests
    // ==========================================

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

        $pipelineData = $this->createPipelineWithStages();
        $pipeline = $pipelineData['pipeline'];
        $stages = $pipelineData['stages'];

        $deal = $this->createDeal([
            'pipeline_id' => $pipeline->id,
            'deal_stage_id' => $stages['negotiation']->id,
            'created_by_id' => $admin->id,
        ]);

        $this->assertNull($deal->won_at);

        // Move to won stage and set won_at
        $deal->update([
            'deal_stage_id' => $stages['won']->id,
            'won_at' => now(),
            'actual_close_date' => now(),
        ]);

        $deal->refresh();
        $this->assertNotNull($deal->won_at);
        $this->assertNotNull($deal->actual_close_date);
    }

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

        $pipelineData = $this->createPipelineWithStages();
        $pipeline = $pipelineData['pipeline'];
        $stages = $pipelineData['stages'];

        $deal = $this->createDeal([
            'pipeline_id' => $pipeline->id,
            'deal_stage_id' => $stages['proposal']->id,
            'created_by_id' => $admin->id,
        ]);

        $this->assertNull($deal->lost_at);

        // Move to lost stage
        $deal->update([
            'deal_stage_id' => $stages['lost']->id,
            'lost_at' => now(),
            'lost_reason' => 'Budget constraints',
            'actual_close_date' => now(),
        ]);

        $deal->refresh();
        $this->assertNotNull($deal->lost_at);
        $this->assertEquals('Budget constraints', $deal->lost_reason);
    }

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

        $pipelineData = $this->createPipelineWithStages();
        $pipeline = $pipelineData['pipeline'];
        $stages = $pipelineData['stages'];

        $deal = $this->createDeal([
            'pipeline_id' => $pipeline->id,
            'deal_stage_id' => $stages['won']->id,
            'probability' => 100,
            'won_at' => now(),
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals(100, $deal->probability);
    }

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

        $pipelineData = $this->createPipelineWithStages();
        $pipeline = $pipelineData['pipeline'];
        $stages = $pipelineData['stages'];

        $deal = $this->createDeal([
            'pipeline_id' => $pipeline->id,
            'deal_stage_id' => $stages['lost']->id,
            'probability' => 0,
            'lost_at' => now(),
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals(0, $deal->probability);
    }

    // ==========================================
    // Pipeline Structure Tests
    // ==========================================

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

        $pipelineData = $this->createPipelineWithStages();
        $pipeline = $pipelineData['pipeline'];

        $stages = $pipeline->stages;

        $this->assertGreaterThan(0, $stages->count());

        $previousPosition = -1;
        foreach ($stages as $stage) {
            $this->assertGreaterThan($previousPosition, $stage->position);
            $previousPosition = $stage->position;
        }
    }

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

        $pipelineData = $this->createPipelineWithStages();
        $pipeline = $pipelineData['pipeline'];

        $defaultStages = $pipeline->stages()->where('is_default_for_pipeline', true)->count();

        $this->assertEquals(1, $defaultStages);
    }

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

        $pipelineData = $this->createPipelineWithStages();
        $pipeline = $pipelineData['pipeline'];

        $wonStages = $pipeline->stages()->where('is_won_stage', true)->count();
        $lostStages = $pipeline->stages()->where('is_lost_stage', true)->count();

        $this->assertEquals(1, $wonStages);
        $this->assertEquals(1, $lostStages);
    }

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

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

        $pipelineData = $this->createPipelineWithStages();
        $stages = $pipelineData['stages'];

        $deal = $this->createDeal([
            'pipeline_id' => $pipelineData['pipeline']->id,
            'deal_stage_id' => $stages['proposal']->id,
            'created_by_id' => $admin->id,
        ]);

        $this->assertNotNull($deal->stage);
        $this->assertEquals('Proposal Sent', $deal->stage->name);
    }

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

        $pipelineData = $this->createPipelineWithStages();
        $pipeline = $pipelineData['pipeline'];

        $deal = $this->createDeal([
            'pipeline_id' => $pipeline->id,
            'created_by_id' => $admin->id,
        ]);

        $this->assertNotNull($deal->pipeline);
        $this->assertEquals($pipeline->id, $deal->pipeline->id);
    }

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

        $pipelineData = $this->createPipelineWithStages();
        $pipeline = $pipelineData['pipeline'];
        $stages = $pipelineData['stages'];

        $this->createDeal([
            'pipeline_id' => $pipeline->id,
            'deal_stage_id' => $stages['qualification']->id,
            'created_by_id' => $admin->id,
        ]);
        $this->createDeal([
            'pipeline_id' => $pipeline->id,
            'deal_stage_id' => $stages['qualification']->id,
            'created_by_id' => $admin->id,
        ]);

        $this->assertCount(2, $stages['qualification']->deals);
    }

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

        $pipelineData = $this->createPipelineWithStages();
        $pipeline = $pipelineData['pipeline'];
        $stages = $pipelineData['stages'];

        $this->createDeal([
            'pipeline_id' => $pipeline->id,
            'deal_stage_id' => $stages['qualification']->id,
            'created_by_id' => $admin->id,
        ]);
        $this->createDeal([
            'pipeline_id' => $pipeline->id,
            'deal_stage_id' => $stages['meeting']->id,
            'created_by_id' => $admin->id,
        ]);
        $this->createDeal([
            'pipeline_id' => $pipeline->id,
            'deal_stage_id' => $stages['proposal']->id,
            'created_by_id' => $admin->id,
        ]);

        $this->assertCount(3, $pipeline->deals);
    }

    // ==========================================
    // Data Integrity Tests
    // ==========================================

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

        $pipelineData = $this->createPipelineWithStages();
        $pipeline = $pipelineData['pipeline'];
        $stages = $pipelineData['stages'];

        $deal = $this->createDeal([
            'pipeline_id' => $pipeline->id,
            'deal_stage_id' => $stages['qualification']->id,
            'value' => 50000.00,
            'created_by_id' => $admin->id,
        ]);

        // Move through stages
        $deal->update(['deal_stage_id' => $stages['meeting']->id]);
        $deal->update(['deal_stage_id' => $stages['proposal']->id]);
        $deal->update(['deal_stage_id' => $stages['negotiation']->id]);

        $this->assertEquals(50000.00, $deal->fresh()->value);
    }

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

        $pipelineData = $this->createPipelineWithStages();
        $pipeline = $pipelineData['pipeline'];

        $originalDate = now()->addMonth();
        $newDate = now()->addMonths(2);

        $deal = $this->createDeal([
            'pipeline_id' => $pipeline->id,
            'expected_close_date' => $originalDate,
            'created_by_id' => $admin->id,
        ]);

        $deal->update(['expected_close_date' => $newDate]);

        $this->assertEquals($newDate->format('Y-m-d'), $deal->fresh()->expected_close_date->format('Y-m-d'));
    }

    // ==========================================
    // Kanban/Board Tests
    // ==========================================

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

        $pipelineData = $this->createPipelineWithStages();
        $pipeline = $pipelineData['pipeline'];
        $stages = $pipelineData['stages'];

        // Create deals in different stages
        $this->createDeal([
            'pipeline_id' => $pipeline->id,
            'deal_stage_id' => $stages['qualification']->id,
            'created_by_id' => $admin->id,
        ]);
        $this->createDeal([
            'pipeline_id' => $pipeline->id,
            'deal_stage_id' => $stages['qualification']->id,
            'created_by_id' => $admin->id,
        ]);
        $this->createDeal([
            'pipeline_id' => $pipeline->id,
            'deal_stage_id' => $stages['proposal']->id,
            'created_by_id' => $admin->id,
        ]);

        // Group deals by stage
        $groupedDeals = Deal::where('pipeline_id', $pipeline->id)
            ->get()
            ->groupBy('deal_stage_id');

        $this->assertCount(2, $groupedDeals[$stages['qualification']->id]);
        $this->assertCount(1, $groupedDeals[$stages['proposal']->id]);
    }

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

        $pipelineData = $this->createPipelineWithStages();
        $pipeline = $pipelineData['pipeline'];
        $stages = $pipelineData['stages'];

        $this->createDeal([
            'pipeline_id' => $pipeline->id,
            'deal_stage_id' => $stages['qualification']->id,
            'value' => 10000.00,
            'created_by_id' => $admin->id,
        ]);
        $this->createDeal([
            'pipeline_id' => $pipeline->id,
            'deal_stage_id' => $stages['proposal']->id,
            'value' => 25000.00,
            'created_by_id' => $admin->id,
        ]);
        $this->createDeal([
            'pipeline_id' => $pipeline->id,
            'deal_stage_id' => $stages['negotiation']->id,
            'value' => 15000.00,
            'created_by_id' => $admin->id,
        ]);

        $totalPipelineValue = Deal::where('pipeline_id', $pipeline->id)->sum('value');

        $this->assertEquals(50000.00, $totalPipelineValue);
    }
}
