<?php

namespace classes;

use Exception;

class edge_gateway_agent_artifact_locator
{
    private const EDGE_AGENT_BUILD_ARTIFACT_DIRECTORY = 'build/install';
    private const ROUTER_ARTIFACT_DIRECTORY = 'resources/edge-gateway-agent';
    private const DEFAULT_MOUNTED_ARTIFACT_DIRECTORY = '/services/edge-agent/php-agent';
    private const DEFAULT_BAKED_ARTIFACT_DIRECTORY = '/opt/truckwash-edge-agent-artifacts';

    /**
     * @return array<int,string>
     */
    public static function candidatePaths(
        string $fileName,
        ?string $basePath = null,
        ?string $mountedArtifactDirectory = null,
        ?string $bakedArtifactDirectory = null
    ): array
    {
        $basePath = self::normalizePath($basePath ?? WD);
        $candidateDirectories = [];

        $configuredDirectory = trim((string)(getenv('EDGE_AGENT_ARTIFACT_DIR') ?: ''));
        if ($configuredDirectory !== '') {
            $candidateDirectories[] = self::normalizePath($configuredDirectory);
        }

        $candidateDirectories[] = self::routerArtifactDirectory($basePath);

        foreach (self::edgeAgentBuildDirectories($basePath) as $directory) {
            $candidateDirectories[] = $directory;
        }

        $mountedArtifactDirectory = $mountedArtifactDirectory ?? self::mountedArtifactDirectory();
        if ($mountedArtifactDirectory !== null) {
            $candidateDirectories[] = self::normalizePath($mountedArtifactDirectory);
        }

        $bakedArtifactDirectory = $bakedArtifactDirectory ?? self::bakedArtifactDirectory();
        if ($bakedArtifactDirectory !== null) {
            $candidateDirectories[] = self::normalizePath($bakedArtifactDirectory);
        }

        foreach (self::legacyPhpAgentDirectories($basePath) as $directory) {
            $candidateDirectories[] = $directory;
        }

        $paths = [];
        foreach (array_values(array_unique($candidateDirectories)) as $directory) {
            $paths[] = rtrim($directory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $fileName;
        }

        return array_values(array_unique($paths));
    }

    /**
     * @throws Exception
     */
    public static function resolve(
        string $fileName,
        ?string $basePath = null,
        ?string $mountedArtifactDirectory = null,
        ?string $bakedArtifactDirectory = null
    ): string
    {
        $candidatePaths = self::candidatePaths($fileName, $basePath, $mountedArtifactDirectory, $bakedArtifactDirectory);
        foreach ($candidatePaths as $path) {
            if (is_file($path)) {
                return $path;
            }
        }

        $message = 'Missing edge agent artifact: ' . $fileName . '.';
        $message .= ' Checked paths: ' . implode(', ', $candidatePaths) . '.';
        $message .= ' Deploy the router-owned artifacts under ' . self::routerArtifactDirectory($basePath) . '.';

        $legacyNodeArtifact = self::legacyNodeArtifactPath($basePath);
        if ($legacyNodeArtifact !== null) {
            $message .= ' Legacy Node dist artifact found at ' . $legacyNodeArtifact . '.';
            $message .= ' Remove /services/edge-agent/dist and deploy the router resources instead of depending on the legacy mount.';
        }

        throw new Exception($message);
    }

    private static function normalizePath(string $path): string
    {
        $normalized = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path);
        if (DIRECTORY_SEPARATOR === '/') {
            $normalized = preg_replace('#/+#', '/', $normalized) ?: $normalized;
        }

        $trimmed = rtrim($normalized, DIRECTORY_SEPARATOR);
        if ($trimmed === '' && str_starts_with($normalized, DIRECTORY_SEPARATOR)) {
            return DIRECTORY_SEPARATOR;
        }

        return $trimmed;
    }

