<?php

namespace Tests\Feature;

use App\Livewire\Estimates\Create;
use App\Livewire\Estimates\Edit;
use App\Livewire\Estimates\Show;
use App\Models\Branch;
use App\Models\Customer;
use App\Models\Diagnosis;
use App\Models\Estimate;
use App\Models\JobCard;
use App\Models\User;
use App\Models\Vehicle;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Tests\TestCase;

class EstimateModuleTest extends TestCase
{
    use RefreshDatabase;

    private User $user;

    private Branch $branch;

    private Customer $customer;

    private Vehicle $vehicle;

    private JobCard $jobCard;

    private Diagnosis $diagnosis;

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

        // Create test data using factories
        $this->user = User::factory()->create([
            'role' => 'service_coordinator',
        ]);

        // Give user required permissions for estimates
        $permissions = [
            ['name' => 'service-orders.view', 'display_name' => 'View Service Orders', 'module' => 'service-orders'],
            ['name' => 'service-orders.create', 'display_name' => 'Create Service Orders', 'module' => 'service-orders'],
            ['name' => 'service-orders.update', 'display_name' => 'Update Service Orders', 'module' => 'service-orders'],
        ];

        foreach ($permissions as $permissionData) {
            $permission = \App\Models\Permission::firstOrCreate(
                ['name' => $permissionData['name']],
                [
                    'display_name' => $permissionData['display_name'],
                    'description' => $permissionData['display_name'],
                    'module' => $permissionData['module'],
                ]
            );
            $this->user->permissions()->attach($permission);
        }

        $this->branch = Branch::create([
            'name' => 'Main Branch',
            'code' => 'MAIN',
            'address' => '123 Test St',
            'phone' => '555-0123',
            'email' => 'main@test.com',
        ]);

        $this->customer = Customer::factory()->create();
        $this->vehicle = Vehicle::factory()->create([
            'customer_id' => $this->customer->id,
        ]);

        $this->jobCard = JobCard::factory()->create([
            'customer_id' => $this->customer->id,
            'vehicle_id' => $this->vehicle->id,
            'branch_code' => $this->branch->code,
            'status' => 'in_diagnosis',
            'service_advisor_id' => $this->user->id,
        ]);

        $this->diagnosis = Diagnosis::create([
            'job_card_id' => $this->jobCard->id,
            'service_coordinator_id' => $this->user->id,
            'diagnostic_findings' => 'Worn brake pads detected',
            'labor_operations' => [
                [
                    'operation' => 'Replace front brake pads',
                    'estimated_hours' => 2.0,
                    'labor_rate' => 85.00,
                ],
            ],
            'parts_required' => [
                [
                    'part_name' => 'Front brake pads',
                    'part_number' => 'BP-TOY-001',
                    'quantity' => 1,
                    'estimated_cost' => 75.00,
                ],
            ],
            'estimated_repair_time' => 2.5,
            'diagnosis_status' => 'completed',
        ]);

