<?php

namespace routes;

use classes\authentication;
use classes\edge_gateway_manager;
use classes\edge_gateway_department_workspace_service;
use classes\edge_gateway_operation_exception;
use classes\edge_gateway_operation_service;
use classes\edge_gateway_registry_service;
use classes\edge_gateway_view_service;
use classes\edgegateway;
use classes\response;
use Exception;
use objects\logs_o;
use traits\route_t;

class moduleEdgeGatewayRoute
{
    use route_t;

    public function run(): void
    {
        $this->get('/modules/edge-gateways', fn() => $this->handleListGateways(), [
            'modules_shelly_config' => 'List edge gateway module fleet',
        ]);
        $this->get('/modules/edge-gateways/workspace/departments', fn() => $this->handleDepartmentWorkspaceList(), [
            'modules_shelly_config' => 'List department hardware workspaces',
        ]);
        $this->get('/modules/edge-gateways/workspace/departments/{id}', fn() => $this->handleDepartmentWorkspaceDetail(), [
            'modules_shelly_config' => 'View department hardware workspace detail',
        ]);
        $this->get('/modules/edge-gateways/{id}', fn() => $this->handleGatewayDetail(), [
            'modules_shelly_config' => 'View edge gateway module detail',
        ]);
        $this->get('/modules/edge-gateways/{id}/tasks', fn() => $this->handleGatewayTasksPage(), [
            'modules_shelly_config' => 'View edge gateway module tasks',
        ]);
        $this->get('/modules/edge-gateways/{id}/logs', fn() => $this->handleGatewayLogsPage(), [
            'modules_shelly_config' => 'View edge gateway module logs',
        ]);
        $this->get('/modules/edge-gateways/{id}/statistics', fn() => $this->handleGatewayStatisticsPage(), [
            'modules_shelly_config' => 'View edge gateway module statistics',
        ]);
        $this->post('/modules/edge-gateways/{id}/stream-session', fn() => $this->handleGatewayStreamSessionCreate(), [
            'modules_shelly_config' => 'Create an edge gateway module live stream session',
        ]);
        $this->put('/modules/edge-gateways/{id}', fn() => $this->handleGatewayUpdate(), [
            'modules_shelly_config' => 'Update edge gateway module metadata',
        ]);
        $this->get('/modules/edge-gateways/{id}/operations', fn() => $this->handleGatewayOperationsList(), [
            'modules_shelly_config' => 'List edge gateway module operations',
        ]);
        $this->post('/modules/edge-gateways/{id}/operations', fn() => $this->handleGatewayOperationCreate(), [
            'modules_shelly_config' => 'Queue an edge gateway module operation',
        ]);
        $this->post('/modules/edge-gateways/{id}/operations/{operationId}/cancel', fn() => $this->handleGatewayOperationCancel(), [
            'modules_shelly_config' => 'Cancel an edge gateway module operation',
        ]);
        $this->get('/modules/edge-gateways/{id}/operations/{operationId}/events', fn() => $this->handleGatewayOperationEvents(), [
            'modules_shelly_config' => 'List edge gateway module operation events',
        ]);
        $this->post('/modules/edge-gateways/{id}/rotate-credentials', fn() => $this->handleGatewayCredentialRotate(), [
            'modules_shelly_config' => 'Rotate edge gateway module credentials',
        ]);
        $this->post('/modules/edge-gateways/install-token', fn() => $this->handleInstallTokenCreate(), [
            'modules_shelly_config' => 'Create an edge gateway module install token',
        ]);
        $this->get('/modules/edge-gateways/install-token/{id}/status', fn() => $this->handleInstallTokenStatus(), [
            'modules_shelly_config' => 'View edge gateway module installer status',
        ]);
        $this->post('/modules/edge-gateways/{id}/discovery', fn() => $this->handleGatewayDiscovery(), [
            'modules_shelly_config' => 'Queue discovery through the edge gateway module',
        ]);
        $this->put('/modules/edge-gateways/{id}/bindings', fn() => $this->handleBindingsUpdate(), [
            'modules_shelly_config' => 'Update edge gateway module relay bindings',
        ]);
        $this->delete('/modules/edge-gateways/{id}', fn() => $this->handleGatewayDelete(), [
            'modules_shelly_config' => 'Delete an edge gateway module registration',
        ]);
        $this->post('/modules/edge-gateways/departments/{id}/cutover', fn() => $this->handleDepartmentCutover(), [
            'modules_shelly_config' => 'Update department cutover through the edge gateway module',
        ]);
    }

