<?php

namespace App\Services;

use Illuminate\Support\Facades\Process;
use MirazMac\DotEnv\Writer as EnvWriter;

class ProjectCreator
{
    public function __construct(private readonly ProjectDoctor $doctor)
    {
    }

    public function ensureUsableTargetDirectory(string $projectDir): void
    {
        if (!file_exists($projectDir)) {
            mkdir($projectDir, 0777, true);
        }

        $files = array_values(array_diff(scandir($projectDir) ?: [], ['.', '..']));

        if ($files !== []) {
            throw new \RuntimeException(sprintf('Target directory `%s` is not empty.', $projectDir));
        }
    }

    public function cloneStarter(string $starterRepo, string $projectDir): void
    {
        $this->runOrFail(sprintf('git clone --depth=1 %s .', escapeshellarg($starterRepo)), $projectDir);
        $this->runOrFail('rm -rf .git', $projectDir);
    }

    public function initializeGit(string $projectDir, string $repositoryUrl): void
    {
        $this->runOrFail('git init', $projectDir);

        if ($repositoryUrl !== '') {
            $this->runOrFail(sprintf('git remote add origin %s', escapeshellarg($repositoryUrl)), $projectDir);
        }

        $this->runOrFail('git add .', $projectDir);
        $this->runOrFail('git commit -m "Initial commit"', $projectDir);
        $this->runOrFail('git flow init -d', $projectDir);
    }

    public function writeEnv(string $projectDir, array $values): void
    {
        $envExamplePath = $projectDir . '/.env.example';
        $envPath = $projectDir . '/.env';

        if (!file_exists($envExamplePath)) {
            throw new \RuntimeException('Unable to find `.env.example` in the starter project.');
        }

        $writer = new EnvWriter($envExamplePath);

        foreach ($values as $key => $value) {
            $writer->set($key, (string) $value);
        }

        $writer->write(true, $envPath);
    }

    public function doctor(string $projectDir): array
    {
        return $this->doctor->run($projectDir);
    }

    public function install(string $projectDir): void
    {
        $this->runOrFail(Docker::base('up -d'), $projectDir);
        $this->runOrFail(Docker::exec('composer install', config('project.publicCwd')), $projectDir);
        $this->runOrFail(Docker::exec('composer install', config('project.themeCwd') . '/theme'), $projectDir);
        $this->runOrFail(Docker::exec('yarn install', config('project.themeCwd') . '/theme'), $projectDir);
        $this->runOrFail(Docker::exec('./bin/install.sh'), $projectDir);
    }

    public function buildEnvValues(string $slug, string $url, string $dbName, string $dbHost): array
    {
        $basePort = random_int(8001, 8999);

        return [
            'VIRTUAL_HOST' => $url,
            'PORT_WP' => $basePort,
            'PORT_DEV' => $basePort + 1,
            'DB_NAME' => $dbName,
            'DB_HOST' => $dbHost,
            'CDN_URL' => sprintf('http://%s/media', $url),
            'THEME_NAME' => 'theme',
            'PROJECT_SLUG' => $slug,
        ];
    }

    private function runOrFail(string $command, string $path): void
    {
        $result = Process::path($path)->run($command);

        if (!$result->successful()) {
            $message = trim($result->errorOutput()) ?: trim($result->output()) ?: 'Unknown process failure.';

            throw new \RuntimeException($message);
        }
    }
}
