<?php

declare(strict_types=1);

use App\Models\Quote;
use App\Models\User;
use App\Notifications\QuoteSentNotification;
use Database\Seeders\RoleAndPermissionSeeder;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\URL;

beforeEach(function (): void {
    $this->seed(RoleAndPermissionSeeder::class);

    $this->admin = User::factory()->create();
    $this->admin->assignRole('admin');

    $this->customer = User::factory()->create();
    $this->customer->assignRole('customer');
});

test('admin can list quotes', function (): void {
    Quote::factory()->count(3)->create(['created_by' => $this->admin->id]);

    $response = $this->actingAs($this->admin)
        ->get('http://admin.ezscale.dev/quotes');

    $response->assertOk();
});

test('admin can view create quote page', function (): void {
    $response = $this->actingAs($this->admin)
        ->get('http://admin.ezscale.dev/quotes/create');

    $response->assertOk();
});

test('admin can create a quote for an existing customer', function (): void {
    $response = $this->actingAs($this->admin)
        ->post('http://admin.ezscale.dev/quotes', [
            'user_id' => $this->customer->id,
            'items' => [
                ['description' => 'VPS Basic Plan', 'quantity' => 1, 'unit_price' => 29.99],
                ['description' => 'Extra IP Address', 'quantity' => 2, 'unit_price' => 3.00],
            ],
            'tax' => 3.60,
            'currency' => 'USD',
            'notes' => 'Special offer for valued customer.',
            'valid_until' => now()->addDays(30)->format('Y-m-d'),
        ]);

    $response->assertRedirect();

    $this->assertDatabaseHas('quotes', [
        'user_id' => $this->customer->id,
        'currency' => 'USD',
        'status' => 'draft',
    ]);

    $quote = Quote::latest('id')->first();
    expect((float) $quote->subtotal)->toBe(35.99);
    expect((float) $quote->tax)->toBe(3.60);
    expect((float) $quote->total)->toBe(39.59);
    expect($quote->items)->toHaveCount(2);
});

test('admin can create a quote for a prospect', function (): void {
    $response = $this->actingAs($this->admin)
        ->post('http://admin.ezscale.dev/quotes', [
            'prospect_email' => 'prospect@example.com',
            'prospect_name' => 'John Prospect',
            'items' => [
                ['description' => 'Dedicated Server', 'quantity' => 1, 'unit_price' => 199.99],
            ],
            'tax' => 0,
            'currency' => 'USD',
        ]);

    $response->assertRedirect();

    $this->assertDatabaseHas('quotes', [
        'prospect_email' => 'prospect@example.com',
        'prospect_name' => 'John Prospect',
        'user_id' => null,
    ]);
});

test('admin can view a quote', function (): void {
    $quote = Quote::factory()->create(['created_by' => $this->admin->id]);

    $response = $this->actingAs($this->admin)
        ->get("http://admin.ezscale.dev/quotes/{$quote->id}");

    $response->assertOk();
});

test('admin can edit a quote page', function (): void {
    $quote = Quote::factory()->create(['created_by' => $this->admin->id, 'status' => 'draft']);

    $response = $this->actingAs($this->admin)
        ->get("http://admin.ezscale.dev/quotes/{$quote->id}/edit");

    $response->assertOk();
});

test('admin can update a draft quote', function (): void {
    $quote = Quote::factory()->create([
        'created_by' => $this->admin->id,
        'user_id' => $this->customer->id,
        'status' => 'draft',
    ]);

    $response = $this->actingAs($this->admin)
        ->put("http://admin.ezscale.dev/quotes/{$quote->id}", [
            'user_id' => $this->customer->id,
            'items' => [
                ['description' => 'Updated Service', 'quantity' => 1, 'unit_price' => 50.00],
            ],
            'tax' => 5.00,
            'currency' => 'EUR',
        ]);

    $response->assertRedirect();

    $quote->refresh();
    expect($quote->currency)->toBe('EUR');
    expect((float) $quote->total)->toBe(55.00);
});

