<?php

namespace Tests\Feature\Audit;

use App\Models\Central\Company;
use App\Models\Central\Plan;
use App\Models\Tenant\Appointment;
use App\Models\Tenant\Client;
use App\Models\Tenant\Deal;
use App\Models\Tenant\Lead;
use App\Models\Tenant\Payment;
use App\Models\Tenant\Vehicle;
use App\Models\Tenant\WorkOrder;
use App\Models\Tenant\WorkOrderWork;
use App\Tenancy\TenantManager;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

/**
 * End-to-end: a single customer travels through the CRM funnel.
 * Exercises 5 previously-untested models (Lead, Deal, Appointment, Payment)
 * + their relations to Client / Vehicle / WorkOrder.
 */
class CrmFunnelE2ETest extends TestCase
{
    use RefreshDatabase;

    public function test_full_journey_lead_to_payment_with_balance_due(): void
    {
        $ctx = $this->bootTenant();

        // 1. Inbound lead (anonymous, no client yet).
        $lead = Lead::create([
            'name' => 'Ion Pop',
            'phone' => '+37369123456',
            'email' => 'ion@example.com',
            'car' => 'BMW',
            'model' => 'X5',
            'message' => 'Vreau diagnoză + schimb plăcuțe',
            'source' => 'site',
            'status' => 'new',
        ]);

        $this->assertEquals('new', $lead->status);
        $this->assertNull($lead->client_id);

        // 2. Convert lead → Client + Deal. (Mirrors what UI Lead-convert action does.)
        $client = Client::create([
            'name' => $lead->name, 'phone' => $lead->phone, 'email' => $lead->email,
            'type' => 'individual', 'status' => 'active',
        ]);
        $vehicle = Vehicle::create([
            'client_id' => $client->id,
            'make' => $lead->car, 'model' => $lead->model,
            'plate' => 'TST-001',
        ]);
        $deal = Deal::create([
            'client_id' => $client->id,
            'vehicle_id' => $vehicle->id,
            'name' => 'BMW X5 — frâne + diag',
            'price' => 0,
            'stage' => 'new',
        ]);
        $lead->update([
            'client_id' => $client->id,
            'vehicle_id' => $vehicle->id,
            'deal_id' => $deal->id,
            'status' => 'won',
            'converted_at' => now(),
        ]);

        // Verify Lead now linked.
        $this->assertEquals($client->id, $lead->fresh()->client_id);
        $this->assertEquals($deal->id, $lead->fresh()->deal_id);
        $this->assertNotNull($lead->fresh()->converted_at);

        // 3. Schedule appointment from deal.
        $appt = Appointment::create([
            'client_id' => $client->id, 'vehicle_id' => $vehicle->id,
            'deal_id' => $deal->id,
            'date' => today()->addDays(2)->toDateString(),
            'time_start' => '10:00:00', 'time_end' => '12:00:00',
            'title' => 'Diagnoză BMW X5',
            'status' => 'scheduled',
        ]);
        $this->assertEquals($deal->id, $appt->deal_id);

        // 4. Open WorkOrder from appointment.
        $wo = WorkOrder::create([
            'number' => WorkOrder::generateNumber($ctx['company']->id),
            'client_id' => $client->id,
            'vehicle_id' => $vehicle->id,
            'deal_id' => $deal->id,
            'appointment_id' => $appt->id,
            'opened_at' => today(),
            'status' => 'in_work',
            'complaint' => $lead->message,
        ]);

        // 5. Add labor — WO total should update via saved hook.
        WorkOrderWork::create([
            'work_order_id' => $wo->id,
            'name' => 'Schimb plăcuțe față',
            'hours' => 1.5,
            'price_per_hour' => 400,
            'status' => 'todo',
        ]);

        $wo->refresh();
        $this->assertEquals(600.0, (float) $wo->total, '1.5h × 400 = 600');
        $this->assertEquals(600.0, $wo->balanceDue(), 'no payments yet, full due');

        // 6. Customer pays 250 cash.
        Payment::create([
            'client_id' => $client->id,
            'work_order_id' => $wo->id,
            'paid_at' => today(),
            'amount' => 250,
            'method' => 'cash',
        ]);

        $wo->refresh();
        $this->assertEquals(250.0, $wo->paidAmount());
        $this->assertEquals(350.0, $wo->balanceDue(), '600 − 250 = 350');

        // 7. Customer pays the rest.
        Payment::create([
            'client_id' => $client->id,
            'work_order_id' => $wo->id,
            'paid_at' => today(),
            'amount' => 350,
            'method' => 'card',
        ]);

        $wo->refresh();
        $this->assertEquals(600.0, $wo->paidAmount());
        $this->assertEquals(0.0, $wo->balanceDue());

        // 8. Close the WO. master_id wasn't set, so no push attempt → no error.
        $wo->update(['status' => 'done', 'closed_at' => today(), 'pay_status' => 'paid']);
        $this->assertEquals('done', $wo->fresh()->status);

        // 9. The Deal can now be marked won.
        $deal->update(['stage' => 'done', 'price' => $wo->total, 'won_at' => now()]);
        $this->assertEquals('done', $deal->fresh()->stage);
    }

    public function test_lead_status_transitions_persist(): void
    {
        $this->bootTenant();
        $lead = Lead::create(['name' => 'X', 'phone' => '+1', 'status' => 'new']);

        foreach (['contacted', 'won', 'lost'] as $st) {
            $lead->update(['status' => $st]);
            $this->assertEquals($st, $lead->fresh()->status);
        }
    }

    public function test_payment_negative_amount_not_silently_increases_balance(): void
    {
        $ctx = $this->bootTenant();
        $client = Client::create(['name' => 'X', 'phone' => '+1', 'type' => 'individual', 'status' => 'active']);
        $vehicle = Vehicle::create(['client_id' => $client->id, 'make' => 'X', 'model' => 'Y', 'plate' => 'A1']);
        $wo = WorkOrder::create([
            'number' => WorkOrder::generateNumber($ctx['company']->id),
            'client_id' => $client->id, 'vehicle_id' => $vehicle->id,
            'opened_at' => today(), 'status' => 'in_work',
        ]);
        WorkOrderWork::create([
            'work_order_id' => $wo->id, 'name' => 'X',
            'hours' => 1, 'price_per_hour' => 100, 'status' => 'todo',
        ]);
        $wo->refresh();
        $this->assertEquals(100.0, $wo->balanceDue());

        // A negative payment (refund) is supported and should INCREASE balance back.
        Payment::create([
            'client_id' => $client->id, 'work_order_id' => $wo->id,
            'paid_at' => today(), 'amount' => -50, 'method' => 'cash',
        ]);
        $wo->refresh();
        $this->assertEquals(-50.0, $wo->paidAmount(), 'negative paidAmount means refund');
        $this->assertEquals(150.0, $wo->balanceDue(), 'refund of 50 adds to outstanding: 100 − (−50) = 150');
    }

    private function bootTenant(): array
    {
        $plan = Plan::firstOrCreate(['slug' => 'test'], ['name' => 'T', 'price' => 0, 'features' => []]);
        $company = Company::create([
            'plan_id' => $plan->id, 'slug' => 'crm-' . uniqid(),
            'name' => 'CRM Co', 'status' => 'active',
        ]);
        app(TenantManager::class)->setCurrent($company);
        return compact('company');
    }
}
