<?php

namespace MintyPHP\Tests\Module\Helpdesk\Service;

use MintyPHP\Module\Helpdesk\Service\BcODataGateway;
use MintyPHP\Module\Helpdesk\Service\BcSoapGateway;
use MintyPHP\Module\Helpdesk\Service\TicketCommunicationService;
use PHPUnit\Framework\TestCase;

class TicketCommunicationServiceTest extends TestCase
{
    public function testLoadTicketCommunicationFeedMergesSoapAndOData(): void
    {
        $soapGateway = $this->createMock(BcSoapGateway::class);
        $soapGateway->method('getTicketCommunicationText')->willReturn([
            'ok' => true,
            'soap_used' => true,
            'return_value' => 0,
            'communication_text' => "2026-04-03 08:10 - Alice: Hallo\n2026-04-03 08:11 - Alice: Update",
            'message_entries' => '',
        ]);

        $odataGateway = $this->createMock(BcODataGateway::class);
        $odataGateway->expects($this->any())->method('getTicketLog')->with('T26-1216')->willReturn([
            [
                'Entry_No' => 1,
                'Created_By' => 'ICTEST',
                'Creation_Date' => '2026-04-03',
                'Creation_Time' => '07:59:00.000',
                'Type' => 'Status',
                'State_Subtype' => 'Offen',
                'Record_ID' => 'ICI Support Ticket: T26-1216',
            ],
        ]);

        $service = new TicketCommunicationService($soapGateway, $odataGateway);
        $result = $service->loadTicketCommunicationFeed('T26-1216', 40);

        $this->assertNotEmpty($result['entries']);
        $this->assertGreaterThanOrEqual(3, count($result['entries']));
        $this->assertSame(true, $result['meta']['soap_used']);
        $this->assertSame(false, $result['meta']['fallback_used']);

        $sources = array_unique(array_map(static fn (array $entry): string => (string) ($entry['source'] ?? ''), $result['entries']));
        $this->assertContains('soap', $sources);
        $this->assertContains('odata', $sources);
    }

    public function testLoadTicketCommunicationFeedFallsBackToODataWhenSoapFails(): void
    {
        $soapGateway = $this->createMock(BcSoapGateway::class);
        $soapGateway->method('getTicketCommunicationText')->willReturn([
            'ok' => false,
            'soap_used' => false,
            'return_value' => 400,
            'communication_text' => '',
            'message_entries' => '',
            'error' => 'BC SOAP returned error code 400',
        ]);

        $odataGateway = $this->createMock(BcODataGateway::class);
        $odataGateway->expects($this->any())->method('getTicketLog')->with('T26-1216')->willReturn([
            [
                'Entry_No' => 1,
                'Created_By' => 'ICTEST',
                'Creation_Date' => '2026-04-03',
                'Creation_Time' => '07:59:00.000',
                'Type' => 'Nachricht',
                'State_Subtype' => '',
                'Record_ID' => 'Fallback entry',
            ],
        ]);

        $service = new TicketCommunicationService($soapGateway, $odataGateway);
        $result = $service->loadTicketCommunicationFeed('T26-1216', 40);

        $this->assertTrue($result['ok']);
        $this->assertNotEmpty($result['entries']);
        $this->assertSame(false, $result['meta']['soap_used']);
        $this->assertSame(true, $result['meta']['fallback_used']);
        $this->assertSame('BC SOAP returned error code 400', $result['meta']['soap_error']);
        $this->assertSame('odata', $result['entries'][0]['source']);
    }

    public function testLoadDebitorCommunicationFeedUsesLastTenTickets(): void
    {
        $soapGateway = $this->createMock(BcSoapGateway::class);
        $soapGateway->method('getTicketCommunicationText')->willReturn([
            'ok' => false,
            'soap_used' => false,
            'return_value' => 400,
            'communication_text' => '',
            'message_entries' => '',
            'error' => 'SOAP unavailable',
        ]);

        $tickets = [];
        for ($i = 1; $i <= 12; $i++) {
            $tickets[] = [
                'No' => 'T' . $i,
                'Last_Activity_Date' => sprintf('2026-04-%02dT08:00:00Z', $i),
                'Created_On' => sprintf('2026-03-%02dT08:00:00Z', $i),
            ];
        }

        $odataGateway = $this->createMock(BcODataGateway::class);
        $odataGateway->expects($this->any())->method('getTicketsForCustomer')->with('10001', 'Test GmbH')->willReturn($tickets);
        $odataGateway->expects($this->any())->method('getContactsForCustomer')->with('', 'Test GmbH')->willReturn([]);
        $odataGateway->method('getTicketLog')->willReturnCallback(static function (string $ticketNo): array {
            return [[
                'Entry_No' => 1,
                'Created_By' => 'ICTEST',
                'Creation_Date' => '2026-04-03',
                'Creation_Time' => '07:59:00.000',
                'Type' => 'Nachricht',
                'State_Subtype' => '',
                'Record_ID' => 'Entry ' . $ticketNo,
            ]];
        });

        $service = new TicketCommunicationService($soapGateway, $odataGateway);
        $result = $service->loadDebitorCommunicationFeed('10001', 'Test GmbH', 10, 40, 250);

        $this->assertTrue($result['ok']);
        $this->assertSame(10, $result['meta']['tickets_considered']);
        $this->assertSame(10, $result['meta']['tickets_with_data']);
        $this->assertSame(10, $result['meta']['soap_partial_failures']);

        $ticketNos = array_unique(array_map(static fn (array $entry): string => (string) ($entry['ticket_no'] ?? ''), $result['entries']));
        $this->assertCount(10, $ticketNos);
        $this->assertNotContains('T1', $ticketNos);
        $this->assertNotContains('T2', $ticketNos);
        $this->assertContains('T12', $ticketNos);
    }

