<?php

declare(strict_types=1);

use App\Models\SupportTicket;
use App\Models\TicketReply;
use App\Models\User;
use App\Notifications\TicketCreatedNotification;
use App\Notifications\TicketCustomerReplyNotification;
use App\Notifications\TicketStaffReplyNotification;
use App\Notifications\TicketStatusChangedNotification;
use App\Services\Tickets\ImapEmailService;
use App\Services\Tickets\ParsedEmail;
use App\Services\Tickets\TicketEmailProcessor;
use Database\Seeders\RoleAndPermissionSeeder;
use Illuminate\Support\Facades\Notification;

beforeEach(function (): void {
    $this->seed(RoleAndPermissionSeeder::class);
    $this->accountUrl = 'http://'.config('app.domains.account');
    $this->adminUrl = 'http://'.config('app.domains.admin');
});

// ---------------------------------------------------------------------------
// Ticket Reference Auto-Generation
// ---------------------------------------------------------------------------
describe('Ticket Reference', function (): void {
    it('auto-generates ticket reference on create', function (): void {
        $customer = User::factory()->customer()->create();

        $ticket = SupportTicket::factory()->create([
            'user_id' => $customer->id,
        ]);

        $ticket->refresh();

        expect($ticket->ticket_reference)->toBe('[EZSCALE-'.$ticket->id.']');
    });
});

// ---------------------------------------------------------------------------
// Email Processing - New Tickets
// ---------------------------------------------------------------------------
describe('TicketEmailProcessor - New Tickets', function (): void {
    it('creates new ticket from email for known user', function (): void {
        Notification::fake();

        $customer = User::factory()->customer()->create(['email' => 'customer@example.com']);
        User::factory()->admin()->create();

        $processor = app(TicketEmailProcessor::class);

        $email = new ParsedEmail(
            messageId: '<msg-001@example.com>',
            from: 'customer@example.com',
            subject: 'Need help with VPS',
            body: 'I cannot connect to my VPS server.',
        );

        $processor->processEmail($email);

        $ticket = SupportTicket::where('customer_email', 'customer@example.com')->first();

        expect($ticket)->not->toBeNull()
            ->and($ticket->user_id)->toBe($customer->id)
            ->and($ticket->subject)->toBe('Need help with VPS')
            ->and($ticket->status)->toBe('open')
            ->and($ticket->department)->toBe('general');

        $reply = TicketReply::where('ticket_id', $ticket->id)->first();
        expect($reply->body)->toBe('I cannot connect to my VPS server.')
            ->and($reply->via_email)->toBeTrue()
            ->and($reply->from_email)->toBe('customer@example.com')
            ->and($reply->message_id)->toBe('<msg-001@example.com>');

        Notification::assertSentTo(
            User::role('admin')->first(),
            TicketCreatedNotification::class
        );
    });

    it('creates new ticket from email for unknown sender', function (): void {
        Notification::fake();

        User::factory()->admin()->create();

        $processor = app(TicketEmailProcessor::class);

        $email = new ParsedEmail(
            messageId: '<msg-002@example.com>',
            from: 'stranger@example.com',
            subject: 'Pricing question',
            body: 'How much does a VPS cost?',
        );

        $processor->processEmail($email);

        $ticket = SupportTicket::where('customer_email', 'stranger@example.com')->first();

        expect($ticket)->not->toBeNull()
            ->and($ticket->user_id)->toBeNull()
            ->and($ticket->subject)->toBe('Pricing question');

        $reply = TicketReply::where('ticket_id', $ticket->id)->first();
        expect($reply->user_id)->toBeNull()
            ->and($reply->from_email)->toBe('stranger@example.com')
            ->and($reply->via_email)->toBeTrue();
    });
});

