<?php

namespace classes;

use Exception;

class edge_gateway_install_service
{
    private const ARTIFACTS = [
        'manifest.json' => 'application/json; charset=utf-8',
        'agent.php' => 'application/x-httpd-php; charset=utf-8',
        'lan-worker.php' => 'application/x-httpd-php; charset=utf-8',
        'auto-updater.php' => 'application/x-httpd-php; charset=utf-8',
        'docker-compose.gateway.yml' => 'text/yaml; charset=utf-8',
        'Dockerfile.edge-agent' => 'text/plain; charset=utf-8',
        'Dockerfile.lan-worker' => 'text/plain; charset=utf-8',
        'Dockerfile.auto-updater' => 'text/plain; charset=utf-8',
        'gateway-launcher.sh' => 'text/x-shellscript; charset=utf-8',
        'truckwash-edge-gateway-stack.service' => 'text/plain; charset=utf-8',
        'truckwash-edge-agent.service' => 'text/plain; charset=utf-8',
    ];

    public function __construct(private readonly ?edge_gateway_manager $manager = null)
    {
        edge_gateway_schema_bootstrap::ensureTables();
    }

    public function buildInstallScript(string $plainToken): string
    {
        return $this->normalizeLineEndings($this->manager()->buildInstallScript($plainToken));
    }

    /**
     * @throws Exception
     */
    public function verifyInstallToken(string $plainToken): array
    {
        return $this->manager()->verifyInstallToken($plainToken);
    }

    /**
     * @throws Exception
     */
    public function readArtifact(string $fileName): string
    {
        if ($fileName === 'manifest.json') {
            return $this->buildManifest();
        }

        $path = $this->artifactPath($fileName);
        $contents = file_get_contents($path);
        if ($contents === false) {
            throw new Exception('Unable to read edge agent artifact');
        }

        return $this->normalizeLineEndings($contents);
    }

    public function contentType(string $fileName): string
    {
        return self::ARTIFACTS[$fileName] ?? 'application/octet-stream';
    }

    public function buildArtifactUrl(string $fileName): string
    {
        return rtrim($this->manager()->getApiBaseUrl(), '/') . '/edge-agent/artifacts/' . $fileName;
    }

    /**
     * @throws Exception
     */
    public function artifactPath(string $fileName): string
    {
        if (!array_key_exists($fileName, self::ARTIFACTS) || $fileName === 'manifest.json') {
            throw new Exception('Unknown edge agent artifact');
        }

        return edge_gateway_agent_artifact_locator::resolve($fileName);
    }

    /**
     * @throws Exception
     */
    private function buildManifest(): string
    {
        $artifacts = [];
        foreach (self::ARTIFACTS as $fileName => $contentType) {
            if ($fileName === 'manifest.json') {
                continue;
            }

            $path = $this->artifactPath($fileName);
            $sha256 = hash_file('sha256', $path);
            $bytes = filesize($path);
            if ($sha256 === false || $bytes === false) {
                throw new Exception('Unable to inspect edge agent artifact: ' . $fileName);
            }

            $artifacts[] = [
                'name' => $fileName,
                'sha256' => $sha256,
                'bytes' => $bytes,
                'content_type' => $contentType,
            ];
        }

        return json_encode([
            'schema_version' => 1,
            'package' => 'truckwash-edge-agent',
            'version' => edge_gateway_manager::DEFAULT_INSTALL_VERSION,
            'generated_at' => gmdate('c'),
            'artifacts' => $artifacts,
        ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL;
    }

    private function manager(): edge_gateway_manager
    {
        return $this->manager ?? new edge_gateway_manager();
    }

    private function normalizeLineEndings(string $contents): string
    {
        return str_replace(["\r\n", "\r"], "\n", $contents);
    }
}