    public function testLoadDebitorCommunicationFeedResolvesContactActorsOncePerDebitor(): void
    {
        $soapGateway = $this->createMock(BcSoapGateway::class);
        $soapGateway->method('getTicketCommunicationText')->willReturn([
            'ok' => false,
            'soap_used' => false,
            'return_value' => 400,
            'communication_text' => '',
            'message_entries' => '',
            'error' => 'SOAP unavailable',
        ]);

        $odataGateway = $this->createMock(BcODataGateway::class);
        $odataGateway->expects($this->any())->method('getTicketsForCustomer')->with('10001', 'Test GmbH')->willReturn([
            ['No' => 'T1', 'Last_Activity_Date' => '2026-04-10T08:00:00Z', 'Created_On' => '2026-04-09T08:00:00Z'],
            ['No' => 'T2', 'Last_Activity_Date' => '2026-04-09T08:00:00Z', 'Created_On' => '2026-04-08T08:00:00Z'],
        ]);
        $odataGateway->expects($this->once())
            ->method('getContactsForCustomer')
            ->with('', 'Test GmbH')
            ->willReturn([
                ['No' => 'KT002967', 'Name' => 'Felix Schneider'],
            ]);
        $odataGateway->expects($this->never())
            ->method('getTicket');
        $odataGateway->method('getTicketLog')->willReturn([
            [
                'Entry_No' => 1,
                'Created_By' => 'KT002967',
                'Created_By_Type' => 'Contact',
                'Creation_Date' => '2026-04-03',
                'Creation_Time' => '07:59:00.000',
                'Type' => 'Nachricht',
                'State_Subtype' => '',
                'Record_ID' => 'Hallo',
            ],
        ]);

        $service = new TicketCommunicationService($soapGateway, $odataGateway);
        $result = $service->loadDebitorCommunicationFeed('10001', 'Test GmbH', 2, 40, 250);

        $this->assertTrue($result['ok']);
        $this->assertNotEmpty($result['entries']);
        $actors = array_unique(array_map(static fn (array $entry): string => (string) ($entry['actor'] ?? ''), $result['entries']));
        $this->assertSame(['Felix Schneider'], $actors);
    }

    public function testLoadTicketCommunicationFeedRespectsEntryLimit(): void
    {
        $soapGateway = $this->createMock(BcSoapGateway::class);
        $soapGateway->method('getTicketCommunicationText')->willReturn([
            'ok' => true,
            'soap_used' => true,
            'return_value' => 0,
            'communication_text' => "2026-04-03 08:10 - Alice: A\n2026-04-03 08:11 - Alice: B\n2026-04-03 08:12 - Alice: C",
            'message_entries' => '',
        ]);

        $odataGateway = $this->createMock(BcODataGateway::class);
        $odataGateway->expects($this->any())->method('getTicketLog')->with('T26-1216')->willReturn([]);

        $service = new TicketCommunicationService($soapGateway, $odataGateway);
        $result = $service->loadTicketCommunicationFeed('T26-1216', 2);

        $this->assertTrue($result['ok']);
        $this->assertCount(2, $result['entries']);
    }