    private function handleListGateways(): void
    {
        global /** @var response $response */ $response;
        $user = $this->requireModuleOperator();

        $departmentId = self::isParametersSet(['department_id']) ? (int)self::getParameter('department_id') : null;
        $view = trim((string)$this->fromQuery('view'));
        if ($departmentId !== null && $departmentId > 0) {
            $this->requireDepartmentAccess((string)$departmentId);
        }

        $payload = $this->views()->listGatewaysWithFleetUsage($departmentId, $view !== 'summary');
        (new logs_o())->add('modules_edgegateway', 'global', 1, $user->id, 'MODULES_EDGEGATEWAY_LIST', 'Listed edge gateways');
        $response->add_meta('fleet_usage', $payload['fleet_usage']);
        $response->success($payload['gateways']);
    }

    private function handleGatewayDetail(): void
    {
        global /** @var response $response */ $response;
        $user = $this->requireModuleOperator();
        $gateway = $this->requireGatewayAccess((int)$this->fromRoute('id'));
        (new logs_o())->add('modules_edgegateway', 'global', 1, $user->id, 'MODULES_EDGEGATEWAY_GET', 'Fetched edge gateway detail');
        $response->success($gateway);
    }

    private function handleDepartmentWorkspaceList(): void
    {
        global /** @var response $response */ $response;
        $user = $this->requireModuleOperator();
        $summaries = $this->workspaces()->listDepartmentSummaries();
        (new logs_o())->add('modules_edgegateway', 'global', 1, $user->id, 'MODULES_EDGEGATEWAY_WORKSPACE_LIST', 'Listed department hardware workspace summaries');
        $response->success($summaries);
    }

    private function handleDepartmentWorkspaceDetail(): void
    {
        global /** @var response $response */ $response;
        $user = $this->requireModuleOperator();
        $departmentId = (int)$this->fromRoute('id');
        self::requireParameterIntPositive($departmentId, 'id');
        $this->requireDepartmentAccess((string)$departmentId);
        (new logs_o())->add('modules_edgegateway', 'global', 1, $user->id, 'MODULES_EDGEGATEWAY_WORKSPACE_GET', 'Fetched department hardware workspace detail');
        $response->success($this->workspaces()->getDepartmentWorkspace($departmentId));
    }

    private function handleGatewayTasksPage(): void
    {
        global /** @var response $response */ $response;
        $user = $this->requireModuleOperator();
        $gatewayId = (int)$this->fromRoute('id');
        $this->requireGatewayAccess($gatewayId);
        (new logs_o())->add('modules_edgegateway', 'global', 1, $user->id, 'MODULES_EDGEGATEWAY_TASKS_GET', 'Fetched edge gateway task timeline');
        $response->success($this->manager()->buildGatewayTasksPage($gatewayId));
    }

    private function handleGatewayLogsPage(): void
    {
        global /** @var response $response */ $response;
        $user = $this->requireModuleOperator();
        $gatewayId = (int)$this->fromRoute('id');
        $this->requireGatewayAccess($gatewayId);
        (new logs_o())->add('modules_edgegateway', 'global', 1, $user->id, 'MODULES_EDGEGATEWAY_LOGS_GET', 'Fetched edge gateway logs');
        $response->success($this->manager()->buildGatewayLogsPage($gatewayId));
    }

