using System.Text;

namespace Jiaowu.Api.Infrastructure.Configuration;

public static class EnvironmentFile
{
    public const string PathVariableName = "JIAOWU_ENV_FILE";

    public static string? Load(string? path = null)
    {
        var configuredPath = path;
        if (string.IsNullOrWhiteSpace(configuredPath))
        {
            configuredPath = Environment.GetEnvironmentVariable(PathVariableName);
        }

        var isExplicit = !string.IsNullOrWhiteSpace(configuredPath);
        var resolvedPath = isExplicit
            ? Path.GetFullPath(configuredPath!, Environment.CurrentDirectory)
            : Path.Combine(AppContext.BaseDirectory, ".env");

        if (!File.Exists(resolvedPath))
        {
            if (isExplicit)
            {
                throw new FileNotFoundException(
                    $"环境变量文件不存在：{resolvedPath}",
                    resolvedPath);
            }

            return null;
        }

        var values = Parse(resolvedPath);
        foreach (var (key, value) in values)
        {
            if (Environment.GetEnvironmentVariable(key) is null)
            {
                Environment.SetEnvironmentVariable(key, value);
            }
        }

        return resolvedPath;
    }

    private static Dictionary<string, string> Parse(string path)
    {
        var values = new Dictionary<string, string>(StringComparer.Ordinal);
        var lineNumber = 0;
        foreach (var sourceLine in File.ReadLines(path, Encoding.UTF8))
        {
            lineNumber++;
            var line = sourceLine.Trim();
            if (line.Length == 0 || line.StartsWith('#'))
            {
                continue;
            }

            if (line.StartsWith("export ", StringComparison.Ordinal))
            {
                line = line[7..].TrimStart();
            }

            var separatorIndex = line.IndexOf('=');
            if (separatorIndex <= 0)
            {
                throw InvalidLine(path, lineNumber, "缺少 KEY=VALUE 分隔符");
            }

            var key = line[..separatorIndex].Trim();
            if (!IsValidKey(key))
            {
                throw InvalidLine(path, lineNumber, $"变量名无效：{key}");
            }

            var rawValue = line[(separatorIndex + 1)..].Trim();
            values[key] = ParseValue(path, lineNumber, rawValue);
        }

        return values;
    }

    private static string ParseValue(string path, int lineNumber, string rawValue)
    {
        if (rawValue.Length == 0)
        {
            return string.Empty;
        }

        if (rawValue[0] == '\'')
        {
            if (rawValue.Length < 2 || rawValue[^1] != '\'')
            {
                throw InvalidLine(path, lineNumber, "单引号值没有正确闭合");
            }

            return rawValue[1..^1];
        }

        if (rawValue[0] != '"')
        {
            return rawValue;
        }

        if (rawValue.Length < 2 || rawValue[^1] != '"')
        {
            throw InvalidLine(path, lineNumber, "双引号值没有正确闭合");
        }

        var value = rawValue[1..^1];
        var result = new StringBuilder(value.Length);
        for (var index = 0; index < value.Length; index++)
        {
            var current = value[index];
            if (current != '\\' || index == value.Length - 1)
            {
                result.Append(current);
                continue;
            }

            var escaped = value[++index];
            switch (escaped)
            {
                case 'n':
                    result.Append('\n');
                    break;
                case 'r':
                    result.Append('\r');
                    break;
                case 't':
                    result.Append('\t');
                    break;
                case '\\':
                    result.Append('\\');
                    break;
                case '"':
                    result.Append('"');
                    break;
                default:
                    result.Append('\\');
                    result.Append(escaped);
                    break;
            }
        }

        return result.ToString();
    }

    private static bool IsValidKey(string key)
    {
        if (key.Length == 0 || !(char.IsAsciiLetter(key[0]) || key[0] == '_'))
        {
            return false;
        }

        return key.All(character =>
            char.IsAsciiLetterOrDigit(character) || character == '_');
    }

    private static FormatException InvalidLine(
        string path,
        int lineNumber,
        string reason) =>
        new($"{path} 第 {lineNumber} 行无效：{reason}。");
}