test('admin cannot edit an accepted quote', function (): void {
    $quote = Quote::factory()->accepted()->create([
        'created_by' => $this->admin->id,
        'user_id' => $this->customer->id,
    ]);

    $response = $this->actingAs($this->admin)
        ->put("http://admin.ezscale.dev/quotes/{$quote->id}", [
            'user_id' => $this->customer->id,
            'items' => [
                ['description' => 'Updated', 'quantity' => 1, 'unit_price' => 10.00],
            ],
            'tax' => 0,
            'currency' => 'USD',
        ]);

    $response->assertRedirect();
    $response->assertSessionHas('error');
});

test('admin can send a quote to customer', function (): void {
    Notification::fake();

    $quote = Quote::factory()->create([
        'created_by' => $this->admin->id,
        'user_id' => $this->customer->id,
        'status' => 'draft',
    ]);

    $response = $this->actingAs($this->admin)
        ->post("http://admin.ezscale.dev/quotes/{$quote->id}/send");

    $response->assertRedirect();

    $quote->refresh();
    expect($quote->status)->toBe('sent');
    expect($quote->sent_at)->not->toBeNull();

    Notification::assertSentTo($this->customer, QuoteSentNotification::class);
});

test('admin can send a quote to prospect', function (): void {
    Notification::fake();

    $quote = Quote::factory()->prospect()->create([
        'created_by' => $this->admin->id,
        'status' => 'draft',
    ]);

    $response = $this->actingAs($this->admin)
        ->post("http://admin.ezscale.dev/quotes/{$quote->id}/send");

    $response->assertRedirect();

    $quote->refresh();
    expect($quote->status)->toBe('sent');

    Notification::assertSentOnDemand(QuoteSentNotification::class);
});

test('admin can download a quote PDF', function (): void {
    $quote = Quote::factory()->create([
        'created_by' => $this->admin->id,
        'user_id' => $this->customer->id,
    ]);

    $response = $this->actingAs($this->admin)
        ->get("http://admin.ezscale.dev/quotes/{$quote->id}/download");

    $response->assertOk();
    expect($response->headers->get('Content-Type'))->toContain('pdf');
});

test('admin can delete a draft quote', function (): void {
    $quote = Quote::factory()->create([
        'created_by' => $this->admin->id,
        'status' => 'draft',
    ]);

    $response = $this->actingAs($this->admin)
        ->delete("http://admin.ezscale.dev/quotes/{$quote->id}");

    $response->assertRedirect();
    $this->assertDatabaseMissing('quotes', ['id' => $quote->id]);
});

test('admin cannot delete an accepted quote', function (): void {
    $quote = Quote::factory()->accepted()->create([
        'created_by' => $this->admin->id,
    ]);

    $response = $this->actingAs($this->admin)
        ->delete("http://admin.ezscale.dev/quotes/{$quote->id}");

    $response->assertRedirect();
    $this->assertDatabaseHas('quotes', ['id' => $quote->id]);
});

test('public quote show via signed URL works', function (): void {
    $quote = Quote::factory()->sent()->create([
        'created_by' => $this->admin->id,
        'user_id' => $this->customer->id,
    ]);

    $signedUrl = URL::signedRoute('quotes.show', ['quote' => $quote->id]);
    // Extract the path + query from the signed URL for the marketing domain
    $parsed = parse_url($signedUrl);
    $path = $parsed['path'] ?? '';
    $query = $parsed['query'] ?? '';

    $response = $this->get("http://ezscale.dev{$path}?{$query}");

    $response->assertOk();
});

test('public quote accept via signed URL works', function (): void {
    $quote = Quote::factory()->sent()->create([
        'created_by' => $this->admin->id,
        'user_id' => $this->customer->id,
        'valid_until' => now()->addDays(30),
    ]);

    $signedUrl = URL::signedRoute('quotes.accept', ['quote' => $quote->id]);
    $parsed = parse_url($signedUrl);
    $path = $parsed['path'] ?? '';
    $query = $parsed['query'] ?? '';

    $response = $this->post("http://ezscale.dev{$path}?{$query}");

    $response->assertRedirect();

    $quote->refresh();
    expect($quote->status)->toBe('accepted');
    expect($quote->accepted_at)->not->toBeNull();
});

