<?php

declare(strict_types=1);

namespace ApiModel\Action;

use ApiModel\Service\RoleService;
use ApiModel\Service\MenuService;
use ApiModel\Service\MessageService;
use ApiModel\Service\UserService;
use ApiModel\Service\AuthService;
use ApiModel\Service\AuditLogService;
use ApiModel\Shared\Support\AuthContext;
use ApiModel\Shared\Http\HttpUtil;
use ApiModel\Shared\Exceptions\BadRequestException;
use Psr\Http\Message\ServerRequestInterface;

final class SecurityAction
{
    public function __construct(
        private readonly RoleService $roleService,
        private readonly MenuService $menuService,
        private readonly MessageService $messageService,
        private readonly UserService $userService,
        private readonly AuditLogService $auditLogService,
        private readonly AuthService $authService,
    ) {}

    /** @return list<array<string, mixed>> */
    public function getMenu(ServerRequestInterface $request): array
    {
        $u = AuthContext::jwtUser($request);
        return $this->menuService->getMenuForUser($u['userId'], $u['language']);
    }

    /** @return list<array<string, mixed>> */
    public function listRoles(ServerRequestInterface $request): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        return $this->roleService->listRoles();
    }

    /** @return array<string, mixed> */
    public function getRole(ServerRequestInterface $request, string $id): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        return $this->roleService->getRole($id);
    }

    /** @return array<string, mixed> */
    public function createRole(ServerRequestInterface $request): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        $b = HttpUtil::parseJson($request);
        $nameEs = trim((string) ($b['name_es'] ?? ''));
        $nameEn = trim((string) ($b['name_en'] ?? ''));
        $descEs = isset($b['description_es']) ? trim((string) $b['description_es']) : null;
        $descEn = isset($b['description_en']) ? trim((string) $b['description_en']) : null;

        if ($nameEs === '' || $nameEn === '') {
            throw new BadRequestException('El nombre en español e inglés es obligatorio');
        }

        return $this->roleService->createRole($nameEs, $nameEn, $descEs, $descEn);
    }

    /** @return array<string, mixed> */
    public function updateRole(ServerRequestInterface $request, string $id): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        $b = HttpUtil::parseJson($request);
        $nameEs = trim((string) ($b['name_es'] ?? ''));
        $nameEn = trim((string) ($b['name_en'] ?? ''));
        $descEs = isset($b['description_es']) ? trim((string) $b['description_es']) : null;
        $descEn = isset($b['description_en']) ? trim((string) $b['description_en']) : null;
        $isActive = (bool) ($b['is_active'] ?? true);

        if ($nameEs === '' || $nameEn === '') {
            throw new BadRequestException('El nombre en español e inglés es obligatorio');
        }

        return $this->roleService->updateRole($id, $nameEs, $nameEn, $descEs, $descEn, $isActive);
    }

    /** @return list<array<string, mixed>> */
    public function getRolePermissions(ServerRequestInterface $request, string $roleId): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        return $this->roleService->getPermissions($roleId);
    }

    /** @return array{ok: true} */
    public function saveRolePermission(ServerRequestInterface $request, string $roleId): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        $b = HttpUtil::parseJson($request);
        $pageId = trim((string) ($b['page_id'] ?? ''));
        $canView = (bool) ($b['can_view'] ?? false);
        $canCreate = (bool) ($b['can_create'] ?? false);
        $canEdit = (bool) ($b['can_edit'] ?? false);
        $canDelete = (bool) ($b['can_delete'] ?? false);

        if ($pageId === '') {
            throw new BadRequestException('El identificador de página es obligatorio');
        }

        $this->roleService->savePermission($roleId, $pageId, $canView, $canCreate, $canEdit, $canDelete);

        return ['ok' => true];
    }

    /** @return list<array<string, mixed>> */
    public function listModules(ServerRequestInterface $request): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        return $this->menuService->listModules();
    }

    /** @return array<string, mixed> */
    public function createModule(ServerRequestInterface $request): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        $b = HttpUtil::parseJson($request);
        $nameEs = trim((string) ($b['name_es'] ?? ''));
        $nameEn = trim((string) ($b['name_en'] ?? ''));
        $icon = isset($b['icon']) ? trim((string) $b['icon']) : null;
        $orderIndex = (int) ($b['order_index'] ?? 0);

        if ($nameEs === '' || $nameEn === '') {
            throw new BadRequestException('El nombre en español e inglés es obligatorio');
        }

        return $this->menuService->createModule($nameEs, $nameEn, $icon, $orderIndex);
    }

    /** @return array<string, mixed> */
    public function updateModule(ServerRequestInterface $request, string $id): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        $b = HttpUtil::parseJson($request);
        $nameEs = trim((string) ($b['name_es'] ?? ''));
        $nameEn = trim((string) ($b['name_en'] ?? ''));
        $icon = isset($b['icon']) ? trim((string) $b['icon']) : null;
        $orderIndex = (int) ($b['order_index'] ?? 0);
        $isActive = (bool) ($b['is_active'] ?? true);

        if ($nameEs === '' || $nameEn === '') {
            throw new BadRequestException('El nombre en español e inglés es obligatorio');
        }

        return $this->menuService->updateModule($id, $nameEs, $nameEn, $icon, $orderIndex, $isActive);
    }

    /** @return list<array<string, mixed>> */
    public function listPages(ServerRequestInterface $request): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        return $this->menuService->listPages();
    }

    /** @return array<string, mixed> */
    public function createPage(ServerRequestInterface $request): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        $b = HttpUtil::parseJson($request);
        $moduleId = trim((string) ($b['module_id'] ?? ''));
        $nameEs = trim((string) ($b['name_es'] ?? ''));
        $nameEn = trim((string) ($b['name_en'] ?? ''));
        $path = trim((string) ($b['path'] ?? ''));
        $icon = isset($b['icon']) ? trim((string) $b['icon']) : null;
        $orderIndex = (int) ($b['order_index'] ?? 0);

        if ($moduleId === '' || $nameEs === '' || $nameEn === '' || $path === '') {
            throw new BadRequestException('Módulo, nombre (es/en) y ruta son obligatorios');
        }

        return $this->menuService->createPage($moduleId, $nameEs, $nameEn, $path, $icon, $orderIndex);
    }

    /** @return array<string, mixed> */
    public function updatePage(ServerRequestInterface $request, string $id): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        $b = HttpUtil::parseJson($request);
        $moduleId = trim((string) ($b['module_id'] ?? ''));
        $nameEs = trim((string) ($b['name_es'] ?? ''));
        $nameEn = trim((string) ($b['name_en'] ?? ''));
        $path = trim((string) ($b['path'] ?? ''));
        $icon = isset($b['icon']) ? trim((string) $b['icon']) : null;
        $orderIndex = (int) ($b['order_index'] ?? 0);
        $isActive = (bool) ($b['is_active'] ?? true);

        if ($moduleId === '' || $nameEs === '' || $nameEn === '' || $path === '') {
            throw new BadRequestException('Módulo, nombre (es/en) y ruta son obligatorios');
        }

        return $this->menuService->updatePage($id, $moduleId, $nameEs, $nameEn, $path, $icon, $orderIndex, $isActive);
    }

    /** @return list<array<string, mixed>> */
    public function listMessages(ServerRequestInterface $request): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        return $this->messageService->listMessages();
    }

    /** @return array<string, mixed> */
    public function getMessage(ServerRequestInterface $request, string $code): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        return $this->messageService->getMessageByCode($code);
    }

    /** @return array<string, mixed> */
    public function createMessage(ServerRequestInterface $request): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        $b = HttpUtil::parseJson($request);
        $code = trim((string) ($b['code'] ?? ''));
        $subjectEs = trim((string) ($b['subject_es'] ?? ''));
        $subjectEn = trim((string) ($b['subject_en'] ?? ''));
        $bodyHtmlEs = trim((string) ($b['body_html_es'] ?? ''));
        $bodyHtmlEn = trim((string) ($b['body_html_en'] ?? ''));

        if ($code === '' || $subjectEs === '' || $subjectEn === '' || $bodyHtmlEs === '' || $bodyHtmlEn === '') {
            throw new BadRequestException('Código, asuntos y cuerpos en español e inglés son obligatorios');
        }

        return $this->messageService->createMessage($code, $subjectEs, $subjectEn, $bodyHtmlEs, $bodyHtmlEn);
    }

    /** @return array<string, mixed> */
    public function updateMessage(ServerRequestInterface $request, string $id): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        $b = HttpUtil::parseJson($request);
        $code = trim((string) ($b['code'] ?? ''));
        $subjectEs = trim((string) ($b['subject_es'] ?? ''));
        $subjectEn = trim((string) ($b['subject_en'] ?? ''));
        $bodyHtmlEs = trim((string) ($b['body_html_es'] ?? ''));
        $bodyHtmlEn = trim((string) ($b['body_html_en'] ?? ''));
        $isActive = (bool) ($b['is_active'] ?? true);

        if ($code === '' || $subjectEs === '' || $subjectEn === '' || $bodyHtmlEs === '' || $bodyHtmlEn === '') {
            throw new BadRequestException('Código, asuntos y cuerpos en español e inglés son obligatorios');
        }

        return $this->messageService->updateMessage($id, $code, $subjectEs, $subjectEn, $bodyHtmlEs, $bodyHtmlEn, $isActive);
    }

    /** @return list<array<string, mixed>> */
    public function listUsers(ServerRequestInterface $request): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        $params = $request->getQueryParams();
        $search = isset($params['query']) ? (string) $params['query'] : null;

        return $this->userService->listUsersForAdmin($search);
    }

    /** @return array<string, mixed> */
    public function createUser(ServerRequestInterface $request): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        $b = HttpUtil::parseJson($request);
        $name = trim((string) ($b['name'] ?? ''));
        $email = trim((string) ($b['email'] ?? ''));
        $rolesInput = $b['roles'] ?? [];
        $roleIds = self::parseRoleIds($rolesInput);

        if ($name === '' || $email === '') {
            throw new BadRequestException('El nombre y correo electrónico son obligatorios');
        }

        $user = $this->userService->createUser($name, $email, $roleIds, 'es');
        $this->authService->forgotPassword($email, 'es');

        return $user;
    }

    /** @return array<string, mixed> */
    public function updateUser(ServerRequestInterface $request, string $id): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        $b = HttpUtil::parseJson($request);
        $name = trim((string) ($b['name'] ?? ''));
        $email = trim((string) ($b['email'] ?? ''));
        $password = isset($b['password']) ? (string) $b['password'] : null;
        if ($password === '') {
            $password = null;
        }
        $rolesInput = $b['roles'] ?? [];
        $isActive = (bool) ($b['activo'] ?? $b['is_active'] ?? true);
        $roleIds = self::parseRoleIds($rolesInput);

        if ($name === '' || $email === '') {
            throw new BadRequestException('El nombre y correo electrónico son obligatorios');
        }

        return $this->userService->updateUser($id, $name, $email, $password, $roleIds, $isActive);
    }

    /** @return array{ok: true} */
    public function deleteUser(ServerRequestInterface $request, string $id): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        $this->userService->deleteUser($id);

        return ['ok' => true];
    }

    /** @return list<array<string, mixed>> */
    public function listLogsSummary(ServerRequestInterface $request): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        return $this->auditLogService->listUsersLogsSummary();
    }

    /** @return list<array<string, mixed>> */
    public function getUserLogsDetail(ServerRequestInterface $request): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        $b = HttpUtil::parseJson($request);
        $userId = trim((string) ($b['id'] ?? ''));

        if ($userId === '') {
            throw new BadRequestException('El identificador de usuario es obligatorio');
        }

        return $this->auditLogService->listLogsForUser($userId);
    }

    /** @return array{ok: true} */
    public function deleteRole(ServerRequestInterface $request, string $id): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        $this->roleService->deleteRole($id);

        return ['ok' => true];
    }

    /** @return array{ok: true} */
    public function deleteModule(ServerRequestInterface $request, string $id): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        $this->menuService->deleteModule($id);

        return ['ok' => true];
    }

    /** @return array{ok: true} */
    public function deletePage(ServerRequestInterface $request, string $id): array
    {
        $u = AuthContext::jwtUser($request);
        AuthContext::requireRoles($u, ['ADMIN', 'ADMINISTRADOR', 'ADMINISTRATOR']);

        $this->menuService->deletePage($id);

        return ['ok' => true];
    }

    /** @return list<string> */
    private static function parseRoleIds(mixed $rolesInput): array
    {
        if (! is_array($rolesInput)) {
            return [];
        }

        $roleIds = [];
        foreach ($rolesInput as $r) {
            if (is_string($r) && $r !== '') {
                $roleIds[] = $r;
                continue;
            }
            if (is_array($r) && ! empty($r['id']) && (! isset($r['activo']) || $r['activo'])) {
                $roleIds[] = (string) $r['id'];
            }
        }

        return array_values(array_unique($roleIds));
    }
}