    private function handleGatewayStatisticsPage(): void
    {
        global /** @var response $response */ $response;
        $user = $this->requireModuleOperator();
        $gatewayId = (int)$this->fromRoute('id');
        $this->requireGatewayAccess($gatewayId);
        (new logs_o())->add('modules_edgegateway', 'global', 1, $user->id, 'MODULES_EDGEGATEWAY_STATISTICS_GET', 'Fetched edge gateway statistics');
        $response->success($this->manager()->buildGatewayStatisticsPage($gatewayId));
    }

    private function handleGatewayStreamSessionCreate(): void
    {
        global /** @var response $response */ $response;
        $user = $this->requireModuleOperator();
        $gatewayId = (int)$this->fromRoute('id');
        $this->requireGatewayAccess($gatewayId);
        $payload = self::getParametersAsArray();
        $scopes = isset($payload['scopes']) && is_array($payload['scopes']) ? (array)$payload['scopes'] : [];
        (new logs_o())->add('modules_edgegateway', 'global', 1, $user->id, 'MODULES_EDGEGATEWAY_STREAM_SESSION_CREATE', 'Created edge gateway stream session');
        $response->success($this->manager()->createBrowserStreamSession($gatewayId, (int)$user->id, $scopes), 201);
    }

    private function handleGatewayUpdate(): void
    {
        global /** @var response $response */ $response;
        $user = $this->requireModuleOperator();
        self::requireParameters(['label', 'is_primary']);
        self::requireType(self::getParameter('label'), self::TYPE_STRING());
        self::requireType(self::getParameter('is_primary'), self::TYPE_BOOL());

        $gatewayId = (int)$this->fromRoute('id');
        $this->requireGatewayAccess($gatewayId);
        $result = $this->registry()->updateGatewayMetadata($gatewayId, [
            'label' => (string)self::getParameter('label'),
            'is_primary' => (bool)self::getParameter('is_primary'),
        ], (int)$user->id);

        (new logs_o())->add('modules_edgegateway', 'global', 1, $user->id, 'MODULES_EDGEGATEWAY_UPDATE', 'Updated edge gateway metadata');
        $response->success($result);
    }

    private function handleGatewayOperationsList(): void
    {
        global /** @var response $response */ $response;
        $user = $this->requireModuleOperator();
        $gatewayId = (int)$this->fromRoute('id');
        $this->requireGatewayAccess($gatewayId);
        (new logs_o())->add('modules_edgegateway', 'global', 1, $user->id, 'MODULES_EDGEGATEWAY_OPERATIONS_LIST', 'Listed edge gateway operations');
        $response->success($this->operations()->listOperations($gatewayId));
    }

    private function handleGatewayOperationCreate(): void
    {
        global /** @var response $response */ $response;
        $user = $this->requireModuleOperator();
        self::requireParameters(['type', 'request']);
        self::requireType(self::getParameter('type'), self::TYPE_STRING());
        self::requireType(self::getParameter('request'), self::TYPE_ARRAY());

        $gatewayId = (int)$this->fromRoute('id');
        $this->requireGatewayAccess($gatewayId);

        try {
            $operation = $this->operations()->queueOperation(
                $gatewayId,
                (string)self::getParameter('type'),
                (array)self::getParameter('request'),
                (int)$user->id
            );
            (new logs_o())->add('modules_edgegateway', 'global', 1, $user->id, 'MODULES_EDGEGATEWAY_OPERATION_QUEUE', 'Queued edge gateway operation');
            $response->success([
                'operation' => $operation,
                'gateway' => $this->views()->getGateway($gatewayId),
            ], 201);
        } catch (edge_gateway_operation_exception $exception) {
            $response->error([
                'message' => $exception->getMessage(),
                'error_code' => $exception->errorCode,
            ], $exception->status);
        }
    }

