<?php

namespace MintyPHP\Service\Org;

use MintyPHP\Repository\Org\DepartmentRepositoryInterface;
use MintyPHP\Service\Audit\AuditRecorderInterface;
use MintyPHP\Service\Directory\DirectorySettingsGateway;
use MintyPHP\Service\Tenant\TenantScopeService;
use MintyPHP\Service\User\UserServicesFactory;

class DepartmentService
{
    public function __construct(
        private readonly UserServicesFactory $userServicesFactory,
        private readonly DepartmentRepositoryInterface $departmentRepository,
        private readonly DirectorySettingsGateway $settingsGateway,
        private readonly TenantScopeService $scopeGateway,
        private readonly AuditRecorderInterface $systemAuditService
    ) {
    }

    public function list(): array
    {
        return $this->departmentRepository->list();
    }

    public function listPaged(array $options): array
    {
        // Global-scope users see all departments — remove tenant filter so the query isn't restricted.
        if (!empty($options['tenantUserId'])) {
            $tenantUserId = (int) $options['tenantUserId'];
            if ($tenantUserId > 0 && $this->scopeGateway->hasGlobalAccess($tenantUserId)) {
                unset($options['tenantUserId'], $options['tenantIds']);
            }
        }
        return $this->departmentRepository->listPaged($options);
    }

    public function listByTenantIds(array $tenantIds): array
    {
        return $this->departmentRepository->listByTenantIds($tenantIds);
    }

    public function groupActiveByTenantIds(array $tenantIds): array
    {
        $tenantIds = toIntIds($tenantIds);
        if (!$tenantIds) {
            return [];
        }

        $departments = $this->listByTenantIds($tenantIds);
        if (!$departments) {
            return [];
        }

        $grouped = [];
        foreach ($departments as $department) {
            $tenantId = (int) ($department['tenant_id'] ?? 0);
            if ($tenantId <= 0) {
                continue;
            }
            $grouped[$tenantId] ??= [];
            $grouped[$tenantId][] = $department;
        }

        foreach ($grouped as &$items) {
            usort($items, static function (array $a, array $b): int {
                return strcmp((string) ($a['description'] ?? ''), (string) ($b['description'] ?? ''));
            });
        }
        unset($items);

        return $grouped;
    }

    public function listByIds(array $departmentIds): array
    {
        return $this->departmentRepository->listByIds($departmentIds);
    }



    public function findByUuid(string $uuid): ?array
    {
        return $this->departmentRepository->findByUuid($uuid);
    }

    public function findById(int $id): ?array
    {
        return $this->departmentRepository->find($id);
    }

    public function createFromAdmin(array $input, int $currentUserId = 0): array
    {
        $form = $this->sanitizeBase($input);
        $errors = $this->validateBase($form);
        $warnings = $this->warningsForCode($form, 0);

        if ($errors) {
            return ['ok' => false, 'errors' => $errors, 'warnings' => $warnings, 'form' => $form];
        }

        $createdId = $this->departmentRepository->create([
            'description' => $form['description'],
            'tenant_id' => $form['tenant_id'],
            'code' => $form['code'] ?: null,
            'cost_center' => $form['cost_center'] ?: null,
            'active' => $form['active'],
            'created_by' => $currentUserId > 0 ? $currentUserId : null,
        ]);

        if (!$createdId) {
            return ['ok' => false, 'errors' => [t('Department can not be created')], 'form' => $form];
        }

        $createdDepartment = $this->departmentRepository->find((int) $createdId);
        $uuid = $createdDepartment['uuid'] ?? null;
        if (!empty($input['is_default'])) {
            $this->settingsGateway->setDefaultDepartmentId((int) $createdId);
        }

        $this->systemAuditService->record('admin.departments.create', 'success', [
            'actor_user_id' => $currentUserId > 0 ? $currentUserId : null,
            'target_type' => 'department',
            'target_id' => (int) $createdId,
            'target_uuid' => is_string($uuid) ? $uuid : '',
            'metadata' => ['tenant_id' => $form['tenant_id']],
        ]);

        return ['ok' => true, 'form' => $form, 'warnings' => $warnings, 'uuid' => $uuid, 'id' => (int) $createdId];
    }

