<?php

declare(strict_types=1);

use App\Models\Invoice;
use App\Models\Service;
use App\Models\SupportTicket;
use App\Models\TicketReply;
use App\Models\User;
use App\Services\Provisioning\VirtFusionService;
use Database\Seeders\RoleAndPermissionSeeder;
use Laravel\Passport\Passport;

beforeEach(function (): void {
    $this->seed(RoleAndPermissionSeeder::class);
    Passport::$validateKeyPermissions = false;
});

// ---------------------------------------------------------------------------
// Services
// ---------------------------------------------------------------------------
describe('Services API', function (): void {
    it('lists customer services paginated', function (): void {
        $customer = User::factory()->customer()->create();
        Service::factory()->count(3)->create(['user_id' => $customer->id]);

        Passport::actingAs($customer);

        $this->getJson('/api/v1/services')
            ->assertOk()
            ->assertJsonStructure([
                'data' => [
                    '*' => ['id', 'service_type', 'platform', 'status', 'hostname', 'ipv4_address', 'created_at'],
                ],
                'links',
                'meta',
            ])
            ->assertJsonCount(3, 'data');
    });

    it('shows a single service with plan details', function (): void {
        $customer = User::factory()->customer()->create();
        $service = Service::factory()->create(['user_id' => $customer->id]);

        Passport::actingAs($customer);

        $this->getJson("/api/v1/services/{$service->id}")
            ->assertOk()
            ->assertJsonStructure([
                'data' => ['id', 'service_type', 'platform', 'status', 'hostname', 'plan', 'created_at'],
            ])
            ->assertJsonPath('data.id', $service->id);
    });

    it('returns 403 when accessing another user service', function (): void {
        $customer = User::factory()->customer()->create();
        $otherCustomer = User::factory()->customer()->create();
        $service = Service::factory()->create(['user_id' => $otherCustomer->id]);

        Passport::actingAs($customer);

        $this->getJson("/api/v1/services/{$service->id}")
            ->assertForbidden();
    });

    it('reboots a VPS service', function (): void {
        $customer = User::factory()->customer()->create();
        $service = Service::factory()->create([
            'user_id' => $customer->id,
            'service_type' => 'vps',
            'platform' => 'virtfusion',
            'status' => 'active',
        ]);

        $mock = Mockery::mock(VirtFusionService::class);
        $mock->shouldReceive('restart')->once()->andReturn(true);
        $this->app->instance(VirtFusionService::class, $mock);

        Passport::actingAs($customer);

        $this->postJson("/api/v1/services/{$service->id}/reboot")
            ->assertOk()
            ->assertJsonPath('message', 'VPS reboot initiated successfully.');
    });

    it('rejects reboot for non-VPS services', function (): void {
        $customer = User::factory()->customer()->create();
        $service = Service::factory()->create([
            'user_id' => $customer->id,
            'service_type' => 'dedicated',
            'platform' => 'synergycp',
            'status' => 'active',
        ]);

        Passport::actingAs($customer);

        $this->postJson("/api/v1/services/{$service->id}/reboot")
            ->assertUnprocessable()
            ->assertJsonPath('message', 'Reboot is only available for VPS services.');
    });

    it('rejects reboot for inactive services', function (): void {
        $customer = User::factory()->customer()->create();
        $service = Service::factory()->suspended()->create([
            'user_id' => $customer->id,
            'service_type' => 'vps',
            'platform' => 'virtfusion',
        ]);

        Passport::actingAs($customer);

        $this->postJson("/api/v1/services/{$service->id}/reboot")
            ->assertUnprocessable()
            ->assertJsonPath('message', 'Service must be active to reboot.');
    });
});

