<?php

namespace App\Services;

use Illuminate\Support\Facades\Process;

class ProjectDoctor
{
    public function run(string $projectDir): array
    {
        return array_merge($this->checkHost(), $this->checkProject($projectDir));
    }

    public function checkHost(): array
    {
        return [
            $this->checkBinary('Docker', 'command -v docker', 'Install Docker Desktop or make sure `docker` is in PATH.'),
            $this->checkBinary('Docker Compose', 'docker compose version', 'Install Docker Compose v2 support.'),
            $this->checkBinary('git-flow', 'git flow version', 'Install git-flow before running `wp init`.'),
        ];
    }

    public function checkProject(string $projectDir): array
    {
        $results = $this->checkLayout($projectDir);

        $envPath = $projectDir . '/.env';

        if (!file_exists($envPath)) {
            $results[] = $this->warning('Environment', 'No `.env` file found yet. Run `wp init` or copy `.env.example` first.');

            return $results;
        }

        $env = parse_ini_file($envPath, false, INI_SCANNER_RAW) ?: [];

        $results = array_merge($results, $this->checkEnv($env));
        $results = array_merge($results, $this->checkPorts($env));
        $results[] = $this->checkComposerAccess($projectDir);

        return $results;
    }

    public function hasErrors(array $results): bool
    {
        foreach ($results as $result) {
            if ($result['status'] === 'error') {
                return true;
            }
        }

        return false;
    }

    private function checkBinary(string $label, string $command, string $failureMessage): array
    {
        $result = Process::run($command);

        if ($result->successful()) {
            return $this->ok($label, trim($result->output()) ?: 'available');
        }

        return $this->error($label, $failureMessage);
    }

    private function checkLayout(string $projectDir): array
    {
        $requiredFiles = [
            '.env.example',
            'docker-compose.yml',
            'theme/functions.php',
            'theme/composer.json',
            'theme/package.json',
        ];

        $results = [];

        foreach ($requiredFiles as $file) {
            $path = $projectDir . '/' . $file;

            $results[] = file_exists($path)
                ? $this->ok($file, 'found')
                : $this->error($file, 'Missing required starter file.');
        }

        return $results;
    }

    private function checkEnv(array $env): array
    {
        $results = [];
        $requiredKeys = ['VIRTUAL_HOST', 'PORT_WP', 'PORT_DEV', 'DB_NAME', 'DB_HOST', 'THEME_NAME'];

        foreach ($requiredKeys as $key) {
            $value = $env[$key] ?? null;

            $results[] = ($value !== null && $value !== '')
                ? $this->ok($key, 'set')
                : $this->error($key, 'Missing required env value.');
        }

        return $results;
    }

    private function checkPorts(array $env): array
    {
        $results = [];

        foreach (['PORT_WP', 'PORT_DEV'] as $key) {
            $port = (int) ($env[$key] ?? 0);

            if ($port <= 0) {
                $results[] = $this->error($key, 'Port must be a valid integer.');
                continue;
            }

            $socket = @stream_socket_server(sprintf('tcp://127.0.0.1:%d', $port), $errno, $errorMessage);

            if ($socket === false) {
                $results[] = $this->error($key, sprintf('Port %d is unavailable: %s', $port, $errorMessage ?: 'unknown error'));
                continue;
            }

            fclose($socket);
            $results[] = $this->ok($key, sprintf('Port %d is available.', $port));
        }

        return $results;
    }

    private function checkComposerAccess(string $projectDir): array
    {
        $composerFile = $projectDir . '/theme/composer.json';

        if (!file_exists($composerFile)) {
            return $this->warning('Composer access', 'Skipped because `theme/composer.json` is missing.');
        }

        $composer = json_decode((string) file_get_contents($composerFile), true);
        $repositories = $composer['repositories'] ?? [];
        $usesPrivateRegistry = false;

        foreach ($repositories as $repository) {
            $url = $repository['url'] ?? '';

            if (is_string($url) && str_contains($url, 'packages.probots.')) {
                $usesPrivateRegistry = true;
                break;
            }
        }

        if (!$usesPrivateRegistry) {
            return $this->ok('Composer access', 'No private package registry detected.');
        }

        if (getenv('COMPOSER_AUTH')) {
            return $this->ok('Composer access', 'Using COMPOSER_AUTH from the environment.');
        }

        $authPaths = [
            rtrim((string) getenv('HOME'), '/') . '/.composer/auth.json',
            rtrim((string) getenv('HOME'), '/') . '/.config/composer/auth.json',
        ];

        foreach ($authPaths as $path) {
            if ($path !== '/.composer/auth.json' && $path !== '/.config/composer/auth.json' && file_exists($path)) {
                return $this->ok('Composer access', sprintf('Found auth config at `%s`.', $path));
            }
        }

        return $this->error('Composer access', 'Missing Composer auth for the private Probots registry.');
    }

    private function ok(string $label, string $message): array
    {
        return ['status' => 'ok', 'label' => $label, 'message' => $message];
    }

    private function warning(string $label, string $message): array
    {
        return ['status' => 'warning', 'label' => $label, 'message' => $message];
    }

    private function error(string $label, string $message): array
    {
        return ['status' => 'error', 'label' => $label, 'message' => $message];
    }
}
