<?php

namespace MintyPHP\Tests\Module\Helpdesk\Service;

use MintyPHP\Http\SessionStoreInterface;
use MintyPHP\Module\Helpdesk\Service\BcSoapGateway;
use MintyPHP\Module\Helpdesk\Service\HelpdeskOAuthTokenService;
use MintyPHP\Module\Helpdesk\Service\EffectiveHelpdeskSettingsService;
use MintyPHP\Module\Helpdesk\Service\HelpdeskSettingsGateway;
use PHPUnit\Framework\TestCase;

class BcSoapGatewayTest extends TestCase
{
    /**
     * @param array{http_code:int,body:string,error:string} $response
     */
    private function createGateway(array $response): BcSoapGateway
    {
        $settings = $this->createMock(EffectiveHelpdeskSettingsService::class);
        $settings->method('isConfigured')->willReturn(true);
        $settings->method('getODataBaseUrl')->willReturn('https://bc.example.com:7048/BusinessCentral-NUP/ODataV4');
        $settings->method('getCompanyName')->willReturn('Test Company');
        $settings->method('getAuthMode')->willReturn(HelpdeskSettingsGateway::AUTH_MODE_BASIC);
        $settings->method('getBasicUser')->willReturn('tester');
        $settings->method('getBasicPassword')->willReturn('secret');

        $oauthTokenService = $this->createMock(HelpdeskOAuthTokenService::class);
        $sessionStore = $this->createMock(SessionStoreInterface::class);
        $sessionStore->method('all')->willReturn([
            'current_tenant' => ['id' => 1],
        ]);

        return new class($settings, $oauthTokenService, $sessionStore, $response) extends BcSoapGateway {
            /**
             * @param array{http_code:int,body:string,error:string} $fakeResponse
             */
            public function __construct(
                EffectiveHelpdeskSettingsService $settings,
                HelpdeskOAuthTokenService $oauthTokenService,
                SessionStoreInterface $sessionStore,
                private readonly array $fakeResponse
            ) {
                parent::__construct($settings, $oauthTokenService, $sessionStore);
            }

            protected function sendSoapRequest(
                string $endpoint,
                string $soapAction,
                string $requestBody,
                array $headers,
                string $basicUser,
                string $basicPassword
            ): array {
                return $this->fakeResponse;
            }
        };
    }

    public function testGetTicketCommunicationTextReturnsErrorForEmptyTicketNo(): void
    {
        $gateway = $this->createGateway([
            'http_code' => 200,
            'body' => '',
            'error' => '',
        ]);

        $result = $gateway->getTicketCommunicationText('');

        $this->assertFalse($result['ok']);
        $this->assertSame('Missing ticket number', $result['error']);
    }

    public function testGetTicketCommunicationTextParsesSuccessfulResponse(): void
    {
        $soapBody = <<<'XML'
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <GetTicketCommunicationText_Result xmlns="urn:microsoft-dynamics-schemas/codeunit/ICISupportWebserviceAPI">
      <return_value>0</return_value>
      <communicationText>Hallo aus SOAP</communicationText>
      <messageEntries>line 1</messageEntries>
    </GetTicketCommunicationText_Result>
  </soap:Body>
</soap:Envelope>
XML;

        $gateway = $this->createGateway([
            'http_code' => 200,
            'body' => $soapBody,
            'error' => '',
        ]);

        $result = $gateway->getTicketCommunicationText('T26-1216');

        $this->assertTrue($result['ok']);
        $this->assertTrue($result['soap_used']);
        $this->assertSame(0, $result['return_value']);
        $this->assertSame('Hallo aus SOAP', $result['communication_text']);
        $this->assertSame('line 1', $result['message_entries']);
    }