    private function handleGatewayOperationCancel(): void
    {
        global /** @var response $response */ $response;
        $user = $this->requireModuleOperator();
        $gatewayId = (int)$this->fromRoute('id');
        $operationId = (int)$this->fromRoute('operationId');
        self::requireParameterIntPositive($operationId, 'operationId');
        $this->requireGatewayAccess($gatewayId);

        try {
            $operation = $this->operations()->cancelOperation($gatewayId, $operationId, (int)$user->id);
            (new logs_o())->add('modules_edgegateway', 'global', 1, $user->id, 'MODULES_EDGEGATEWAY_OPERATION_CANCEL', 'Cancelled edge gateway operation');
            $response->success([
                'operation' => $operation,
                'gateway' => $this->views()->getGateway($gatewayId),
            ]);
        } catch (edge_gateway_operation_exception $exception) {
            $response->error([
                'message' => $exception->getMessage(),
                'error_code' => $exception->errorCode,
            ], $exception->status);
        }
    }

    private function handleGatewayOperationEvents(): void
    {
        global /** @var response $response */ $response;
        $user = $this->requireModuleOperator();
        $gatewayId = (int)$this->fromRoute('id');
        $operationId = (int)$this->fromRoute('operationId');
        self::requireParameterIntPositive($operationId, 'operationId');
        $this->requireGatewayAccess($gatewayId);
        (new logs_o())->add('modules_edgegateway', 'global', 1, $user->id, 'MODULES_EDGEGATEWAY_EVENTS_LIST', 'Listed edge gateway operation events');
        $response->success($this->operations()->listOperationEvents($gatewayId, $operationId));
    }

    private function handleGatewayCredentialRotate(): void
    {
        global /** @var response $response */ $response;
        $user = $this->requireModuleOperator();
        $gatewayId = (int)$this->fromRoute('id');
        $this->requireGatewayAccess($gatewayId);
        (new logs_o())->add('modules_edgegateway', 'global', 1, $user->id, 'MODULES_EDGEGATEWAY_CREDENTIALS_ROTATE', 'Rotated edge gateway credentials');
        $response->success($this->operations()->rotateCredentials($gatewayId, (int)$user->id));
    }

    private function handleInstallTokenCreate(): void
    {
        global /** @var response $response */ $response;
        $user = $this->requireModuleOperator();
        self::requireParameters(['department_id']);

        $departmentId = (int)self::getParameter('department_id');
        self::requireParameterIntPositive($departmentId, 'department_id');
        $this->requireDepartmentAccess((string)$departmentId);

        (new logs_o())->add('modules_edgegateway', 'global', 1, $user->id, 'MODULES_EDGEGATEWAY_INSTALL_TOKEN_CREATE', 'Created edge gateway install token');
        $response->success(
            $this->registry()->createInstallToken(
                $departmentId,
                self::isParametersSet(['label']) ? (string)self::getParameter('label') : null,
                (int)$user->id
            ),
            201
        );
    }

    private function handleInstallTokenStatus(): void
    {
        global /** @var response $response */ $response;
        $user = $this->requireModuleOperator();
        $claimTokenId = (int)$this->fromRoute('id');
        self::requireParameterIntPositive($claimTokenId, 'id');

        $status = $this->registry()->getInstallTokenStatus($claimTokenId);
        $departmentId = (int)($status['department_id'] ?? 0);
        self::requireParameterIntPositive($departmentId, 'department_id');
        $this->requireDepartmentAccess((string)$departmentId);

        (new logs_o())->add(
            'modules_edgegateway',
            'global',
            1,
            $user->id,
            'MODULES_EDGEGATEWAY_INSTALL_TOKEN_STATUS',
            'Viewed edge gateway installer status'
        );

        unset($status['department_id']);
        $response->success($status);
    }

