<?php

namespace MintyPHP\Tests\Service\Tenant;

use MintyPHP\Repository\Org\DepartmentRepositoryInterface;
use MintyPHP\Repository\Tenant\TenantRepositoryInterface;
use MintyPHP\Service\Audit\SystemAuditService;
use MintyPHP\Service\Directory\DirectorySettingsGateway;
use MintyPHP\Service\Tenant\TenantService;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

class TenantServiceTest extends TestCase
{
    private TenantRepositoryInterface&MockObject $tenantRepository;
    private DepartmentRepositoryInterface&MockObject $departmentRepository;
    private DirectorySettingsGateway&MockObject $settingsGateway;
    private SystemAuditService&MockObject $systemAuditService;
    private TenantService $service;

    protected function setUp(): void
    {
        $this->tenantRepository = $this->createMock(TenantRepositoryInterface::class);
        $this->departmentRepository = $this->createMock(DepartmentRepositoryInterface::class);
        $this->settingsGateway = $this->createMock(DirectorySettingsGateway::class);
        $this->systemAuditService = $this->createMock(SystemAuditService::class);

        $this->service = new TenantService(
            $this->tenantRepository,
            $this->departmentRepository,
            $this->settingsGateway,
            $this->systemAuditService
        );
    }

    public function testListDelegatesToRepository(): void
    {
        $expected = [
            ['id' => 1, 'description' => 'Tenant A'],
            ['id' => 2, 'description' => 'Tenant B'],
        ];

        $this->tenantRepository->expects($this->once())
            ->method('list')
            ->willReturn($expected);

        $result = $this->service->list();

        self::assertSame($expected, $result);
    }

    public function testCreateFromAdminSucceeds(): void
    {
        $input = $this->validInput();

        $this->tenantRepository->expects($this->once())
            ->method('create')
            ->willReturn(42);

        $this->tenantRepository->expects($this->once())
            ->method('find')
            ->with(42)
            ->willReturn(['id' => 42, 'uuid' => 'abc-uuid-123']);

        $this->systemAuditService->expects($this->once())
            ->method('record');

        $result = $this->service->createFromAdmin($input, 5);

        self::assertTrue($result['ok']);
        self::assertSame('abc-uuid-123', $result['uuid']);
        self::assertSame(42, $result['id']);
        self::assertSame('Test Tenant', $result['form']['description']);
    }

    public function testCreateFromAdminRejectsEmptyDescription(): void
    {
        $input = $this->validInput();
        $input['description'] = '';

        $this->tenantRepository->expects($this->never())
            ->method('create');

        $result = $this->service->createFromAdmin($input);

        self::assertFalse($result['ok']);
        self::assertNotEmpty($result['errors']);
    }

    public function testUpdateFromAdminSucceeds(): void
    {
        $input = $this->validInput();

        $this->tenantRepository->expects($this->once())
            ->method('find')
            ->with(10)
            ->willReturn(['id' => 10, 'uuid' => 'existing-uuid', 'status' => 'active']);

        $this->tenantRepository->expects($this->once())
            ->method('update')
            ->with(10, $this->isType('array'))
            ->willReturn(true);

        $this->systemAuditService->expects($this->once())
            ->method('record');

        $result = $this->service->updateFromAdmin(10, $input, 5);

        self::assertTrue($result['ok']);
        self::assertSame('Test Tenant', $result['form']['description']);
    }

    public function testDeleteByUuidSucceeds(): void
    {
        $tenant = ['id' => 7, 'uuid' => 'del-uuid-789'];

        $this->tenantRepository->expects($this->once())
            ->method('findByUuid')
            ->with('del-uuid-789')
            ->willReturn($tenant);

        $this->departmentRepository->expects($this->once())
            ->method('countByTenantId')
            ->with(7)
            ->willReturn(0);

        $this->tenantRepository->expects($this->once())
            ->method('delete')
            ->with(7)
            ->willReturn(true);

        $this->systemAuditService->expects($this->once())
            ->method('record');

        $result = $this->service->deleteByUuid('del-uuid-789');

        self::assertTrue($result['ok']);
        self::assertSame($tenant, $result['tenant']);
    }

    public function testDeleteByUuidBlocksWhenDepartmentsExist(): void
    {
        $tenant = ['id' => 7, 'uuid' => 'dep-uuid-789'];

        $this->tenantRepository->expects($this->once())
            ->method('findByUuid')
            ->with('dep-uuid-789')
            ->willReturn($tenant);

        $this->departmentRepository->expects($this->once())
            ->method('countByTenantId')
            ->with(7)
            ->willReturn(3);

        $this->tenantRepository->expects($this->never())
            ->method('delete');

        $result = $this->service->deleteByUuid('dep-uuid-789');

        self::assertFalse($result['ok']);
        self::assertSame(409, $result['status']);
        self::assertSame('tenant_has_departments', $result['error']);
    }

    public function testDeleteByUuidReturnsNotFoundForEmptyUuid(): void
    {
        $this->tenantRepository->expects($this->never())
            ->method('findByUuid');

        $result = $this->service->deleteByUuid('');

        self::assertFalse($result['ok']);
        self::assertSame(404, $result['status']);
        self::assertSame('not_found', $result['error']);
    }

    public function testDeleteByUuidReturnsNotFoundForMissingTenant(): void
    {
        $this->tenantRepository->expects($this->once())
            ->method('findByUuid')
            ->with('nonexistent-uuid')
            ->willReturn(null);

        $result = $this->service->deleteByUuid('nonexistent-uuid');

        self::assertFalse($result['ok']);
        self::assertSame(404, $result['status']);
        self::assertSame('not_found', $result['error']);
    }

    /**
     * @return array<string, mixed>
     */
    private function validInput(): array
    {
        return [
            'description' => 'Test Tenant',
            'status' => 'active',
            'address' => '',
            'postal_code' => '',
            'city' => '',
            'country' => '',
            'region' => '',
            'vat_id' => '',
            'tax_number' => '',
            'phone' => '',
            'fax' => '',
            'email' => '',
            'support_email' => '',
            'support_phone' => '',
            'billing_email' => '',
            'website' => '',
            'privacy_url' => '',
            'imprint_url' => '',
            'primary_color' => '',
            'primary_color_use_default' => true,
            'default_theme' => '',
            'allow_user_theme_mode' => '',
        ];
    }
}
