<?php

namespace EasyChat\Controller;

use EasyChat\Library\RequestContext;
use EasyChat\Library\SafeRuntime;
use EasyChat\Library\Update\UpdateCenterService;
use EasyChat\Library\Update\UpdateOperationService;
use EasyChat\Library\Update\UpdatePlatformProfile;

class Update extends Base
{
    protected $layouts = 'layout';

    public function __construct($registry)
    {
        parent::__construct($registry);
        $session = RequestContext::session();
        if (empty($session['manager_id'])) {
            header('Location: index.php?route=auth');
            exit;
        }
        if (($session['manager_role'] ?? '') !== 'admin') {
            header('Location: index.php?route=index');
            exit;
        }
    }

    public function index(): void
    {
        $this->vars['title'] = 'Обновления | EasyChat';
        $this->vars['update_platform'] = UpdatePlatformProfile::current();
        $this->vars['update_current_version'] = defined('EASYCHAT_VERSION') ? EASYCHAT_VERSION : '1.0.0';
        $this->template->view('update');
    }

    public function status(): void
    {
        $this->requireAdminPost();
        $force = (string)(RequestContext::post()['force'] ?? '0') === '1';
        $this->releaseSessionLockForReadOnlyJsonResponse();
        $this->json((new UpdateCenterService($this->registry))->snapshot($force));
    }

    public function install(): void
    {
        $this->requireAdminPost();
        if (session_status() === PHP_SESSION_ACTIVE) {
            session_write_close();
        }
         
         
         
        $result = (new UpdateOperationService($this->registry))->runLegacyInstall();
        $this->updateResumeCookie($result);
        unset($result['resume_token']);
        $status = empty($result['success']) ? 422 : 200;
        if (empty($result['success']) && (string)($result['error'] ?? '') === 'legacy_drain_pending') {
             
             
            $status = 202;
        }
        $this->json($result, $status);
    }

    public function start(): void
    {
        $this->requireAdminPost();
        if (session_status() === PHP_SESSION_ACTIVE) {
            session_write_close();
        }
        $result = (new UpdateOperationService($this->registry))->start();
        $this->json($result, empty($result['success']) ? 422 : 200);
    }

    public function operationStatus(): void
    {
        $this->requireAdminPost();
        $operationId = trim((string)(RequestContext::post()['operation_id'] ?? ''));
        $this->releaseSessionLockForReadOnlyJsonResponse();
        $result = (new UpdateOperationService($this->registry))->status($operationId);
        $this->json($result, empty($result['success']) ? 422 : 200);
    }

    public function advance(): void
    {
        $this->requireAdminPost();
        $operationId = trim((string)(RequestContext::post()['operation_id'] ?? ''));
        if (session_status() === PHP_SESSION_ACTIVE) {
            session_write_close();
        }
        $result = (new UpdateOperationService($this->registry))->advance($operationId);
        $this->updateResumeCookie($result);
        unset($result['resume_token']);
        $status = empty($result['success']) ? 422 : (!empty($result['busy']) ? 202 : 200);
        $this->json($result, $status);
    }

    public function retry(): void
    {
        $this->requireAdminPost();
        $operationId = trim((string)(RequestContext::post()['operation_id'] ?? ''));
        if (session_status() === PHP_SESSION_ACTIVE) {
            session_write_close();
        }
        $result = (new UpdateOperationService($this->registry))->retry($operationId);
        $this->json($result, empty($result['success']) ? 422 : 200);
    }

    public function package(): void
    {
        $this->requireAdminPost();
        $artifactKey = (string)(RequestContext::post()['artifact_key'] ?? '');
        if (session_status() === PHP_SESSION_ACTIVE) {
            session_write_close();
        }
        try {
            $package = (new UpdateCenterService($this->registry))->prepareNativePackage($artifactKey);
        } catch (\Throwable $e) {
            $this->json(['success' => false, 'error' => $this->safeError($e->getMessage())], 422);
            return;
        }

        $handle = fopen((string)$package['path'], 'rb');
        if (!is_resource($handle)) {
            SafeRuntime::unlink((string)$package['path']);
            SafeRuntime::rmdir((string)$package['work_dir']);
            $this->json(['success' => false, 'error' => 'update_package_read_failed'], 503);
            return;
        }

        register_shutdown_function(static function () use ($package): void {
            SafeRuntime::unlink((string)$package['path']);
            SafeRuntime::rmdir((string)$package['work_dir']);
        });
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="' . str_replace(['"', "\r", "\n", '\\'], '', (string)$package['filename']) . '"');
        header('Content-Length: ' . (int)$package['size_bytes']);
        header('X-Content-Type-Options: nosniff');
        header('X-EasyChat-Artifact-SHA256: ' . (string)$package['sha256']);
        header('Cache-Control: no-store');
        while (!feof($handle)) {
            $chunk = fread($handle, 1024 * 1024);
            if ($chunk === false) {
                break;
            }
            echo $chunk;
            flush();
        }
        fclose($handle);
        exit;
    }

    private function requireAdminPost(): void
    {
        header('Content-Type: application/json; charset=utf-8');
        $this->requireCsrfTokenJson();
    }

    private function json(array $payload, int $status = 200): void
    {
        http_response_code($status);
        echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        exit;
    }

    private function safeError(string $error): string
    {
        return preg_match('/^[a-z0-9_]{3,80}$/D', $error) ? $error : 'update_request_failed';
    }

    private function updateResumeCookie(array $result): void
    {
        $token = (string)($result['resume_token'] ?? '');
        $operation = is_array($result['operation'] ?? null) ? $result['operation'] : [];
        $pending = (
                (empty($result['success'])
                    && (string)($result['error'] ?? '') === 'legacy_drain_pending')
                || (string)($operation['state'] ?? '') === 'legacy_drain_pending'
            )
            && preg_match('/^[a-f0-9]{64}$/D', $token) === 1;
        $server = RequestContext::server();
        $script = str_replace('\\', '/', (string)($server['SCRIPT_NAME'] ?? '/index.php'));
        $directory = rtrim(str_replace('\\', '/', dirname($script)), '/');
        $path = ($directory === '' || $directory === '.') ? '/' : $directory . '/';
        $secure = (!empty($server['HTTPS']) && strtolower((string)$server['HTTPS']) !== 'off')
            || (int)($server['SERVER_PORT'] ?? 0) === 443;
        $expires = $pending
            ? time() + max(600, min(900, (int)($result['retry_after'] ?? $operation['retry_after'] ?? 0) + 180))
            : time() - 3600;

        setcookie('easychat_update_resume', $pending ? $token : '', [
            'expires' => $expires,
            'path' => $path,
            'secure' => $secure,
            'httponly' => true,
            'samesite' => 'Strict',
        ]);
    }
}