// ---------------------------------------------------------------------------
// Email Processing - Reply Matching
// ---------------------------------------------------------------------------
describe('TicketEmailProcessor - Reply Matching', function (): void {
    it('matches reply by subject reference [EZSCALE-{id}]', function (): void {
        Notification::fake();

        $customer = User::factory()->customer()->create(['email' => 'customer@example.com']);
        User::factory()->admin()->create();

        $ticket = SupportTicket::factory()->open()->create([
            'user_id' => $customer->id,
            'subject' => 'Server issue',
        ]);
        $ticket->refresh(); // ensure ticket_reference is populated

        $processor = app(TicketEmailProcessor::class);

        $email = new ParsedEmail(
            messageId: '<msg-reply-001@example.com>',
            from: 'customer@example.com',
            subject: 'Re: Server issue '.$ticket->ticket_reference,
            body: 'Here is additional info.',
        );

        $processor->processEmail($email);

        $replies = TicketReply::where('ticket_id', $ticket->id)->get();

        expect($replies)->toHaveCount(1)
            ->and($replies->first()->body)->toBe('Here is additional info.')
            ->and($replies->first()->via_email)->toBeTrue();
    });

    it('matches reply by In-Reply-To header', function (): void {
        Notification::fake();

        $customer = User::factory()->customer()->create(['email' => 'customer@example.com']);
        User::factory()->admin()->create();

        $ticket = SupportTicket::factory()->open()->create([
            'user_id' => $customer->id,
        ]);

        $existingReply = TicketReply::factory()->create([
            'ticket_id' => $ticket->id,
            'user_id' => $customer->id,
            'message_id' => '<original-msg@example.com>',
        ]);

        $processor = app(TicketEmailProcessor::class);

        $email = new ParsedEmail(
            messageId: '<msg-reply-002@example.com>',
            from: 'customer@example.com',
            subject: 'Re: Something totally different',
            body: 'Follow-up information.',
            inReplyTo: '<original-msg@example.com>',
        );

        $processor->processEmail($email);

        $newReplies = TicketReply::where('ticket_id', $ticket->id)
            ->where('id', '!=', $existingReply->id)
            ->get();

        expect($newReplies)->toHaveCount(1)
            ->and($newReplies->first()->body)->toBe('Follow-up information.');
    });
});

// ---------------------------------------------------------------------------
// Body Cleaning
// ---------------------------------------------------------------------------
describe('Email Body Cleaning', function (): void {
    it('strips email signatures', function (): void {
        $body = "Hello, I need help with my VPS.\n\n-- \nJohn Doe\nSenior Developer";

        $cleaned = ImapEmailService::stripSignaturesAndQuotes($body);

        expect($cleaned)->toBe('Hello, I need help with my VPS.');
    });

    it('strips quoted content', function (): void {
        $body = "Thank you for the update.\n\nOn Mon, Feb 9, 2026 John wrote:\n> Original message here\n> More quoted text";

        $cleaned = ImapEmailService::stripSignaturesAndQuotes($body);

        expect($cleaned)->toBe('Thank you for the update.');
    });
});

// ---------------------------------------------------------------------------
// Artisan Command
// ---------------------------------------------------------------------------
describe('ProcessTicketEmails Command', function (): void {
    it('exits with warning when polling is disabled', function (): void {
        config(['tickets.polling_enabled' => false]);

        $this->artisan('tickets:process-emails')
            ->expectsOutput('Ticket email polling is disabled. Set TICKET_EMAIL_POLLING=true to enable.')
            ->assertExitCode(0);
    });
});

// ---------------------------------------------------------------------------
// Notification Integration
// ---------------------------------------------------------------------------
describe('Ticket Notifications', function (): void {
    it('sends admin notification when customer creates ticket via web', function (): void {
        Notification::fake();

        $customer = User::factory()->customer()->create();
        $admin = User::factory()->admin()->create();

        $this->actingAs($customer)
            ->post($this->accountUrl.'/tickets', [
                'subject' => 'Help with billing',
                'department' => 'billing',
                'priority' => 'medium',
                'message' => 'I need help understanding my invoice details.',
            ])
            ->assertRedirect();

        Notification::assertSentTo($admin, TicketCreatedNotification::class);
    });

    it('sends customer notification when staff replies', function (): void {
        Notification::fake();

        $admin = User::factory()->admin()->create();
        $customer = User::factory()->customer()->create();
        $ticket = SupportTicket::factory()->open()->create(['user_id' => $customer->id]);

        $this->actingAs($admin)
            ->post($this->adminUrl.'/tickets/'.$ticket->id.'/reply', [
                'body' => 'We are looking into this right now.',
            ])
            ->assertRedirect();

        Notification::assertSentTo($customer, TicketStaffReplyNotification::class);
    });

    it('sends customer notification when status changes', function (): void {
        Notification::fake();

        $admin = User::factory()->admin()->create();
        $customer = User::factory()->customer()->create();
        $ticket = SupportTicket::factory()->open()->create(['user_id' => $customer->id]);

        $this->actingAs($admin)
            ->put($this->adminUrl.'/tickets/'.$ticket->id.'/status', [
                'status' => 'in_progress',
            ])
            ->assertRedirect();

        Notification::assertSentTo($customer, TicketStatusChangedNotification::class);
    });

    it('sends admin notification when customer replies', function (): void {
        Notification::fake();

        $customer = User::factory()->customer()->create();
        $admin = User::factory()->admin()->create();
        $ticket = SupportTicket::factory()->open()->create(['user_id' => $customer->id]);

        $this->actingAs($customer)
            ->post($this->accountUrl.'/tickets/'.$ticket->id.'/reply', [
                'body' => 'I have more details about my issue.',
            ])
            ->assertRedirect();

        Notification::assertSentTo($admin, TicketCustomerReplyNotification::class);
    });
});