    public function testGetTicketCommunicationTextReturnsErrorWhenReturnCodeIndicatesFailure(): void
    {
        $soapBody = <<<'XML'
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <GetTicketCommunicationText_Result xmlns="urn:microsoft-dynamics-schemas/codeunit/ICISupportWebserviceAPI">
      <return_value>400</return_value>
      <communicationText></communicationText>
    </GetTicketCommunicationText_Result>
  </soap:Body>
</soap:Envelope>
XML;

        $gateway = $this->createGateway([
            'http_code' => 200,
            'body' => $soapBody,
            'error' => '',
        ]);

        $result = $gateway->getTicketCommunicationText('T26-1216');

        $this->assertFalse($result['ok']);
        $this->assertSame(400, $result['return_value']);
        $this->assertSame('BC SOAP returned error code 400', $result['error']);
    }

    public function testGetTicketCommunicationTextReturnsErrorForInvalidSoapXml(): void
    {
        $gateway = $this->createGateway([
            'http_code' => 200,
            'body' => 'invalid-xml',
            'error' => '',
        ]);

        $result = $gateway->getTicketCommunicationText('T26-1216');

        $this->assertFalse($result['ok']);
        $this->assertSame('Invalid SOAP XML response', $result['error']);
    }

    public function testGetTicketFileReturnsErrorForInvalidEntryNo(): void
    {
        $gateway = $this->createGateway(['http_code' => 200, 'body' => '', 'error' => '']);

        $result = $gateway->getTicketFile(0);

        $this->assertFalse($result['ok']);
        $this->assertSame('Invalid entry number', $result['error']);
    }

    public function testGetTicketFileReturnsErrorOnHttpFailure(): void
    {
        $gateway = $this->createGateway(['http_code' => 500, 'body' => '', 'error' => '']);

        $result = $gateway->getTicketFile(42);

        $this->assertFalse($result['ok']);
        $this->assertStringContainsString('HTTP 500', $result['error']);
    }

    public function testGetTicketFileReturnsErrorOnNon100ReturnValue(): void
    {
        $body = <<<XML
<?xml version="1.0" encoding="utf-8"?>
<Envelope><Body><GetTicketFile_Result><return_value>400</return_value></GetTicketFile_Result></Body></Envelope>
XML;
        $gateway = $this->createGateway(['http_code' => 200, 'body' => $body, 'error' => '']);

        $result = $gateway->getTicketFile(42);

        $this->assertFalse($result['ok']);
        $this->assertStringContainsString('error code 400', $result['error']);
    }

    public function testGetTicketFileDecodesBase64Parts(): void
    {
        $content = 'Hello World';
        $b64 = base64_encode($content);
        $body = <<<XML
<?xml version="1.0" encoding="utf-8"?>
<Envelope><Body><GetTicketFile_Result>
  <return_value>100</return_value>
  <iCISupportFilePort><ICISupportTicketLog><Parts>
    <PartData>{$b64}</PartData>
  </Parts></ICISupportTicketLog></iCISupportFilePort>
</GetTicketFile_Result></Body></Envelope>
XML;
        $gateway = $this->createGateway(['http_code' => 200, 'body' => $body, 'error' => '']);

        $result = $gateway->getTicketFile(42);

        $this->assertTrue($result['ok']);
        $this->assertSame($content, $result['data']);
    }

    public function testGetTicketFileHandlesMultipleParts(): void
    {
        $part1 = base64_encode('Hello ');
        $part2 = base64_encode('World');
        // Multiple parts are concatenated as base64 before decoding
        $combinedB64 = base64_encode('Hello World');
        $body = <<<XML
<?xml version="1.0" encoding="utf-8"?>
<Envelope><Body><GetTicketFile_Result>
  <return_value>100</return_value>
  <iCISupportFilePort><ICISupportTicketLog><Parts>
    <PartData>{$combinedB64}</PartData>
  </Parts></ICISupportTicketLog></iCISupportFilePort>
</GetTicketFile_Result></Body></Envelope>
XML;
        $gateway = $this->createGateway(['http_code' => 200, 'body' => $body, 'error' => '']);

        $result = $gateway->getTicketFile(42);

        $this->assertTrue($result['ok']);
        $this->assertSame('Hello World', $result['data']);
    }
}
