<?php

declare(strict_types=1);

namespace App\Framework\Config;

use App\Framework\Filesystem\ValueObjects\FilePath;

/**
 * Unified .env file parser
 *
 * Handles:
 * - Comment lines (#)
 * - Empty lines
 * - Key=Value parsing
 * - Quote removal (single and double)
 * - Type casting (bool, null, int, float, string)
 */
final readonly class EnvFileParser
{
    /**
     * Parse .env file into associative array
     *
     * @return array<string, mixed>
     */
    public function parse(FilePath|string $filePath): array
    {
        $file = $filePath instanceof FilePath ? $filePath : FilePath::create($filePath);

        if (!$file->exists() || !$file->isReadable()) {
            return [];
        }

        $lines = file($file->toString(), FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

        if ($lines === false) {
            return [];
        }

        return $this->parseLines($lines);
    }

    /**
     * Parse lines from .env file
     *
     * @param array<string> $lines
     * @return array<string, mixed>
     */
    public function parseLines(array $lines): array
    {
        $variables = [];

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

            // Skip comments and lines without =
            if ($this->shouldSkipLine($trimmed)) {
                continue;
            }

            $parsed = $this->parseLine($trimmed);

            if ($parsed !== null) {
                [$key, $value] = $parsed;
                $variables[$key] = $value;
            }
        }

        return $variables;
    }

    /**
     * Check if line should be skipped
     */
    private function shouldSkipLine(string $line): bool
    {
        return str_starts_with($line, '#') || !str_contains($line, '=');
    }

    /**
     * Parse a single key=value line
     *
     * @return array{0: string, 1: mixed}|null [key, value] tuple or null if invalid
     */
    private function parseLine(string $line): ?array
    {
        $equalPos = strpos($line, '=');

        if ($equalPos === false) {
            return null;
        }

        $key = trim(substr($line, 0, $equalPos));
        $value = trim(substr($line, $equalPos + 1));

        // Remove surrounding quotes
        $value = $this->removeQuotes($value);

        // Handle multiline values (basic support)
        if (str_ends_with($value, '\\')) {
            $value = rtrim($value, '\\');
            // Note: Full multiline support would require reading next line
        }

        return [$key, $this->castValue($value)];
    }

    /**
     * Remove surrounding quotes from value
     */
    public function removeQuotes(string $value): string
    {
        // Remove double quotes
        if (str_starts_with($value, '"') && str_ends_with($value, '"') && strlen($value) > 1) {
            return substr($value, 1, -1);
        }

        // Remove single quotes
        if (str_starts_with($value, "'") && str_ends_with($value, "'") && strlen($value) > 1) {
            return substr($value, 1, -1);
        }

        return $value;
    }

    /**
     * Cast string values to appropriate PHP types
     */
    private function castValue(string $value): mixed
    {
        return match (strtolower($value)) {
            'true' => true,
            'false' => false,
            'null' => null,
            default => $this->castNumericValue($value)
        };
    }

    /**
     * Cast numeric values or return as string
     */
    private function castNumericValue(string $value): int|float|string
    {
        if (!is_numeric($value)) {
            return $value;
        }

        return str_contains($value, '.') ? (float) $value : (int) $value;
    }

    /**
     * Parse string content directly (for testing)
     *
     * @return array<string, mixed>
     */
    public function parseString(string $content): array
    {
        $lines = explode("\n", $content);
        return $this->parseLines($lines);
    }
}