// ---------------------------------------------------------------------------
// Invoices
// ---------------------------------------------------------------------------
describe('Invoices API', function (): void {
    it('lists customer invoices paginated', function (): void {
        $customer = User::factory()->customer()->create();
        Invoice::factory()->count(3)->create(['user_id' => $customer->id]);

        Passport::actingAs($customer);

        $this->getJson('/api/v1/invoices')
            ->assertOk()
            ->assertJsonStructure([
                'data' => [
                    '*' => ['id', 'number', 'total', 'tax', 'currency', 'status', 'due_date', 'created_at'],
                ],
                'links',
                'meta',
            ])
            ->assertJsonCount(3, 'data');
    });

    it('filters invoices by status', function (): void {
        $customer = User::factory()->customer()->create();
        Invoice::factory()->count(2)->create(['user_id' => $customer->id, 'status' => 'paid']);
        Invoice::factory()->count(3)->create(['user_id' => $customer->id, 'status' => 'overdue']);

        Passport::actingAs($customer);

        $this->getJson('/api/v1/invoices?status=paid')
            ->assertOk()
            ->assertJsonCount(2, 'data');
    });

    it('does not show invoices from other users', function (): void {
        $customer = User::factory()->customer()->create();
        $other = User::factory()->customer()->create();
        Invoice::factory()->count(2)->create(['user_id' => $customer->id]);
        Invoice::factory()->count(3)->create(['user_id' => $other->id]);

        Passport::actingAs($customer);

        $this->getJson('/api/v1/invoices')
            ->assertOk()
            ->assertJsonCount(2, 'data');
    });

    it('downloads invoice PDF', function (): void {
        $customer = User::factory()->customer()->create();
        $invoice = Invoice::factory()->create(['user_id' => $customer->id]);

        Passport::actingAs($customer);

        $this->getJson("/api/v1/invoices/{$invoice->id}/pdf")
            ->assertOk()
            ->assertHeader('content-type', 'application/pdf');
    });

    it('returns 403 when downloading another user invoice PDF', function (): void {
        $customer = User::factory()->customer()->create();
        $other = User::factory()->customer()->create();
        $invoice = Invoice::factory()->create(['user_id' => $other->id]);

        Passport::actingAs($customer);

        $this->getJson("/api/v1/invoices/{$invoice->id}/pdf")
            ->assertForbidden();
    });
});

// ---------------------------------------------------------------------------
// Subscriptions
// ---------------------------------------------------------------------------
describe('Subscriptions API', function (): void {
    it('lists customer subscriptions', function (): void {
        $customer = User::factory()->customer()->create();

        // Create subscriptions via Cashier's table directly
        $customer->subscriptions()->create([
            'type' => 'default',
            'stripe_id' => 'sub_test_'.uniqid(),
            'stripe_status' => 'active',
            'stripe_price' => 'price_test',
        ]);

        Passport::actingAs($customer);

        $this->getJson('/api/v1/subscriptions')
            ->assertOk()
            ->assertJsonStructure([
                'data' => [
                    '*' => ['id', 'name', 'stripe_status', 'created_at'],
                ],
            ])
            ->assertJsonCount(1, 'data');
    });
});

