<?php

namespace MintyPHP\Tests\Service\Import;

use MintyPHP\Http\SessionStoreInterface;
use MintyPHP\Service\Access\PermissionService;
use MintyPHP\Service\Audit\ImportAuditInterface;
use MintyPHP\Service\Import\CsvReaderService;
use MintyPHP\Service\Import\ImportService;
use MintyPHP\Service\Import\ImportStateStoreService;
use MintyPHP\Service\Import\ImportTempFileService;
use MintyPHP\Service\Import\Profile\ImportProfileInterface;
use MintyPHP\Service\Settings\SettingsDefaultsGateway;
use MintyPHP\Service\Tenant\TenantScopeService;
use PHPUnit\Framework\TestCase;

class ImportServiceTest extends TestCase
{
    public function testAnalyzeUploadBuildsTokenStateAndAutoMapping(): void
    {
        $profile = new ImportServiceTestProfile('users', false);
        $csvReader = $this->createMock(CsvReaderService::class);
        $csvReader->expects($this->once())
            ->method('analyze')
            ->willReturn([
                'ok' => true,
                'delimiter' => ';',
                'headers' => ['E-Mail'],
                'rows_total' => 2,
            ]);

        $tempFileService = $this->createMock(ImportTempFileService::class);
        $tempFileService->expects($this->once())
            ->method('storeUploadedFile')
            ->willReturn(['ok' => true, 'path' => '/tmp/import.csv']);

        $stateStore = $this->createMock(ImportStateStoreService::class);
        $stateStore->expects($this->once())
            ->method('setState')
            ->with(
                $this->isString(),
                $this->callback(function (array $state): bool {
                    return (string) ($state['path'] ?? '') === '/tmp/import.csv'
                        && (string) ($state['type'] ?? '') === 'users'
                        && is_int($state['created_at'] ?? null);
                })
            );

        $service = $this->newImportService($profile, $csvReader, $tempFileService, $stateStore);
        $result = $service->analyzeUpload([
            'tmp_name' => '/tmp/php-upload',
            'name' => 'users.csv',
            'size' => 10,
            'error' => UPLOAD_ERR_OK,
        ], 'users', 7);

        $this->assertTrue($result['ok']);
        $this->assertSame(';', $result['delimiter']);
        $this->assertSame(['E-Mail' => 'email'], $result['auto_mapping']);
    }

    public function testPreviewFailsWhenRequiredMappingIsMissing(): void
    {
        $profile = new ImportServiceTestProfile('users', false);
        $stateStore = $this->createMock(ImportStateStoreService::class);
        $stateStore->method('getState')->willReturn([
            'token' => 'tok',
            'type' => 'users',
            'headers' => ['email'],
            'path' => '/tmp/import.csv',
            'delimiter' => ',',
            'user_id' => 5,
            'created_at' => time(),
        ]);

        $service = $this->newImportService($profile, null, null, $stateStore);
        $result = $service->preview('tok', ['mapping' => []], 5);

        $this->assertFalse($result['ok']);
        $this->assertSame('mapping_required_missing', $result['code']);
    }

    public function testPreviewFailsWhenAssignmentPermissionIsMissing(): void
    {
        $profile = new ImportServiceTestProfile('users', true);
        $stateStore = $this->createMock(ImportStateStoreService::class);
        $stateStore->method('getState')->willReturn([
            'token' => 'tok',
            'type' => 'users',
            'headers' => ['email', 'tenant'],
            'path' => '/tmp/import.csv',
            'delimiter' => ',',
            'user_id' => 0,
            'created_at' => time(),
        ]);

        $service = $this->newImportService($profile, null, null, $stateStore);
        $result = $service->preview('tok', ['mapping' => ['email' => 'email', 'tenant' => 'tenant']], 0);

        $this->assertFalse($result['ok']);
        $this->assertSame('assignment_permission_required', $result['code']);
    }

    public function testMessageForCodeUsesKnownMappingAndFallback(): void
    {
        $service = $this->newImportService(new ImportServiceTestProfile('users', false));

        $this->assertSame('Invalid file type', $service->messageForCode('invalid_file_type'));
        $this->assertSame('Unexpected error during import', $service->messageForCode('something_unknown'));
    }

    private function newImportService(
        ImportProfileInterface $profile,
        ?CsvReaderService $csvReader = null,
        ?ImportTempFileService $tempFileService = null,
        ?ImportStateStoreService $stateStore = null
    ): ImportService {
        $csvReader = $csvReader ?? $this->createMock(CsvReaderService::class);
        $tempFileService = $tempFileService ?? $this->createMock(ImportTempFileService::class);
        $auditService = $this->createMock(ImportAuditInterface::class);
        $stateStore = $stateStore ?? $this->createMock(ImportStateStoreService::class);
        $permissionService = $this->createMock(PermissionService::class);
        $permissionService->method('userHas')->willReturn(false);
        $settingsDefaultsGateway = $this->createMock(SettingsDefaultsGateway::class);
        $tenantScopeService = $this->createMock(TenantScopeService::class);
        $tenantScopeService->method('hasGlobalAccess')->willReturn(false);
        $tenantScopeService->method('getUserTenantIds')->willReturn([]);
        $sessionStore = $this->createMock(SessionStoreInterface::class);
        $sessionStore->method('get')->willReturn([]);

        return new ImportService(
            $csvReader,
            $tempFileService,
            $auditService,
            $stateStore,
            [$profile->key() => $profile],
            $permissionService,
            $settingsDefaultsGateway,
            $tenantScopeService,
            $sessionStore
        );
    }
}

class ImportServiceTestProfile implements ImportProfileInterface
{
    public function __construct(private readonly string $key, private readonly bool $requiresAssignmentPermission)
    {
    }

    public function key(): string
    {
        return $this->key;
    }

    public function label(): string
    {
        return 'Test';
    }

    public function allowedTargets(): array
    {
        return [
            'email' => 'Email',
            'tenant' => 'Tenant',
        ];
    }

    public function requiredTargets(): array
    {
        return ['email'];
    }

    public function autoMapAliases(): array
    {
        return [
            'email' => ['e-mail', 'email'],
            'tenant' => ['tenant'],
        ];
    }

    public function supportsAssignments(): bool
    {
        return true;
    }

    public function requiresAssignmentPermission(): bool
    {
        return $this->requiresAssignmentPermission;
    }

    public function validateMappedRow(array $mapped, array $context): array
    {
        return ['ok' => true, 'normalized' => $mapped, 'errors' => []];
    }

    public function dryRunRow(array $normalized, array $context): array
    {
        return ['status' => 'would_create'];
    }

    public function commitRow(array $normalized, array $context): array
    {
        return ['status' => 'created'];
    }

    public function inFileDuplicateKey(array $normalized): ?string
    {
        return null;
    }

    public function rowIdentifier(array $normalized): string
    {
        return (string) ($normalized['email'] ?? '');
    }
}