    public function testLoadTicketCommunicationFeedMapsSoapDataBlobByEntryNo(): void
    {
        $soapGateway = $this->createMock(BcSoapGateway::class);
        $soapGateway->method('getTicketCommunicationText')->willReturn([
            'ok' => true,
            'soap_used' => true,
            'return_value' => 100,
            'communication_text' => '[{"EntryNo":"19356","DataBlob":"<p>Hallo <strong>Team</strong></p>"}]',
            'message_entries' => '',
        ]);

        $odataGateway = $this->createMock(BcODataGateway::class);
        $odataGateway->method('getTicketLog')->willReturn([
            [
                'Entry_No' => 19356,
                'Created_By' => 'KT002452',
                'Creation_Date' => '2026-04-02',
                'Creation_Time' => '13:54:00.000',
                'Type' => 'Nachricht',
                'State_Subtype' => '',
                'Record_ID' => '',
            ],
        ]);

        $service = new TicketCommunicationService($soapGateway, $odataGateway);
        $result = $service->loadTicketCommunicationFeed('T26-1204', 40);

        $this->assertTrue($result['ok']);
        $this->assertNotEmpty($result['entries']);
        $this->assertSame(false, $result['meta']['fallback_used']);
        $this->assertSame('soap', $result['entries'][0]['source']);
        $this->assertSame('Hallo Team', $result['entries'][0]['message']);
    }

    public function testLoadTicketCommunicationFeedResolvesContactCodeToName(): void
    {
        $soapGateway = $this->createMock(BcSoapGateway::class);
        $soapGateway->method('getTicketCommunicationText')->willReturn([
            'ok' => false,
            'soap_used' => false,
            'return_value' => 400,
            'communication_text' => '',
            'message_entries' => '',
            'error' => 'BC SOAP returned error code 400',
        ]);

        $odataGateway = $this->createMock(BcODataGateway::class);
        $odataGateway->expects($this->any())->method('getTicketLog')->with('T26-1204')->willReturn([
            [
                'Entry_No' => 19356,
                'Created_By' => 'KT002967',
                'Creation_Date' => '2026-04-02',
                'Creation_Time' => '13:54:00.000',
                'Type' => 'Nachricht',
                'State_Subtype' => '',
                'Record_ID' => 'Hallo',
            ],
        ]);
        $odataGateway->expects($this->any())->method('getTicket')->with('T26-1204')->willReturn([
            'No' => 'T26-1204',
            'Company_Contact_Name' => 'Aero-Dienst GmbH',
            'Current_Contact_Name' => 'Fallback Kontakt',
        ]);
        $odataGateway->expects($this->any())->method('getContactsForCustomer')->with('', 'Aero-Dienst GmbH')->willReturn([
            [
                'No' => 'KT002967',
                'Name' => 'Felix Schneider',
            ],
        ]);

        $service = new TicketCommunicationService($soapGateway, $odataGateway);
        $result = $service->loadTicketCommunicationFeed('T26-1204', 40);

        $this->assertTrue($result['ok']);
        $this->assertNotEmpty($result['entries']);
        $this->assertSame('Felix Schneider', $result['entries'][0]['actor']);
    }

    public function testFileTypeEntriesIncludeFileMetadata(): void
    {
        $soapGateway = $this->createMock(BcSoapGateway::class);
        $soapGateway->method('getTicketCommunicationText')->willReturn([
            'ok' => false,
            'soap_used' => false,
            'return_value' => 0,
            'communication_text' => '',
            'message_entries' => '',
        ]);

        $odataGateway = $this->createMock(BcODataGateway::class);
        $odataGateway->method('getTicketLog')->willReturn([
            [
                'Entry_No' => 42,
                'Created_By' => 'NKS',
                'Created_By_Type' => 'Benutzer',
                'Creation_Date' => '2026-04-03',
                'Creation_Time' => '10:00:00.000',
                'Type' => 'Datei',
                'State_Subtype' => '',
                'Record_ID' => 'screenshot.png',
            ],
            [
                'Entry_No' => 41,
                'Created_By' => 'NKS',
                'Created_By_Type' => 'Benutzer',
                'Creation_Date' => '2026-04-03',
                'Creation_Time' => '09:55:00.000',
                'Type' => 'Status',
                'State_Subtype' => 'Offen',
                'Record_ID' => 'ICI Support Ticket: T26-1216',
            ],
        ]);

        $service = new TicketCommunicationService($soapGateway, $odataGateway);
        $result = $service->loadTicketCommunicationFeed('T26-1216', 40);

        $this->assertTrue($result['ok']);
        $entries = $result['entries'];

        // Find the file entry
        $fileEntries = array_values(array_filter($entries, static fn (array $e) => ($e['type_variant'] ?? '') === 'file'));
        $this->assertNotEmpty($fileEntries, 'Should have at least one file entry');

        $fileEntry = $fileEntries[0];
        $this->assertSame('42', $fileEntry['file_entry_no']);
        $this->assertSame('screenshot.png', $fileEntry['file_name']);

        // Non-file entries should have empty file fields
        $statusEntries = array_values(array_filter($entries, static fn (array $e) => ($e['type_variant'] ?? '') === 'status'));
        if ($statusEntries !== []) {
            $this->assertSame('', $statusEntries[0]['file_entry_no']);
            $this->assertSame('', $statusEntries[0]['file_name']);
        }
    }
}
