<?php

namespace App\Services\Instalador;

use Illuminate\Encryption\Encrypter;
use RuntimeException;

/**
 * Grava variáveis no arquivo .env durante a instalação pelo navegador.
 *
 * Preserva o que já existe e reescreve apenas as chaves informadas — o .env
 * costuma trazer ajustes do provedor de hospedagem que não podem ser perdidos.
 */
class EscritorEnv
{
    public function __construct(private readonly string $caminho) {}

    public static function padrao(): self
    {
        return new self(base_path('.env'));
    }

    public function existe(): bool
    {
        return file_exists($this->caminho);
    }

    public function gravavel(): bool
    {
        return $this->existe()
            ? is_writable($this->caminho)
            : is_writable(dirname($this->caminho));
    }

    /**
     * @param  array<string, string|null>  $valores
     */
    public function definir(array $valores): void
    {
        if (! $this->gravavel()) {
            throw new RuntimeException('O arquivo .env não pode ser gravado. Ajuste a permissão para 664 pelo File Manager.');
        }

        $conteudo = $this->existe() ? (string) file_get_contents($this->caminho) : '';

        foreach ($valores as $chave => $valor) {
            $linha = $chave.'='.$this->escapar((string) $valor);

            $conteudo = preg_match("/^{$chave}=.*$/m", $conteudo)
                ? preg_replace("/^{$chave}=.*$/m", $linha, $conteudo)
                : rtrim($conteudo, "\r\n")."\n".$linha."\n";
        }

        if (file_put_contents($this->caminho, $conteudo) === false) {
            throw new RuntimeException('Falha ao gravar o arquivo .env.');
        }
    }

    /** Gera a APP_KEY se ainda não houver uma. */
    public function garantirChave(): string
    {
        $atual = env('APP_KEY');

        if (filled($atual)) {
            return (string) $atual;
        }

        $chave = 'base64:'.base64_encode(Encrypter::generateKey(config('app.cipher')));

        $this->definir(['APP_KEY' => $chave]);
        config(['app.key' => $chave]);

        return $chave;
    }

    /**
     * Valor entre aspas sempre que houver caractere que o parser do .env
     * interpretaria — `#` inicia comentário e espaço encerra o valor, o que
     * corta senhas de banco pela metade.
     */
    private function escapar(string $valor): string
    {
        return preg_match('/[\s#"\'$]/', $valor)
            ? '"'.str_replace(['\\', '"'], ['\\\\', '\"'], $valor).'"'
            : $valor;
    }
}