// ---------------------------------------------------------------------------
// Tickets
// ---------------------------------------------------------------------------
describe('Tickets API', function (): void {
    it('lists customer tickets paginated', function (): void {
        $customer = User::factory()->customer()->create();
        SupportTicket::factory()->count(3)->create(['user_id' => $customer->id]);

        Passport::actingAs($customer);

        $this->getJson('/api/v1/tickets')
            ->assertOk()
            ->assertJsonStructure([
                'data' => [
                    '*' => ['id', 'reference', 'subject', 'status', 'priority', 'department', 'created_at'],
                ],
                'links',
                'meta',
            ])
            ->assertJsonCount(3, 'data');
    });

    it('creates a new ticket', function (): void {
        $customer = User::factory()->customer()->create();
        // Create an admin for notification
        User::factory()->admin()->create();

        Passport::actingAs($customer);

        $this->postJson('/api/v1/tickets', [
            'subject' => 'Test Ticket Subject',
            'message' => 'This is a detailed test message for the ticket body.',
            'priority' => 'medium',
            'department' => 'technical',
        ])
            ->assertCreated()
            ->assertJsonStructure([
                'data' => ['id', 'reference', 'subject', 'status', 'priority', 'department', 'replies'],
            ])
            ->assertJsonPath('data.subject', 'Test Ticket Subject')
            ->assertJsonPath('data.status', 'open');

        $this->assertDatabaseHas('support_tickets', [
            'user_id' => $customer->id,
            'subject' => 'Test Ticket Subject',
            'priority' => 'medium',
            'department' => 'technical',
        ]);
    });

    it('validates ticket creation data', function (): void {
        $customer = User::factory()->customer()->create();

        Passport::actingAs($customer);

        $this->postJson('/api/v1/tickets', [])
            ->assertUnprocessable()
            ->assertJsonValidationErrors(['subject', 'message', 'priority', 'department']);
    });

    it('shows a ticket with replies', function (): void {
        $customer = User::factory()->customer()->create();
        $ticket = SupportTicket::factory()->create(['user_id' => $customer->id]);
        TicketReply::factory()->count(2)->create([
            'ticket_id' => $ticket->id,
            'user_id' => $customer->id,
        ]);

        Passport::actingAs($customer);

        $this->getJson("/api/v1/tickets/{$ticket->id}")
            ->assertOk()
            ->assertJsonStructure([
                'data' => [
                    'id', 'reference', 'subject', 'status',
                    'replies' => [
                        '*' => ['id', 'body', 'is_staff_reply', 'user', 'created_at'],
                    ],
                ],
            ])
            ->assertJsonCount(2, 'data.replies');
    });

    it('returns 403 when viewing another user ticket', function (): void {
        $customer = User::factory()->customer()->create();
        $other = User::factory()->customer()->create();
        $ticket = SupportTicket::factory()->create(['user_id' => $other->id]);

        Passport::actingAs($customer);

        $this->getJson("/api/v1/tickets/{$ticket->id}")
            ->assertForbidden();
    });

    it('replies to an open ticket', function (): void {
        $customer = User::factory()->customer()->create();
        User::factory()->admin()->create();
        $ticket = SupportTicket::factory()->open()->create(['user_id' => $customer->id]);

        Passport::actingAs($customer);

        $this->postJson("/api/v1/tickets/{$ticket->id}/reply", [
            'body' => 'This is my reply to the ticket.',
        ])
            ->assertOk()
            ->assertJsonStructure([
                'data' => ['id', 'replies'],
            ]);

        $this->assertDatabaseHas('ticket_replies', [
            'ticket_id' => $ticket->id,
            'user_id' => $customer->id,
            'body' => 'This is my reply to the ticket.',
        ]);
    });

    it('cannot reply to a closed ticket', function (): void {
        $customer = User::factory()->customer()->create();
        $ticket = SupportTicket::factory()->closed()->create(['user_id' => $customer->id]);

        Passport::actingAs($customer);

        $this->postJson("/api/v1/tickets/{$ticket->id}/reply", [
            'body' => 'Trying to reply to closed ticket.',
        ])
            ->assertUnprocessable()
            ->assertJsonPath('message', 'Cannot reply to a closed ticket.');
    });

    it('cannot reply to another user ticket', function (): void {
        $customer = User::factory()->customer()->create();
        $other = User::factory()->customer()->create();
        $ticket = SupportTicket::factory()->open()->create(['user_id' => $other->id]);

        Passport::actingAs($customer);

        $this->postJson("/api/v1/tickets/{$ticket->id}/reply", [
            'body' => 'Trying to reply to someone else ticket.',
        ])
            ->assertForbidden();
    });
});

// ---------------------------------------------------------------------------
// Authentication
// ---------------------------------------------------------------------------
describe('API Authentication', function (): void {
    it('returns 401 for unauthenticated requests to services', function (): void {
        $this->getJson('/api/v1/services')
            ->assertUnauthorized();
    });

    it('returns 401 for unauthenticated requests to invoices', function (): void {
        $this->getJson('/api/v1/invoices')
            ->assertUnauthorized();
    });

    it('returns 401 for unauthenticated requests to subscriptions', function (): void {
        $this->getJson('/api/v1/subscriptions')
            ->assertUnauthorized();
    });

    it('returns 401 for unauthenticated requests to tickets', function (): void {
        $this->getJson('/api/v1/tickets')
            ->assertUnauthorized();
    });
});
