<?php

namespace MintyPHP\Service\Access;

use MintyPHP\Http\SessionStoreInterface;
use MintyPHP\Repository\Access\PermissionRepositoryInterface;
use MintyPHP\Repository\Access\RolePermissionRepositoryInterface;
use MintyPHP\Repository\Access\UserRoleRepositoryInterface;
use MintyPHP\Service\Audit\SystemAuditService;

class PermissionService
{
    private array $apiPermissionCache = [];

    public function __construct(
        private readonly PermissionRepositoryInterface $permissionRepository,
        private readonly RolePermissionRepositoryInterface $rolePermissionRepository,
        private readonly UserRoleRepositoryInterface $userRoleRepository,
        private readonly SystemAuditService $systemAuditService,
        private readonly SessionStoreInterface $sessionStore
    ) {
    }

    public const USERS_CREATE = 'users.create';
    public const USERS_DELETE = 'users.delete';
    public const USERS_VIEW = 'users.view';
    public const USERS_VIEW_META = 'users.view_meta';
    public const USERS_VIEW_AUDIT = 'users.view_audit';
    public const USERS_UPDATE = 'users.update';
    public const USERS_ACCESS_PDF = 'users.access_pdf';
    public const USERS_SELF_UPDATE = 'users.self_update';
    public const USERS_UPDATE_ASSIGNMENTS = 'users.update_assignments';
    public const ADDRESS_BOOK_VIEW = 'address_book.view';
    public const TENANTS_VIEW = 'tenants.view';
    public const TENANTS_CREATE = 'tenants.create';
    public const TENANTS_UPDATE = 'tenants.update';
    public const TENANTS_SSO_MANAGE = 'tenants.sso_manage';
    public const TENANTS_DELETE = 'tenants.delete';
    public const DEPARTMENTS_VIEW = 'departments.view';
    public const DEPARTMENTS_CREATE = 'departments.create';
    public const DEPARTMENTS_UPDATE = 'departments.update';
    public const DEPARTMENTS_DELETE = 'departments.delete';
    public const DEPARTMENTS_IMPORT = 'departments.import';
    public const ROLES_VIEW = 'roles.view';
    public const ROLES_CREATE = 'roles.create';
    public const ROLES_UPDATE = 'roles.update';
    public const ROLES_DELETE = 'roles.delete';
    public const PERMISSIONS_VIEW = 'permissions.view';
    public const PERMISSIONS_CREATE = 'permissions.create';
    public const PERMISSIONS_UPDATE = 'permissions.update';
    public const PERMISSIONS_DELETE = 'permissions.delete';
    public const SETTINGS_VIEW = 'settings.view';
    public const SETTINGS_UPDATE = 'settings.update';
    public const TENANT_SCOPE_GLOBAL = 'tenant.scope.global';
    public const IMPORTS_VIEW = 'imports.view';
    public const IMPORTS_AUDIT_VIEW = 'imports.audit.view';
    public const JOBS_VIEW = 'jobs.view';
    public const JOBS_MANAGE = 'jobs.manage';
    public const JOBS_RUN_NOW = 'jobs.run_now';
    public const USER_LIFECYCLE_AUDIT_VIEW = 'user_lifecycle_audit.view';
    public const USERS_IMPORT = 'users.import';
    public const USERS_IMPORT_ASSIGNMENTS = 'users.import_assignments';
    public const USERS_LIFECYCLE_RESTORE = 'users.lifecycle_restore';
    public const CUSTOM_FIELDS_MANAGE = 'custom_fields.manage';
    public const CUSTOM_FIELDS_EDIT_VALUES = 'custom_fields.edit_values';
    public const API_DOCS_VIEW = 'api_docs.view';
    public const DOCS_VIEW = 'docs.view';
    public const MAIL_LOG_VIEW = 'mail_log.view';
    public const API_AUDIT_VIEW = 'api_audit.view';
    public const SYSTEM_AUDIT_VIEW = 'system_audit.view';
    public const SYSTEM_AUDIT_PURGE = 'system_audit.purge';
    public const STATS_VIEW = 'stats.view';
    public const API_TOKENS_MANAGE = 'api_tokens.manage';

    public function userHas(int $userId, string $permissionKey): bool
    {
        $permissionKey = trim((string) $permissionKey);
        if ($userId <= 0 || $permissionKey === '') {
            return false;
        }
        $keys = $this->getUserPermissions($userId);
        return in_array($permissionKey, $keys, true);
    }

    public function getUserPermissions(int $userId, bool $refresh = false): array
    {
        if ($userId <= 0) {
            return [];
        }

        // Two-tier cache: API requests (stateless) use an in-memory array per-request;
        // web requests use the session store so permissions survive across the page lifecycle.
        if ($this->isApiRequest()) {
            if (!$refresh && isset($this->apiPermissionCache[$userId])) {
                return $this->apiPermissionCache[$userId];
            }

            $roleIds = $this->userRoleRepository->listRoleIdsByUserId($userId);
            $keys = $this->rolePermissionRepository->listPermissionKeysByRoleIds($roleIds);
            $this->apiPermissionCache[$userId] = $keys;
            return $keys;
        }

        $cache = $this->sessionStore->get('permissions');
        if ($refresh) {
            $cache = null;
        }
        if (is_array($cache) && (int) ($cache['user_id'] ?? 0) === $userId) {
            $keys = $cache['keys'] ?? [];
            if (is_array($keys)) {
                return $keys;
            }
        }
        $roleIds = $this->userRoleRepository->listRoleIdsByUserId($userId);
        $keys = $this->rolePermissionRepository->listPermissionKeysByRoleIds($roleIds);
        $this->sessionStore->set('permissions', [
            'user_id' => $userId,
            'keys' => $keys,
        ]);
        return $keys;
    }

