<?php

namespace App\Tests\Unit\Service\Dolibarr;

use App\Entity\Organization\Organization;
use App\Service\Dolibarr\DolibarrApiService;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;

class DolibarrApiServiceTest extends TestCase
{
    private MockObject|HttpClientInterface $client;

    public function setUp(): void
    {
        $this->client = $this->getMockBuilder(HttpClientInterface::class)
            ->disableOriginalConstructor()
            ->getMock();
    }

    /**
     * @see DolibarrApiService::getSociety()
     */
    public function testGetSociety(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getSociety'])
            ->getMock();

        $organizationId = 123;

        $dolibarrApiService
            ->expects(self::once())
            ->method('getJsonContent')
            ->with('thirdparties', ['limit' => '1', 'sqlfilters' => '(ef.2iopen_organization_id:=:'.$organizationId.')'])
            ->willReturn([['id' => 1]]); // dummy non-empty data

        $society = $dolibarrApiService->getSociety($organizationId);

        $this->assertEquals(['id' => 1], $society);
    }

    /**
     * @see DolibarrApiService::getSociety()
     */
    public function testGetSocietyNotExisting(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getSociety'])
            ->getMock();

        $organizationId = 123;

        $dolibarrApiService
            ->expects(self::once())
            ->method('getJsonContent')
            ->with('thirdparties', ['limit' => '1', 'sqlfilters' => '(ef.2iopen_organization_id:=:'.$organizationId.')'])
            ->willThrowException(new HttpException(404));

        $society = $dolibarrApiService->getSociety($organizationId);

        $this->assertEquals(null, $society);
    }

    /**
     * @see DolibarrApiService::getSociety()
     */
    public function testGetSocietyWithError(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getSociety'])
            ->getMock();

        $organizationId = 123;

        $e = new HttpException(500);

        $dolibarrApiService
            ->expects(self::once())
            ->method('getJsonContent')
            ->with('thirdparties', ['limit' => '1', 'sqlfilters' => '(ef.2iopen_organization_id:=:'.$organizationId.')'])
            ->willThrowException($e);

        $this->expectException($e::class);

        $dolibarrApiService->getSociety($organizationId);
    }

    /**
     * @see DolibarrApiService::getActiveContract()
     */
    public function testGetActiveContract(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getActiveContract'])
            ->getMock();

        $socId = 1;

        $dolibarrApiService
            ->expects(self::once())
            ->method('getJsonContent')
            ->with('contracts', ['limit' => '1', 'sqlfilters' => 'statut:=:1', 'thirdparty_ids' => $socId])
            ->willReturn([['id' => 1]]); // dummy non-empty data

        $this->assertEquals(['id' => 1], $dolibarrApiService->getActiveContract($socId));
    }

    /**
     * @see DolibarrApiService::getActiveContract()
     */
    public function testGetActiveContractMissing(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getActiveContract'])
            ->getMock();

        $socId = 1;

        $dolibarrApiService
            ->expects(self::once())
            ->method('getJsonContent')
            ->with('contracts', ['limit' => '1', 'sqlfilters' => 'statut:=:1', 'thirdparty_ids' => $socId])
            ->willThrowException(new HttpException(404));

        $this->assertEquals(null, $dolibarrApiService->getActiveContract($socId));
    }

    /**
     * @see DolibarrApiService::getActiveContract()
     */
    public function testGetActiveContractError(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getActiveContract'])
            ->getMock();

        $socId = 1;

        $dolibarrApiService
            ->expects(self::once())
            ->method('getJsonContent')
            ->with('contracts', ['limit' => '1', 'sqlfilters' => 'statut:=:1', 'thirdparty_ids' => $socId])
            ->willThrowException(new HttpException(500));

        $this->expectException(HttpException::class);

        $dolibarrApiService->getActiveContract($socId);
    }

    /**
     * @see DolibarrApiService::getBills()
     */
    public function testGetBills(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getBills'])
            ->getMock();

        $socId = 1;

        $dolibarrApiService
            ->expects(self::once())
            ->method('getJsonContent')
            ->with('invoices', ['sortfield' => 'datef', 'sortorder' => 'DESC', 'limit' => 5, 'sqlfilters' => "(fk_soc:=:$socId) and (fk_statut:!=:0)"])
            ->willReturn([['id' => 10], ['id' => 20]]);

        $this->assertEquals([['id' => 10], ['id' => 20]], $dolibarrApiService->getBills($socId));
    }

    /**
     * @see DolibarrApiService::getBills()
     */
    public function testGetBillsMissing(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getBills'])
            ->getMock();

        $socId = 1;

        $dolibarrApiService
            ->expects(self::once())
            ->method('getJsonContent')
            ->with('invoices', ['sortfield' => 'datef', 'sortorder' => 'DESC', 'limit' => 5, 'sqlfilters' => "(fk_soc:=:$socId) and (fk_statut:!=:0)"])
            ->willThrowException(new HttpException(404));

        $this->assertEquals([], $dolibarrApiService->getBills($socId));
    }

