<?php

declare(strict_types=1);

namespace App\Framework\Process\Services;

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Filesystem\ValueObjects\FilePath;
use App\Framework\Process\Process;
use App\Framework\Process\ValueObjects\Command;
use App\Framework\Process\ValueObjects\Composer\ComposerDiagnosticResult;
use App\Framework\Process\ValueObjects\Composer\ComposerPackage;

/**
 * Composer Service.
 *
 * Verwaltet Composer-Operationen wie Diagnose, Updates und Package-Informationen.
 */
final readonly class ComposerService
{
    public function __construct(
        private Process $process,
        private ?FilePath $projectRoot = null
    ) {
    }

    /**
     * Führt Composer Diagnose durch.
     */
    public function diagnose(): ComposerDiagnosticResult
    {
        $result = $this->process->run(
            command: Command::fromArray(['composer', 'diagnose']),
            workingDirectory: $this->projectRoot,
            timeout: Duration::fromSeconds(30)
        );

        $warnings = [];
        $errors = [];

        // Parse Output
        $lines = explode("\n", $result->stdout);

        foreach ($lines as $line) {
            $line = trim($line);

            if (str_contains($line, '[warning]')) {
                $warnings[] = $line;
            } elseif (str_contains($line, '[error]')) {
                $errors[] = $line;
            }
        }

        // Composer Version abrufen
        $version = $this->getVersion();

        if (empty($errors)) {
            return ComposerDiagnosticResult::healthy($version);
        }

        return ComposerDiagnosticResult::withIssues($warnings, $errors, $version);
    }

    /**
     * Gibt die Composer-Version zurück.
     */
    public function getVersion(): string
    {
        $result = $this->process->run(
            Command::fromArray(['composer', '--version'])
        );

        if (! $result->isSuccess()) {
            return 'unknown';
        }

        // Parse "Composer version 2.x.x"
        if (preg_match('/Composer version ([^\s]+)/', $result->stdout, $matches)) {
            return $matches[1];
        }

        return trim($result->stdout);
    }

    /**
     * Validiert composer.json.
     */
    public function validate(): bool
    {
        $result = $this->process->run(
            command: Command::fromArray(['composer', 'validate', '--no-check-all']),
            workingDirectory: $this->projectRoot
        );

        return $result->isSuccess();
    }

    /**
     * Listet veraltete Packages auf.
     *
     * @return ComposerPackage[]
     */
    public function getOutdatedPackages(): array
    {
        $result = $this->process->run(
            command: Command::fromArray(['composer', 'outdated', '--format=json']),
            workingDirectory: $this->projectRoot,
            timeout: Duration::fromSeconds(60)
        );

        if (! $result->isSuccess()) {
            return [];
        }

        $data = json_decode($result->stdout, true);

        if (! isset($data['installed'])) {
            return [];
        }

        $packages = [];

        foreach ($data['installed'] as $package) {
            $packages[] = ComposerPackage::fromComposerOutput(
                name: $package['name'] ?? 'unknown',
                currentVersion: $package['version'] ?? 'unknown',
                latestVersion: $package['latest'] ?? null
            );
        }

        return $packages;
    }

    /**
     * Installiert Dependencies.
     */
    public function install(bool $noDev = false): bool
    {
        $args = ['composer', 'install'];

        if ($noDev) {
            $args[] = '--no-dev';
        }

        $result = $this->process->run(
            command: Command::fromArray($args),
            workingDirectory: $this->projectRoot,
            timeout: Duration::fromMinutes(5)
        );

        return $result->isSuccess();
    }

    /**
     * Aktualisiert Dependencies.
     */
    public function update(bool $noDev = false): bool
    {
        $args = ['composer', 'update'];

        if ($noDev) {
            $args[] = '--no-dev';
        }

        $result = $this->process->run(
            command: Command::fromArray($args),
            workingDirectory: $this->projectRoot,
            timeout: Duration::fromMinutes(10)
        );

        return $result->isSuccess();
    }

    /**
     * Zeigt installierte Packages.
     *
     * @return ComposerPackage[]
     */
    public function getInstalledPackages(): array
    {
        $result = $this->process->run(
            command: Command::fromArray(['composer', 'show', '--format=json']),
            workingDirectory: $this->projectRoot
        );

        if (! $result->isSuccess()) {
            return [];
        }

        $data = json_decode($result->stdout, true);

        if (! isset($data['installed'])) {
            return [];
        }

        $packages = [];

        foreach ($data['installed'] as $package) {
            $packages[] = new ComposerPackage(
                name: $package['name'] ?? 'unknown',
                currentVersion: $package['version'] ?? 'unknown',
                description: $package['description'] ?? null
            );
        }

        return $packages;
    }

    /**
     * Dumpt Autoloader.
     */
    public function dumpAutoload(bool $optimize = false): bool
    {
        $args = ['composer', 'dump-autoload'];

        if ($optimize) {
            $args[] = '--optimize';
        }

        $result = $this->process->run(
            command: Command::fromArray($args),
            workingDirectory: $this->projectRoot
        );

        return $result->isSuccess();
    }
}
