<?php

namespace Tests\Feature;

use App\Enums\AppointmentStatus;
use App\Enums\InvoiceStatus;
use App\Enums\UserRole;
use App\Models\Appointment;
use App\Models\Business;
use App\Models\Customer;
use App\Models\Invoice;
use App\Models\InvoiceItem;
use App\Models\Service;
use App\Models\StaffProfile;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class CustomerCrudTest extends TestCase
{
    use RefreshDatabase;

    public function test_owner_can_filter_customers_from_backend_queries(): void
    {
        [$owner, $business] = $this->ownerWithBusiness();
        $matchingCustomer = $this->customer($business, [
            'name' => 'Ariana Ledger',
            'email' => 'ariana@example.test',
            'phone' => '(555) 900-1000',
            'status' => Customer::STATUS_VIP,
            'source' => 'Public booking page',
        ]);
        $this->invoice($business, $matchingCustomer, ['balance_due' => 50]);

        $this->customer($business, [
            'name' => 'Bernard Filterfail',
            'status' => Customer::STATUS_INACTIVE,
            'source' => 'Referral',
        ]);

        $this->actingAs($owner)
            ->get('/customers?q=ariana&status=vip&source=Public%20booking%20page&balance=has_due')
            ->assertOk()
            ->assertSee('Ariana Ledger')
            ->assertDontSee('Bernard Filterfail');
    }

    public function test_owner_can_create_customer(): void
    {
        [$owner, $business] = $this->ownerWithBusiness();

        $response = $this->actingAs($owner)->post('/customers', [
            'name' => 'Nadia Cruz',
            'email' => 'nadia@example.test',
            'phone' => '(555) 111-2222',
            'status' => Customer::STATUS_ACTIVE,
            'birthday' => '1991-04-15',
            'source' => 'Walk-in',
            'address' => '24 Pine Street',
            'notes' => 'Prefers morning appointments.',
        ]);

        $customer = Customer::query()->where('email', 'nadia@example.test')->first();

        $response->assertRedirect(route('admin.customers.show', $customer));
        $this->assertDatabaseHas('customers', [
            'business_id' => $business->id,
            'name' => 'Nadia Cruz',
            'status' => Customer::STATUS_ACTIVE,
            'source' => 'Walk-in',
        ]);
        $this->assertSame('24 Pine Street', $customer->address);
        $this->assertStringContainsString('Prefers morning appointments.', json_encode($customer->note_history));
    }

    public function test_owner_can_update_customer(): void
    {
        [$owner, $business] = $this->ownerWithBusiness();
        $customer = $this->customer($business, ['name' => 'Lena Brooks']);

        $this->actingAs($owner)->put('/customers/'.$customer->id, [
            'name' => 'Lena B. Brooks',
            'email' => 'lena@example.test',
            'phone' => '(555) 333-4444',
            'status' => Customer::STATUS_VIP,
            'birthday' => '1988-09-08',
            'source' => 'Referral',
            'address' => '42 Market Lane',
            'notes' => 'Updated CRM profile note.',
        ])->assertRedirect(route('admin.customers.show', $customer));

        $customer->refresh();

        $this->assertSame('Lena B. Brooks', $customer->name);
        $this->assertSame(Customer::STATUS_VIP, $customer->status);
        $this->assertSame('42 Market Lane', $customer->address);
        $this->assertSame('Updated CRM profile note.', $customer->notes);
    }

    public function test_owner_can_view_profile_with_appointment_and_invoice_history(): void
    {
        [$owner, $business] = $this->ownerWithBusiness();
        $customer = $this->customer($business, [
            'name' => 'Mira Patel',
            'notes' => 'Sensitive skin products only.',
            'meta' => [
                'address' => '9 Garden Road',
                'notes_history' => [
                    ['author' => 'Front Desk', 'date' => now()->toDateString(), 'text' => 'Sensitive skin products only.'],
                ],
            ],
        ]);
        $service = $this->service($business, ['name' => 'Signature Facial']);
        $staff = $this->staff($business, ['name' => 'Olivia Brown']);
        $appointment = $this->appointment($business, $customer, $service, $staff);
        $invoice = $this->invoice($business, $customer, [
            'appointment_id' => $appointment->id,
            'invoice_number' => 'INV-MIRA-001',
        ]);
        InvoiceItem::query()->create([
            'business_id' => $business->id,
            'invoice_id' => $invoice->id,
            'service_id' => $service->id,
            'description' => 'Signature Facial',
            'quantity' => 1,
            'unit_price' => 80,
            'line_total' => 80,
        ]);

        $this->actingAs($owner)
            ->get('/customers/'.$customer->id)
            ->assertOk()
            ->assertSee('Mira Patel')
            ->assertSee('Signature Facial')
            ->assertSee('Olivia Brown')
            ->assertSee('INV-MIRA-001')
            ->assertSee('Sensitive skin products only.');
    }

    public function test_owner_can_add_customer_note_and_delete_customer(): void
    {
        [$owner, $business] = $this->ownerWithBusiness();
        $customer = $this->customer($business, ['name' => 'Iris Nolan']);

        $this->actingAs($owner)
            ->post('/customers/'.$customer->id.'/notes', ['note' => 'Calls before arriving.'])
            ->assertRedirect(route('admin.customers.show', $customer));

        $customer->refresh();
        $this->assertStringContainsString('Calls before arriving.', json_encode($customer->note_history));

        $this->actingAs($owner)
            ->delete('/customers/'.$customer->id)
            ->assertRedirect('/customers');

        $this->assertSoftDeleted('customers', ['id' => $customer->id]);
    }

    public function test_owner_cannot_access_another_business_customer(): void
    {
        [$owner] = $this->ownerWithBusiness();
        [, $otherBusiness] = $this->ownerWithBusiness('other-owner@booklio.test');
        $otherCustomer = $this->customer($otherBusiness, ['name' => 'Private Customer']);

        $this->actingAs($owner)
            ->get('/customers/'.$otherCustomer->id)
            ->assertNotFound();
    }

    private function ownerWithBusiness(string $email = 'owner@booklio.test'): array
    {
        $owner = User::factory()->create([
            'email' => $email,
            'role' => UserRole::BusinessOwner->value,
        ]);
        $business = Business::factory()->create([
            'owner_id' => $owner->id,
            'status' => 'active',
        ]);

        return [$owner, $business];
    }

    private function customer(Business $business, array $overrides = []): Customer
    {
        return Customer::query()->create(array_merge([
            'business_id' => $business->id,
            'name' => 'Jessica Miller',
            'email' => 'jessica@example.test',
            'phone' => '(555) 123-4567',
            'status' => Customer::STATUS_ACTIVE,
            'source' => 'Manual',
            'total_spent' => 0,
            'last_visit_at' => now(),
            'meta' => ['address' => '128 Market Street', 'notes_history' => []],
        ], $overrides));
    }

    private function service(Business $business, array $overrides = []): Service
    {
        $name = $overrides['name'] ?? 'Hair Cut & Style';

        return Service::query()->create(array_merge([
            'business_id' => $business->id,
            'name' => $name,
            'slug' => Service::uniqueSlug($business->id, $name),
            'category' => 'Beauty',
            'price' => 65,
            'duration_minutes' => 60,
            'status' => Service::STATUS_ACTIVE,
        ], $overrides));
    }

    private function staff(Business $business, array $overrides = []): StaffProfile
    {
        return StaffProfile::query()->create(array_merge([
            'business_id' => $business->id,
            'name' => 'Emma Wilson',
            'email' => 'emma@example.test',
            'phone' => '(555) 222-3333',
            'role' => 'Senior Stylist',
            'status' => StaffProfile::STATUS_ACTIVE,
            'availability_status' => StaffProfile::AVAILABILITY_AVAILABLE,
        ], $overrides));
    }

    private function appointment(Business $business, Customer $customer, Service $service, StaffProfile $staff): Appointment
    {
        return Appointment::query()->create([
            'business_id' => $business->id,
            'customer_id' => $customer->id,
            'service_id' => $service->id,
            'staff_profile_id' => $staff->id,
            'booking_number' => 'BK-CUSTOMER-001',
            'status' => AppointmentStatus::Completed->value,
            'starts_at' => now()->setTime(10, 0),
            'ends_at' => now()->setTime(11, 0),
            'duration_minutes' => 60,
            'price' => 80,
        ]);
    }

    private function invoice(Business $business, Customer $customer, array $overrides = []): Invoice
    {
        return Invoice::query()->create(array_merge([
            'business_id' => $business->id,
            'customer_id' => $customer->id,
            'invoice_number' => 'INV-CUSTOMER-001',
            'status' => InvoiceStatus::Unpaid->value,
            'issue_date' => now()->toDateString(),
            'due_date' => now()->addDays(7)->toDateString(),
            'subtotal' => 80,
            'total' => 80,
            'paid_total' => 0,
            'balance_due' => 80,
            'currency' => 'USD',
        ], $overrides));
    }
}