    public function getCachedPermissions(int $userId): array
    {
        if ($userId <= 0) {
            return [];
        }

        if ($this->isApiRequest()) {
            return $this->apiPermissionCache[$userId] ?? [];
        }

        $cache = $this->sessionStore->get('permissions');
        if (is_array($cache) && (int) ($cache['user_id'] ?? 0) === $userId) {
            $keys = $cache['keys'] ?? [];
            return is_array($keys) ? $keys : [];
        }
        return [];
    }

    public function clearUserCache(int $userId): void
    {
        if ($this->isApiRequest()) {
            unset($this->apiPermissionCache[$userId]);
            return;
        }

        $cache = $this->sessionStore->get('permissions');
        if (is_array($cache) && (int) ($cache['user_id'] ?? 0) === $userId) {
            $this->sessionStore->remove('permissions');
        }
    }

    // MINTY_API_REQUEST is defined in the API entry point to distinguish context at runtime.
    private function isApiRequest(): bool
    {
        return defined('MINTY_API_REQUEST');
    }

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

    public function listActive(): array
    {
        return $this->permissionRepository->listActive();
    }

    public function listPaged(array $options): array
    {
        return $this->permissionRepository->listPaged($options);
    }

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

    public function findByKey(string $key): ?array
    {
        return $this->permissionRepository->findByKey($key);
    }

    public function createFromAdmin(array $input): array
    {
        $form = $this->sanitizeBase($input);
        $errors = $this->validateBase($form);
        if ($errors) {
            return ['ok' => false, 'errors' => $errors, 'form' => $form];
        }
        if ($this->permissionRepository->findByKey($form['key'])) {
            return ['ok' => false, 'errors' => [t('Permission key already exists')], 'form' => $form];
        }
        $createdId = $this->permissionRepository->create($form);
        if (!$createdId) {
            return ['ok' => false, 'errors' => [t('Permission can not be created')], 'form' => $form];
        }
        $this->systemAuditService->record('admin.permissions.create', 'success', [
            'target_type' => 'permission',
            'target_id' => (int) $createdId,
            'metadata' => [
                'key' => $form['key'],
                'active' => (int) ($form['active'] ?? 0),
                'is_system' => (int) ($form['is_system'] ?? 0),
            ],
        ]);
        return ['ok' => true, 'form' => $form, 'id' => (int) $createdId];
    }

    public function updateFromAdmin(int $id, array $input): array
    {
        $form = $this->sanitizeBase($input);
        $errors = $this->validateBase($form);
        if ($errors) {
            return ['ok' => false, 'errors' => $errors, 'form' => $form];
        }
        $existing = $this->permissionRepository->findByKey($form['key']);
        if ($existing && (int) ($existing['id'] ?? 0) !== $id) {
            return ['ok' => false, 'errors' => [t('Permission key already exists')], 'form' => $form];
        }
        $before = $this->permissionRepository->find($id) ?? [];
        $updated = $this->permissionRepository->update($id, $form);
        if (!$updated) {
            return ['ok' => false, 'errors' => [t('Permission can not be updated')], 'form' => $form];
        }
        $this->systemAuditService->record('admin.permissions.update', 'success', [
            'target_type' => 'permission',
            'target_id' => $id,
            'before' => [
                'active' => $before['active'] ?? null,
            ],
            'after' => [
                'active' => $form['active'] ?? null,
            ],
            'metadata' => ['key' => $form['key']],
        ]);
        return ['ok' => true, 'form' => $form];
    }

    public function deleteById(int $id): array
    {
        $permission = $this->permissionRepository->find($id);
        if (!$permission) {
            return ['ok' => false, 'status' => 404, 'error' => 'not_found'];
        }
        if ((int) ($permission['is_system'] ?? 0) === 1) {
            return ['ok' => false, 'status' => 403, 'error' => 'system_permission_protected'];
        }
        $deleted = $this->permissionRepository->delete($id);
        if (!$deleted) {
            return ['ok' => false, 'status' => 500, 'error' => 'delete_failed'];
        }
        $this->systemAuditService->record('admin.permissions.delete', 'success', [
            'target_type' => 'permission',
            'target_id' => $id,
            'metadata' => ['key' => (string) ($permission['key'] ?? '')],
        ]);
        return ['ok' => true, 'permission' => $permission];
    }

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

    private function validateBase(array $form): array
    {
        $errors = [];
        if ($form['key'] === '') {
            $errors[] = t('Permission key cannot be empty');
        } elseif (!preg_match('/^[a-z0-9._-]+$/i', $form['key'])) {
            $errors[] = t('Permission key is invalid');
        }
        return $errors;
    }

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

    private function normalizeFlag($value): int
    {
        if (is_bool($value)) {
            return $value ? 1 : 0;
        }
        $value = strtolower(trim((string) $value));
        if (in_array($value, ['1', 'true', 'yes', 'on'], true)) {
            return 1;
        }
        return 0;
    }
}