    /**
     * @see DolibarrApiService::getBills()
     */
    public function testGetBillsError(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getBills'])
            ->getMock();

        $socId = 1;

        $dolibarrApiService
            ->expects(self::once())
            ->method('getJsonContent')
            ->with('invoices', ['sortfield' => 'datef', 'sortorder' => 'DESC', 'limit' => 5, 'sqlfilters' => "(fk_soc:=:$socId) and (fk_statut:!=:0)"])
            ->willThrowException(new HttpException(500));

        $this->expectException(HttpException::class);

        $dolibarrApiService->getBills($socId);
    }

    /**
     * @see DolibarrApiService::getAllClients()
     */
    public function testGetAllClients(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getAllClients'])
            ->getMock();

        $dolibarrApiService
            ->expects(self::once())
            ->method('getJsonContent')
            ->with('thirdparties', ['limit' => '1000000', 'sqlfilters' => 'client:=:1'])
            ->willReturn([['id' => 10], ['id' => 20]]);

        $this->assertEquals([['id' => 10], ['id' => 20]], $dolibarrApiService->getAllClients());
    }

    /**
     * @see DolibarrApiService::getContacts()
     */
    public function testGetContacts(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getContacts'])
            ->getMock();

        $socId = 1;

        $dolibarrApiService
            ->expects(self::once())
            ->method('getJsonContent')
            ->with('contacts', ['limit' => 1000, 'thirdparty_ids' => $socId])
            ->willReturn([['id' => 10], ['id' => 20]]);

        $this->assertEquals([['id' => 10], ['id' => 20]], $dolibarrApiService->getContacts($socId));
    }

    /**
     * @see DolibarrApiService::getContacts()
     */
    public function testGetContactsMissing(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getContacts'])
            ->getMock();

        $socId = 1;

        $dolibarrApiService
            ->expects(self::once())
            ->method('getJsonContent')
            ->with('contacts', ['limit' => 1000, 'thirdparty_ids' => $socId])
            ->willThrowException(new HttpException(404));

        $this->assertEquals([], $dolibarrApiService->getContacts($socId));
    }

    /**
     * @see DolibarrApiService::getContacts()
     */
    public function testGetContactsError(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getContacts'])
            ->getMock();

        $socId = 1;

        $dolibarrApiService
            ->expects(self::once())
            ->method('getJsonContent')
            ->with('contacts', ['limit' => 1000, 'thirdparty_ids' => $socId])
            ->willThrowException(new HttpException(500));

        $this->expectException(HttpException::class);

        $dolibarrApiService->getContacts($socId);
    }

    /**
     * @see DolibarrApiService::getActiveOpentalentContacts()
     */
    public function testGetActiveOpentalentContacts(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getActiveOpentalentContacts'])
            ->getMock();

        $socId = 1;

        $dolibarrApiService
            ->expects(self::once())
            ->method('getJsonContent')
            ->with('contacts?limit=1000&t.statut=1&thirdparty_ids='.$socId.'&sqlfilters:=:(te.2iopen_person_id%3A%3E%3A0)')
            ->willReturn([['id' => 10], ['id' => 20]]);

        $this->assertEquals([['id' => 10], ['id' => 20]], $dolibarrApiService->getActiveOpentalentContacts($socId));
    }

    /**
     * @see DolibarrApiService::getActiveOpentalentContacts()
     */
    public function testGetActiveOpentalentContactsMissing(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getActiveOpentalentContacts'])
            ->getMock();

        $socId = 1;

        $dolibarrApiService
            ->expects(self::once())
            ->method('getJsonContent')
            ->with('contacts?limit=1000&t.statut=1&thirdparty_ids='.$socId.'&sqlfilters:=:(te.2iopen_person_id%3A%3E%3A0)')
            ->willThrowException(new HttpException(404));

        $this->assertEquals([], $dolibarrApiService->getActiveOpentalentContacts($socId));
    }

    /**
     * @see DolibarrApiService::getActiveOpentalentContacts()
     */
    public function testGetActiveOpentalentContactsError(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getActiveOpentalentContacts'])
            ->getMock();

        $socId = 1;

        $dolibarrApiService
            ->expects(self::once())
            ->method('getJsonContent')
            ->with('contacts?limit=1000&t.statut=1&thirdparty_ids='.$socId.'&sqlfilters:=:(te.2iopen_person_id%3A%3E%3A0)')
            ->willThrowException(new HttpException(500));

        $this->expectException(HttpException::class);

        $dolibarrApiService->getActiveOpentalentContacts($socId);
    }