test('expired quote cannot be accepted', function (): void {
    $quote = Quote::factory()->expired()->create([
        'created_by' => $this->admin->id,
        'user_id' => $this->customer->id,
    ]);

    $signedUrl = URL::signedRoute('quotes.accept', ['quote' => $quote->id]);
    $parsed = parse_url($signedUrl);
    $path = $parsed['path'] ?? '';
    $query = $parsed['query'] ?? '';

    $response = $this->post("http://ezscale.dev{$path}?{$query}");

    $response->assertRedirect();

    $quote->refresh();
    expect($quote->status)->not->toBe('accepted');
});

test('public quote reject via signed URL works', function (): void {
    $quote = Quote::factory()->sent()->create([
        'created_by' => $this->admin->id,
        'user_id' => $this->customer->id,
    ]);

    $signedUrl = URL::signedRoute('quotes.reject', ['quote' => $quote->id]);
    $parsed = parse_url($signedUrl);
    $path = $parsed['path'] ?? '';
    $query = $parsed['query'] ?? '';

    $response = $this->post("http://ezscale.dev{$path}?{$query}");

    $response->assertRedirect();

    $quote->refresh();
    expect($quote->status)->toBe('rejected');
});

test('Quote::generateNumber produces correct format', function (): void {
    $number = Quote::generateNumber();

    expect($number)->toStartWith('QT-');
    expect(strlen($number))->toBe(9); // QT- + 6 chars
});

test('Quote::isExpired returns true for past dates', function (): void {
    $quote = Quote::factory()->create([
        'created_by' => $this->admin->id,
        'valid_until' => now()->subDay(),
    ]);

    expect($quote->isExpired())->toBeTrue();
});

test('Quote::isExpired returns false for future dates', function (): void {
    $quote = Quote::factory()->create([
        'created_by' => $this->admin->id,
        'valid_until' => now()->addDays(30),
    ]);

    expect($quote->isExpired())->toBeFalse();
});

test('Quote::isExpired returns false when no valid_until set', function (): void {
    $quote = Quote::factory()->create([
        'created_by' => $this->admin->id,
        'valid_until' => null,
    ]);

    expect($quote->isExpired())->toBeFalse();
});

test('Quote filters by search', function (): void {
    Quote::factory()->create([
        'created_by' => $this->admin->id,
        'number' => 'QT-ABCDEF',
    ]);
    Quote::factory()->create([
        'created_by' => $this->admin->id,
        'number' => 'QT-XYZYXZ',
    ]);

    $response = $this->actingAs($this->admin)
        ->get('http://admin.ezscale.dev/quotes?search=ABCDEF');

    $response->assertOk();
});

test('Quote filters by status', function (): void {
    Quote::factory()->create([
        'created_by' => $this->admin->id,
        'status' => 'draft',
    ]);
    Quote::factory()->sent()->create([
        'created_by' => $this->admin->id,
    ]);

    $response = $this->actingAs($this->admin)
        ->get('http://admin.ezscale.dev/quotes?status=draft');

    $response->assertOk();
});

test('store quote validates required items', function (): void {
    $response = $this->actingAs($this->admin)
        ->post('http://admin.ezscale.dev/quotes', [
            'user_id' => $this->customer->id,
            'items' => [],
            'currency' => 'USD',
        ]);

    $response->assertSessionHasErrors('items');
});

test('store quote requires customer or prospect email', function (): void {
    $response = $this->actingAs($this->admin)
        ->post('http://admin.ezscale.dev/quotes', [
            'items' => [
                ['description' => 'Test', 'quantity' => 1, 'unit_price' => 10],
            ],
            'currency' => 'USD',
        ]);

    $response->assertSessionHasErrors('prospect_email');
});
