<?php

declare(strict_types=1);

namespace App\Services\Install;

use JsonException;
use RuntimeException;

final class EnvironmentFileWriter
{
    /**
     * Ensure .env exists and contains a non-empty APP_KEY (required before cookies/session encryption).
     *
     * @throws JsonException
     * @throws RuntimeException
     */
    public function ensureApplicationKey(string $projectBasePath): ?string
    {
        $this->ensureEnvFileExists($projectBasePath);

        $envPath = $this->envPath($projectBasePath);
        $current = $this->readEnvValue($envPath, 'APP_KEY');

        if ($this->isUsableAppKey($current)) {
            return $current;
        }

        $key = 'base64:'.base64_encode(random_bytes(32));
        $this->mergeKeysIntoEnv($projectBasePath, ['APP_KEY' => $key]);

        return $key;
    }

    /**
     * @param  array<string, string|int|bool>  $values
     *
     * @throws JsonException
     * @throws RuntimeException
     */
    public function mergeKeysIntoEnv(string $projectBasePath, array $values): void
    {
        $this->ensureEnvFileExists($projectBasePath);

        $envPath = $this->envPath($projectBasePath);

        if (! is_writable($envPath)) {
            throw new RuntimeException('.env 文件不可写，请调整权限后重试。');
        }

        $raw = (string) file_get_contents($envPath);
        if (str_starts_with($raw, "\xEF\xBB\xBF")) {
            $raw = substr($raw, 3);
        }

        $lines = preg_split("/\r\n|\n|\r/", $raw) ?: [];
        $pending = $values;
        $out = [];

        foreach ($lines as $line) {
            $replaced = false;
            foreach ($pending as $key => $value) {
                $pattern = '/^(\s*)'.preg_quote($key, '/').'\s*=/';
                if (preg_match($pattern, $line) === 1) {
                    $out[] = $key.'='.$this->encodeValue($key, $value);
                    unset($pending[$key]);
                    $replaced = true;
                    break;
                }
            }
            if (! $replaced) {
                $out[] = $line;
            }
        }

        foreach ($pending as $key => $value) {
            $out[] = $key.'='.$this->encodeValue($key, $value);
        }

        $body = implode("\n", $out);
        if ($body !== '' && ! str_ends_with($body, "\n")) {
            $body .= "\n";
        }

        if (file_put_contents($envPath, $body, LOCK_EX) === false) {
            throw new RuntimeException('写入 .env 失败，请稍后重试。');
        }
    }

    /**
     * @throws RuntimeException
     */
    private function ensureEnvFileExists(string $projectBasePath): void
    {
        $envPath = $this->envPath($projectBasePath);
        $examplePath = $this->exampleEnvPath($projectBasePath);

        if (is_file($envPath)) {
            return;
        }

        if (! is_file($examplePath)) {
            throw new RuntimeException('未找到 .env 与 .env.example，无法创建环境文件。');
        }

        if (! @copy($examplePath, $envPath)) {
            throw new RuntimeException('无法从 .env.example 创建 .env，请检查目录写入权限。');
        }
    }

    private function envPath(string $projectBasePath): string
    {
        return $projectBasePath.DIRECTORY_SEPARATOR.'.env';
    }

    private function exampleEnvPath(string $projectBasePath): string
    {
        return $projectBasePath.DIRECTORY_SEPARATOR.'.env.example';
    }

    private function readEnvValue(string $envPath, string $key): ?string
    {
        if (! is_file($envPath)) {
            return null;
        }

        $raw = (string) file_get_contents($envPath);

        if (preg_match('/^'.preg_quote($key, '/').'\s*=(.*)$/m', $raw, $matches) !== 1) {
            return null;
        }

        $value = trim($matches[1]);

        if ($value !== '' && ($value[0] === '"' || $value[0] === "'")) {
            $value = trim($value, "\"'");
        }

        return $value === '' ? null : $value;
    }

    private function isUsableAppKey(?string $key): bool
    {
        if ($key === null || $key === '') {
            return false;
        }

        if ($key === 'base64:') {
            return false;
        }

        return true;
    }

    private function encodeValue(string $key, string|int|bool $value): string
    {
        if ($key === 'DB_PORT') {
            return (string) (int) $value;
        }

        if ($key === 'DBERP_INSTALLED') {
            return filter_var($value, FILTER_VALIDATE_BOOLEAN) ? 'true' : 'false';
        }

        if ($key === 'DBERP_UPDATE_VERSION') {
            return (string) (int) $value;
        }

        if ($key === 'DBERP_DISPLAY_VERSION') {
            return json_encode((string) $value, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        }

        if ($key === 'APP_KEY') {
            return (string) $value;
        }

        if ($key === 'APP_ENV' || $key === 'APP_DEBUG') {
            return (string) $value;
        }

        $string = (string) $value;

        return json_encode($string, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    }
}