        $this->actingAs($this->user);
    }

    /** @test */
    public function it_can_create_an_estimate_from_diagnosis()
    {
        Livewire::test(Create::class, ['diagnosis' => $this->diagnosis])
            ->assertSet('diagnosis.id', $this->diagnosis->id)
            ->assertViewHas('diagnosis')
            ->call('save')
            ->assertHasNoErrors()
            ->assertRedirect();

        $this->assertDatabaseHas('estimates', [
            'job_card_id' => $this->jobCard->id,
            'diagnosis_id' => $this->diagnosis->id,
            'prepared_by_id' => $this->user->id,
            'status' => 'draft',
        ]);

        // Check that line items were created
        $estimate = Estimate::latest()->first();
        $this->assertGreaterThan(0, $estimate->lineItems()->count());

        // Check that totals were calculated
        $this->assertGreaterThan(0, $estimate->total_amount);
    }

    /** @test */
    public function it_calculates_totals_correctly()
    {
        $component = Livewire::test(Create::class, ['diagnosis' => $this->diagnosis]);

        // Verify initial line items from diagnosis
        $lineItems = $component->get('lineItems');
        $this->assertNotEmpty($lineItems);

        // Check that totals are calculated
        $subtotal = $component->get('subtotal');
        $taxAmount = $component->get('tax_amount');
        $totalAmount = $component->get('total_amount');

        $this->assertGreaterThan(0, $subtotal);
        $this->assertGreaterThan(0, $taxAmount);
        $this->assertGreaterThan(0, $totalAmount);

        // Verify total calculation: subtotal + tax = total
        $expectedTotal = $subtotal + $taxAmount;
        $this->assertEquals($expectedTotal, $totalAmount);
    }

    /** @test */
    public function it_can_add_and_remove_line_items()
    {
        $component = Livewire::test(Create::class, ['diagnosis' => $this->diagnosis]);

        $initialCount = count($component->get('lineItems'));

        // Add a line item
        $component->call('addLineItem');
        $this->assertCount($initialCount + 1, $component->get('lineItems'));

        // Remove a line item
        $component->call('removeLineItem', 0);
        $this->assertCount($initialCount, $component->get('lineItems'));
    }

    /** @test */
    public function it_validates_required_fields()
    {
        Livewire::test(Create::class, ['diagnosis' => $this->diagnosis])
            ->set('terms_and_conditions', '')
            ->set('validity_period_days', 0)
            ->set('tax_rate', -1)
            ->call('save')
            ->assertHasErrors([
                'terms_and_conditions',
                'validity_period_days',
                'tax_rate',
            ]);
    }

    /** @test */
    public function it_can_view_an_estimate()
    {
        $estimate = Estimate::create([
            'estimate_number' => 'MAIN/EST0001',
            'job_card_id' => $this->jobCard->id,
            'diagnosis_id' => $this->diagnosis->id,
            'prepared_by_id' => $this->user->id,
            'labor_cost' => 170.00,
            'parts_cost' => 90.00,
            'subtotal' => 260.00,
            'tax_rate' => 8.25,
            'tax_amount' => 21.45,
            'total_amount' => 281.45,
            'validity_period_days' => 30,
            'terms_and_conditions' => 'Test terms',
            'status' => 'draft',
        ]);

        Livewire::test(Show::class, ['estimate' => $estimate])
            ->assertSet('estimate.id', $estimate->id)
            ->assertSee($estimate->estimate_number)
            ->assertSee('$281.45')
            ->assertViewHas('estimate');
    }

    /** @test */
    public function it_can_edit_an_estimate()
    {
        $estimate = Estimate::factory()->create([
            'job_card_id' => $this->jobCard->id,
            'terms_and_conditions' => 'Original terms',
            'validity_period_days' => 30,
        ]);

        // Test the component functionality directly without layout rendering
        $component = Livewire::test(\App\Livewire\Estimates\Edit::class, ['estimate' => $estimate])
            ->set('terms_and_conditions', 'Updated terms and conditions')
            ->set('validity_period_days', 45);

        // Assert the properties are set correctly
        $component->assertSet('terms_and_conditions', 'Updated terms and conditions')
            ->assertSet('validity_period_days', 45);

        // Call save method
        $component->call('save')
            ->assertHasNoErrors();

        $this->assertDatabaseHas('estimates', [
            'id' => $estimate->id,
            'terms_and_conditions' => 'Updated terms and conditions',
            'validity_period_days' => 45,
        ]);
    }

    /** @test */
    public function it_generates_unique_estimate_numbers()
    {
        $initialCount = Estimate::count();

        // Create first estimate
        Livewire::test(Create::class, ['diagnosis' => $this->diagnosis])
            ->call('save');

        $this->assertEquals($initialCount + 1, Estimate::count());
        $firstEstimate = Estimate::latest()->first();

        // Create another diagnosis for second estimate
        $diagnosis2 = Diagnosis::create([
            'job_card_id' => $this->jobCard->id,
            'service_coordinator_id' => $this->user->id,
            'diagnostic_findings' => 'Different issue',
            'diagnosis_status' => 'completed',
        ]);

        // Create second estimate
        Livewire::test(Create::class, ['diagnosis' => $diagnosis2])
            ->call('save');

        $this->assertEquals($initialCount + 2, Estimate::count());
        $estimates = Estimate::latest()->take(2)->get();
        $secondEstimate = $estimates->first();
        $firstEstimate = $estimates->last();

        $this->assertNotEquals($firstEstimate->estimate_number, $secondEstimate->estimate_number);
        $this->assertStringContainsString('MAIN/EST', $firstEstimate->estimate_number);
        $this->assertStringContainsString('MAIN/EST', $secondEstimate->estimate_number);
    }

    /** @test */
    public function it_updates_job_card_status_when_estimate_created()
    {
        $this->assertEquals('in_diagnosis', $this->jobCard->status);

        Livewire::test(Create::class, ['diagnosis' => $this->diagnosis])
            ->call('save');

        $this->jobCard->refresh();
        $this->assertEquals('estimate_prepared', $this->jobCard->status);
    }

    /** @test */
    public function estimate_routes_are_accessible()
    {
        // Skip permission checks for this test by using an admin role
        $this->user->update(['role' => 'admin']);

        $estimate = Estimate::create([
            'estimate_number' => 'MAIN/EST0001',
            'job_card_id' => $this->jobCard->id,
            'diagnosis_id' => $this->diagnosis->id,
            'prepared_by_id' => $this->user->id,
            'labor_cost' => 170.00,
            'parts_cost' => 90.00,
            'subtotal' => 260.00,
            'tax_rate' => 8.25,
            'tax_amount' => 21.45,
            'total_amount' => 281.45,
            'validity_period_days' => 30,
            'terms_and_conditions' => 'Test terms',
            'status' => 'draft',
        ]);

        // Test index route
        $this->actingAs($this->user)
            ->get('/estimates')
            ->assertStatus(200);

        // Test show route
        $this->actingAs($this->user)
            ->get("/estimates/{$estimate->id}")
            ->assertStatus(200);

        // Test edit route
        $this->get("/estimates/{$estimate->id}/edit")
            ->assertStatus(200);

        // Test create route
        $this->get("/estimates/create/{$this->diagnosis->id}")
            ->assertStatus(200);
    }

    /** @test */
    public function it_can_calculate_estimate_expiration()
    {
        $estimate = Estimate::create([
            'estimate_number' => 'MAIN/EST0001',
            'job_card_id' => $this->jobCard->id,
            'diagnosis_id' => $this->diagnosis->id,
            'prepared_by_id' => $this->user->id,
            'labor_cost' => 170.00,
            'parts_cost' => 90.00,
            'subtotal' => 260.00,
            'tax_rate' => 8.25,
            'tax_amount' => 21.45,
            'total_amount' => 281.45,
            'validity_period_days' => 30,
            'terms_and_conditions' => 'Test terms',
            'status' => 'draft',
        ]);

        $expectedValidUntil = $estimate->created_at->addDays(30);
        $this->assertEquals($expectedValidUntil->format('Y-m-d'), $estimate->valid_until->format('Y-m-d'));

        // Test if estimate is not expired (fresh estimate)
        $this->assertFalse($estimate->is_expired);
    }
}