    /**
     * @see DolibarrApiService::getSocietyTagsIds()
     */
    public function testGetSocietyTagsIds(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getSocietyTagsIds'])
            ->getMock();

        $socId = 1;

        $dolibarrApiService
            ->expects(self::once())
            ->method('getJsonContent')
            ->with('/thirdparties/1/categories')
            ->willReturn([['id' => '10'], ['id' => '20']]);

        $this->assertEquals(
            [10, 20],
            $dolibarrApiService->getSocietyTagsIds($socId)
        );
    }

    /**
     * @see DolibarrApiService::getSocietyTagsIds()
     */
    public function testGetSocietyTagsIdsMissing(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getSocietyTagsIds'])
            ->getMock();

        $socId = 1;

        $dolibarrApiService
            ->expects(self::once())
            ->method('getJsonContent')
            ->with('/thirdparties/1/categories')
            ->willThrowException(new HttpException(404));

        $this->assertEquals(
            [],
            $dolibarrApiService->getSocietyTagsIds($socId)
        );
    }

    /**
     * @see DolibarrApiService::getSocietyTagsIds()
     */
    public function testGetSocietyTagsIdsError(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getSocietyTagsIds'])
            ->getMock();

        $socId = 1;

        $dolibarrApiService
            ->expects(self::once())
            ->method('getJsonContent')
            ->with('/thirdparties/1/categories')
            ->willThrowException(new HttpException(500));

        $this->expectException(HttpException::class);

        $dolibarrApiService->getSocietyTagsIds($socId);
    }

    /**
     * @see DolibarrApiService::createSociety
     */
    public function testCreateSociety(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['createSociety'])
            ->getMock();

        $organization = $this->getMockBuilder(Organization::class)->getMock();
        $organization->method('getId')->willReturn(123);
        $organization->method('getName')->willReturn('Foo');

        $expectedPostBody = [
            'name' => 'Foo',
            'client' => 2,
            'code_client' => -1,
            'import_key' => 'crm',
            'array_options' => ['options_2iopen_organization_id' => 123],
        ];

        $response = $this->getMockBuilder(ResponseInterface::class)->getMock();
        $response->method('getContent')->willReturn('456');

        $dolibarrApiService
            ->expects(self::once())
            ->method('post')
            ->with('/thirdparties', $expectedPostBody)
            ->willReturn($response);

        $result = $dolibarrApiService->createSociety($organization);

        $this->assertEquals(456, $result);
    }

    /**
     * @see DolibarrApiService::createSociety
     */
    public function testCreateSocietyIsClient(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['createSociety'])
            ->getMock();

        $organization = $this->getMockBuilder(Organization::class)->getMock();
        $organization->method('getId')->willReturn(123);
        $organization->method('getName')->willReturn('Foo');

        $expectedPostBody = [
            'name' => 'Foo',
            'client' => 1,
            'code_client' => -1,
            'import_key' => 'crm',
            'array_options' => ['options_2iopen_organization_id' => 123],
        ];

        $response = $this->getMockBuilder(ResponseInterface::class)->getMock();
        $response->method('getContent')->willReturn('456');

        $dolibarrApiService
            ->expects(self::once())
            ->method('post')
            ->with('/thirdparties', $expectedPostBody)
            ->willReturn($response);

        $result = $dolibarrApiService->createSociety($organization, true);

        $this->assertEquals(456, $result);
    }

    public function testSwitchSocietyToProspect(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['switchSocietyToProspect'])
            ->getMock();

        $response = $this->getMockBuilder(ResponseInterface::class)->getMock();
        $response->method('getStatusCode')->willReturn(200);

        $dolibarrApiService
            ->method('getSociety')
            ->with(123)
            ->willReturn(['id' => 456]);

        $dolibarrApiService
            ->expects(self::once())
            ->method('put')
            ->with('thirdparties/456', ['client' => 2])
            ->willReturn($response);

        $dolibarrApiService->switchSocietyToProspect(123);
    }

    public function testSwitchSocietyToProspectWithError(): void
    {
        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['switchSocietyToProspect'])
            ->getMock();

        $response = $this->getMockBuilder(ResponseInterface::class)->getMock();
        $response->method('getStatusCode')->willReturn(500);

        $dolibarrApiService
            ->method('getSociety')
            ->with(123)
            ->willReturn(['id' => 456]);

        $dolibarrApiService
            ->expects(self::once())
            ->method('put')
            ->with('thirdparties/456', ['client' => 2])
            ->willReturn($response);

        $this->expectException(HttpException::class);
        $this->expectExceptionMessage('Error while updating the society in Dolibarr');

        $dolibarrApiService->switchSocietyToProspect(123);
    }
}