    private function handleGatewayDiscovery(): void
    {
        global /** @var response $response */ $response;
        $user = $this->requireModuleOperator();
        $gatewayId = (int)$this->fromRoute('id');
        $this->requireGatewayAccess($gatewayId);
        $this->operations()->queueDiscoveryOperation($gatewayId, (int)$user->id);
        (new logs_o())->add('modules_edgegateway', 'global', 1, $user->id, 'MODULES_EDGEGATEWAY_DISCOVERY_QUEUE', 'Queued edge gateway discovery');
        $response->success($this->views()->getGateway($gatewayId));
    }

    private function handleBindingsUpdate(): void
    {
        global /** @var response $response */ $response;
        $user = $this->requireModuleOperator();
        self::requireParameters(['bindings']);
        self::requireType(self::getParameter('bindings'), self::TYPE_ARRAY());

        $gatewayId = (int)$this->fromRoute('id');
        $this->requireGatewayAccess($gatewayId);
        $this->registry()->setRelayBindings($gatewayId, (array)self::getParameter('bindings'), (int)$user->id);
        (new logs_o())->add('modules_edgegateway', 'global', 1, $user->id, 'MODULES_EDGEGATEWAY_BINDINGS_UPDATE', 'Updated edge gateway bindings');
        $response->success($this->views()->getGateway($gatewayId));
    }

    private function handleGatewayDelete(): void
    {
        global /** @var response $response */ $response;
        $user = $this->requireModuleOperator();
        $gatewayId = (int)$this->fromRoute('id');
        $this->requireGatewayAccess($gatewayId);
        (new logs_o())->add('modules_edgegateway', 'global', 1, $user->id, 'MODULES_EDGEGATEWAY_DELETE', 'Deleted edge gateway');
        $response->success($this->registry()->deleteGateway($gatewayId, (int)$user->id));
    }

    private function handleDepartmentCutover(): void
    {
        global /** @var response $response */ $response;
        $user = $this->requireModuleOperator();
        self::requireParameters(['transport_mode']);
        $departmentId = (int)$this->fromRoute('id');
        self::requireParameterIntPositive($departmentId, 'id');
        $this->requireDepartmentAccess((string)$departmentId);
        (new logs_o())->add('modules_edgegateway', 'global', 1, $user->id, 'MODULES_EDGEGATEWAY_DEPARTMENT_CUTOVER', 'Updated department gateway cutover');
        $response->success($this->registry()->setDepartmentTransportMode($departmentId, (string)self::getParameter('transport_mode'), (int)$user->id));
    }

    private function requireModuleOperator(): object
    {
        global /** @var response $response */ $response;
        $this->requirePermission('modules_shelly_config');

        try {
            (new edgegateway())->requireModuleEnabled();
        } catch (Exception $exception) {
            $response->error($exception->getMessage(), 409);
        }

        $user = (new authentication())->get_user();
        if (!$user) {
            $response->error('Invalid session', 400);
        }

        return $user;
    }

    private function requireGatewayAccess(int $gatewayId): array
    {
        global /** @var response $response */ $response;
        $gateway = $this->views()->getGateway($gatewayId);
        if (!isset($gateway['id'])) {
            $response->error('Edge gateway not found', 404);
        }

        $departmentId = (int)($gateway['department_id'] ?? 0);
        self::requireParameterIntPositive($departmentId, 'department_id');
        $this->requireDepartmentAccess((string)$departmentId);
        return $gateway;
    }

    private function views(): edge_gateway_view_service
    {
        return new edge_gateway_view_service();
    }

    private function registry(): edge_gateway_registry_service
    {
        return new edge_gateway_registry_service();
    }

    private function operations(): edge_gateway_operation_service
    {
        return new edge_gateway_operation_service();
    }

    private function manager(): edge_gateway_manager
    {
        return new edge_gateway_manager();
    }

    private function workspaces(): edge_gateway_department_workspace_service
    {
        return new edge_gateway_department_workspace_service();
    }
}
