<?php

namespace MintyPHP\Service\Import\Profile;

use MintyPHP\Service\Org\DepartmentService;

class DepartmentImportProfile implements ImportProfileInterface
{
    public function __construct(
        private readonly DepartmentImportGateway $gateway,
        private readonly DepartmentService $departmentService
    ) {
    }

    public function key(): string
    {
        return 'departments';
    }

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

    public function allowedTargets(): array
    {
        return [
            'description' => t('Description'),
            'tenant' => t('Tenant'),
            'code' => t('Code'),
            'cost_center' => t('Cost center'),
            'active' => t('Active'),
        ];
    }

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

    public function autoMapAliases(): array
    {
        return [
            'description' => ['description', 'name', 'department', 'abteilung'],
            'tenant' => ['tenant', 'tenant_uuid', 'mandant', 'mandant_uuid'],
            'code' => ['code', 'department_code', 'abteilungscode'],
            'cost_center' => ['cost_center', 'kostenstelle'],
            'active' => ['active', 'status', 'aktiv'],
        ];
    }

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

    public function requiresAssignmentPermission(): bool
    {
        return false;
    }

    public function validateMappedRow(array $mapped, array $context): array
    {
        $errors = [];
        $normalized = [];

        $normalized['description'] = trim((string) ($mapped['description'] ?? ''));
        if ($normalized['description'] === '') {
            $errors[] = ['code' => 'mapping_required_missing', 'message' => t('Required field is empty: description')];
        }

        $normalized['code'] = trim((string) ($mapped['code'] ?? ''));
        $normalized['cost_center'] = trim((string) ($mapped['cost_center'] ?? ''));

        $activeRaw = trim((string) ($mapped['active'] ?? ''));
        $active = $this->parseActive($activeRaw);
        if ($activeRaw !== '' && $active === null) {
            $errors[] = ['code' => 'invalid_active'];
        }
        $normalized['active'] = $active ?? 1;

        $tenantRaw = trim((string) ($mapped['tenant'] ?? ''));
        $tenant = null;
        if ($tenantRaw !== '') {
            if (!$this->isUuid($tenantRaw)) {
                $errors[] = ['code' => 'invalid_tenant_uuid'];
            } else {
                $tenant = $this->gateway->findTenantByUuid($tenantRaw);
                if (!$tenant || (string) ($tenant['status'] ?? '') !== 'active') {
                    $errors[] = ['code' => 'tenant_not_found'];
                }
            }
        }

        if (!$tenant) {
            $defaultTenantId = (int) ($context['default_tenant_id'] ?? 0);
            if ($defaultTenantId > 0) {
                $defaultTenant = $this->gateway->findTenantById($defaultTenantId);
                if ($defaultTenant && (string) ($defaultTenant['status'] ?? '') === 'active') {
                    $tenant = $defaultTenant;
                }
            }
        }

        $tenantId = (int) ($tenant['id'] ?? 0);
        if ($tenantId <= 0) {
            $errors[] = ['code' => 'tenant_not_found'];
        }

        if ($tenantId > 0 && empty($context['is_global_admin'])) {
            $allowedTenantIds = $context['allowed_tenant_ids'] ?? [];
            if (!in_array($tenantId, $allowedTenantIds, true)) {
                $errors[] = ['code' => 'assignment_out_of_scope'];
            }
        }

        $normalized['tenant_id'] = $tenantId;

        return [
            'ok' => !$errors,
            'normalized' => $normalized,
            'errors' => $errors,
        ];
    }

    public function dryRunRow(array $normalized, array $context): array
    {
        if ($this->codeExists($normalized)) {
            return ['status' => 'skipped', 'code' => 'code_exists'];
        }
        if ($this->departmentExists($normalized)) {
            return ['status' => 'skipped', 'code' => 'department_exists'];
        }
        return ['status' => 'would_create'];
    }

    public function commitRow(array $normalized, array $context): array
    {
        if ($this->codeExists($normalized)) {
            return ['status' => 'skipped', 'code' => 'code_exists'];
        }
        if ($this->departmentExists($normalized)) {
            return ['status' => 'skipped', 'code' => 'department_exists'];
        }

        $result = $this->departmentService->createFromAdmin([
            'description' => (string) ($normalized['description'] ?? ''),
            'tenant_id' => (int) ($normalized['tenant_id'] ?? 0),
            'code' => (string) ($normalized['code'] ?? ''),
            'cost_center' => (string) ($normalized['cost_center'] ?? ''),
            'active' => (int) ($normalized['active'] ?? 1),
        ], (int) ($context['current_user_id'] ?? 0));

        if (!($result['ok'] ?? false)) {
            $errors = $result['errors'] ?? [];
            $message = is_array($errors) && isset($errors[0]) ? (string) $errors[0] : t('Department can not be created');
            return ['status' => 'failed', 'code' => 'create_failed', 'message' => $message];
        }

        return [
            'status' => 'created',
            'uuid' => (string) ($result['uuid'] ?? ''),
        ];
    }

    public function inFileDuplicateKey(array $normalized): ?string
    {
        $code = $this->lower(trim((string) ($normalized['code'] ?? '')));
        if ($code !== '') {
            return 'code:' . $code;
        }

        $tenantId = (int) ($normalized['tenant_id'] ?? 0);
        $description = $this->lower(trim((string) ($normalized['description'] ?? '')));
        if ($tenantId <= 0 || $description === '') {
            return null;
        }

        return 'tenant:' . $tenantId . '|description:' . $description;
    }

    public function rowIdentifier(array $normalized): string
    {
        $code = trim((string) ($normalized['code'] ?? ''));
        if ($code !== '') {
            return $code;
        }
        return trim((string) ($normalized['description'] ?? ''));
    }

    private function codeExists(array $normalized): bool
    {
        $code = trim((string) ($normalized['code'] ?? ''));
        if ($code === '') {
            return false;
        }
        return $this->gateway->existsDepartmentCode($code);
    }

    private function departmentExists(array $normalized): bool
    {
        $tenantId = (int) ($normalized['tenant_id'] ?? 0);
        $description = trim((string) ($normalized['description'] ?? ''));
        if ($tenantId <= 0 || $description === '') {
            return false;
        }
        return $this->gateway->existsDepartmentByTenantAndDescription($tenantId, $description);
    }

    private function parseActive(string $value): ?int
    {
        $value = $this->lower(trim($value));
        if ($value === '') {
            return null;
        }

        if (in_array($value, ['1', 'true', 'yes', 'active', 'ja', 'aktiv'], true)) {
            return 1;
        }
        if (in_array($value, ['0', 'false', 'no', 'inactive', 'nein', 'inaktiv'], true)) {
            return 0;
        }
        return null;
    }

    private function isUuid(string $value): bool
    {
        return (bool) preg_match('/^[a-f0-9-]{36}$/i', $value);
    }

    private function lower(string $value): string
    {
        if (function_exists('mb_strtolower')) {
            return mb_strtolower($value, 'UTF-8');
        }
        return strtolower($value);
    }
}