    public function updateFromAdmin(int $departmentId, array $input, int $currentUserId = 0): array
    {
        $form = $this->sanitizeBase($input);
        $errors = $this->validateBase($form);
        $warnings = $this->warningsForCode($form, $departmentId);

        if ($errors) {
            return ['ok' => false, 'errors' => $errors, 'warnings' => $warnings, 'form' => $form];
        }

        $existingBefore = $this->departmentRepository->find($departmentId) ?? [];

        $updated = $this->departmentRepository->update($departmentId, [
            'description' => $form['description'],
            'tenant_id' => $form['tenant_id'],
            'code' => $form['code'] ?: null,
            'cost_center' => $form['cost_center'] ?: null,
            'active' => $form['active'],
            'modified_by' => $currentUserId > 0 ? $currentUserId : null,
        ]);

        if (!$updated) {
            return ['ok' => false, 'errors' => [t('Department can not be updated')], 'form' => $form];
        }

        $this->systemAuditService->record('admin.departments.update', 'success', [
            'actor_user_id' => $currentUserId > 0 ? $currentUserId : null,
            'target_type' => 'department',
            'target_id' => $departmentId,
            'target_uuid' => (string) ($existingBefore['uuid'] ?? ''),
            'before' => [
                'active' => $existingBefore['active'] ?? null,
                'tenant_id' => $existingBefore['tenant_id'] ?? null,
            ],
            'after' => [
                'active' => $form['active'],
                'tenant_id' => $form['tenant_id'],
            ],
        ]);

        return ['ok' => true, 'form' => $form, 'warnings' => $warnings];
    }

    // After moving a department to a different tenant, users who aren't in the new tenant
    // are no longer valid members — remove their assignments immediately.
    public function syncTenant(int $departmentId, int $tenantId): int
    {
        $updated = $this->departmentRepository->setTenant($departmentId, $tenantId);
        if (!$updated) {
            return -1;
        }
        return $this->cleanupUserAssignments($departmentId);
    }

    // Departments belong to exactly one tenant — only the first ID in the array is used.
    public function syncTenants(int $departmentId, array $tenantIds): int
    {
        $tenantId = (int) ($tenantIds[0] ?? 0);
        if ($tenantId <= 0) {
            return -1;
        }
        return $this->syncTenant($departmentId, $tenantId);
    }

    public function cleanupUserAssignments(int $departmentId): int
    {
        return $this->userServicesFactory
            ->createUserDepartmentRepository()
            ->removeInvalidForDepartment($departmentId);
    }

    public function deleteByUuid(string $uuid): array
    {
        $uuid = trim($uuid);
        if ($uuid === '') {
            return ['ok' => false, 'status' => 404, 'error' => 'not_found'];
        }

        $department = $this->departmentRepository->findByUuid($uuid);
        if (!$department || !isset($department['id'])) {
            return ['ok' => false, 'status' => 404, 'error' => 'not_found'];
        }

        $departmentId = (int) $department['id'];
        $userDeptRepo = $this->userServicesFactory->createUserDepartmentRepository();
        $counts = $userDeptRepo->countUsersByDepartmentIds([$departmentId]);
        $userCount = (int) ($counts[$departmentId] ?? 0);
        if ($userCount > 0) {
            return ['ok' => false, 'status' => 409, 'error' => 'department_has_users', 'user_count' => $userCount];
        }

        $deleted = $this->departmentRepository->delete($departmentId);
        if (!$deleted) {
            return ['ok' => false, 'status' => 500, 'error' => 'delete_failed'];
        }

        $this->systemAuditService->record('admin.departments.delete', 'success', [
            'target_type' => 'department',
            'target_id' => $departmentId,
            'target_uuid' => (string) ($department['uuid'] ?? ''),
        ]);

        return ['ok' => true, 'department' => $department];
    }

    private function sanitizeBase(array $input): array
    {
        return [
            'description' => trim((string) ($input['description'] ?? '')),
            'tenant_id' => (int) ($input['tenant_id'] ?? 0),
            'code' => trim((string) ($input['code'] ?? '')),
            'cost_center' => trim((string) ($input['cost_center'] ?? '')),
            'active' => $this->normalizeActive($input['active'] ?? 1),
        ];
    }

    private function validateBase(array $form): array
    {
        $errors = [];
        if ($form['description'] === '') {
            $errors[] = t('Description cannot be empty');
        }
        if ((int) ($form['tenant_id'] ?? 0) <= 0) {
            $errors[] = t('Please select at least one tenant');
        }
        return $errors;
    }

    // Duplicate codes are a warning, not a hard error — codes are optional identifiers,
    // not unique keys, so duplicates are allowed but surfaced to the user.
    private function warningsForCode(array $form, int $excludeId): array
    {
        $warnings = [];
        $code = $form['code'] ?? '';
        if ($code !== '' && $this->departmentRepository->existsByCode($code, $excludeId)) {
            $warnings[] = t('Department code already exists');
        }
        return $warnings;
    }

    private function normalizeActive($value): int
    {
        $value = strtolower(trim((string) $value));
        if (in_array($value, ['0', 'false', 'inactive'], true)) {
            return 0;
        }
        return 1;
    }

}