    private static function routerArtifactDirectory(string $basePath): string
    {
        return self::normalizePath($basePath . DIRECTORY_SEPARATOR . self::ROUTER_ARTIFACT_DIRECTORY);
    }

    /**
     * @return array<int,string>
     */
    private static function edgeAgentBuildDirectories(string $basePath): array
    {
        $relative = str_replace('/', DIRECTORY_SEPARATOR, self::EDGE_AGENT_BUILD_ARTIFACT_DIRECTORY);
        $directories = [
            dirname($basePath, 4) . DIRECTORY_SEPARATOR . 'edge-agent' . DIRECTORY_SEPARATOR . $relative,
            dirname($basePath, 3) . DIRECTORY_SEPARATOR . 'services' . DIRECTORY_SEPARATOR . 'edge-agent' . DIRECTORY_SEPARATOR . $relative,
            dirname($basePath, 2) . DIRECTORY_SEPARATOR . 'edge-agent' . DIRECTORY_SEPARATOR . $relative,
            dirname($basePath) . DIRECTORY_SEPARATOR . 'edge-agent' . DIRECTORY_SEPARATOR . $relative,
        ];

        if (DIRECTORY_SEPARATOR === '/') {
            array_unshift($directories, '/edge-agent/' . self::EDGE_AGENT_BUILD_ARTIFACT_DIRECTORY);
            $directories[] = '/services/edge-agent/' . self::EDGE_AGENT_BUILD_ARTIFACT_DIRECTORY;
        }

        return array_values(array_unique(array_map(
            static fn(string $directory): string => self::normalizePath($directory),
            $directories
        )));
    }

    /**
     * @return array<int,string>
     */
    private static function legacyPhpAgentDirectories(string $basePath): array
    {
        return [
            self::normalizePath(dirname($basePath, 3) . DIRECTORY_SEPARATOR . 'services' . DIRECTORY_SEPARATOR . 'edge-agent' . DIRECTORY_SEPARATOR . 'php-agent'),
            self::normalizePath(dirname($basePath, 2) . DIRECTORY_SEPARATOR . 'edge-agent' . DIRECTORY_SEPARATOR . 'php-agent'),
            self::normalizePath(dirname($basePath) . DIRECTORY_SEPARATOR . 'edge-agent' . DIRECTORY_SEPARATOR . 'php-agent'),
        ];
    }

    private static function mountedArtifactDirectory(): ?string
    {
        if (DIRECTORY_SEPARATOR !== '/') {
            return null;
        }

        return self::DEFAULT_MOUNTED_ARTIFACT_DIRECTORY;
    }

    private static function bakedArtifactDirectory(): ?string
    {
        if (DIRECTORY_SEPARATOR !== '/') {
            return null;
        }

        return self::DEFAULT_BAKED_ARTIFACT_DIRECTORY;
    }

    private static function legacyNodeArtifactPath(?string $basePath = null): ?string
    {
        $basePath = self::normalizePath($basePath ?? WD);
        $candidateDirectories = [
            dirname($basePath, 3) . DIRECTORY_SEPARATOR . 'services' . DIRECTORY_SEPARATOR . 'edge-agent' . DIRECTORY_SEPARATOR . 'dist',
            dirname($basePath, 2) . DIRECTORY_SEPARATOR . 'edge-agent' . DIRECTORY_SEPARATOR . 'dist',
            dirname($basePath) . DIRECTORY_SEPARATOR . 'edge-agent' . DIRECTORY_SEPARATOR . 'dist',
        ];

        if (DIRECTORY_SEPARATOR === '/') {
            $candidateDirectories[] = '/services/edge-agent/dist';
        }

        foreach (array_values(array_unique(array_map(static fn(string $directory): string => self::normalizePath($directory), $candidateDirectories))) as $directory) {
            $path = rtrim($directory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'agent.mjs';
            if (is_file($path)) {
                return $path;
            }
        }

        return null;
    }
}
