using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Npgsql;
using PhoenixLib.Logging;
using PhoenixLib.MultiLanguage;
using WingsAPI.Data.GameData;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

namespace TranslationServer.Loader
{
    public class GenericTranslationFileLoader : IResourceLoader<GenericTranslationDto>
    {
        private static readonly INamingConvention __NamingConvention = UnderscoredNamingConvention.Instance;
        private static readonly IDeserializer __Deserializer = new DeserializerBuilder().WithNamingConvention(__NamingConvention).Build();


        private readonly TranslationsFileLoaderOptions _options;
        private readonly List<GenericTranslationDto> _translations = new();

        public GenericTranslationFileLoader(TranslationsFileLoaderOptions options) => _options = options;

        public async Task<IReadOnlyList<GenericTranslationDto>> LoadAsync()
        {
            if (_translations.Any())
            {
                return _translations;
            }

            Dictionary<string, string> english = null;
            foreach (RegionLanguageType i in Enum.GetValues(typeof(RegionLanguageType)))
            {
                if (i == RegionLanguageType.RU)
                {
                    continue;
                }

                string languageType = i.ToString().ToLowerInvariant();
                var newTmp = new Dictionary<string, string>();

                string languageDirectory = Path.Combine(_options.TranslationsPath, $"{languageType}");
                IEnumerable<string> translationFiles = Enumerable.Empty<string>();
                if (Directory.Exists(languageDirectory))
                {
                    translationFiles = Directory.GetFiles(languageDirectory, "*.yml").Concat(Directory.GetFiles(languageDirectory, "*.yaml"));
                }

                foreach (string translationFile in translationFiles)
                {
                    try
                    {
                        string fileContent = await File.ReadAllTextAsync(translationFile);
                        IDeserializer deserializer = __Deserializer;
                        Dictionary<string, string> tmp = deserializer.Deserialize<Dictionary<string, string>>(fileContent);

                        foreach ((string s, string value) in tmp)
                        {
                            if (string.IsNullOrEmpty(value))
                            {
                                continue;
                            }

                            if (value == $"#{s}" && english != null && english.TryGetValue(s, out string translated))
                            {
                                newTmp[s] = translated;
                                continue;
                            }

                            newTmp[s] = value;
                        }
                    }
                    catch (Exception e)
                    {
                        Log.Error($"[RESOURCE_LOADER] {translationFile} {languageType}", e);
                    }
                }

                if (!newTmp.Any())
                {
                    foreach ((string key, string value) in LoadFromResourceLangFiles(i))
                    {
                        newTmp[key] = value;
                    }

                    if (!newTmp.Any())
                    {
                        foreach ((string key, string value) in LoadFromDatabaseLangFiles(i))
                        {
                            newTmp[key] = value;
                        }
                    }
                }

                if (i == RegionLanguageType.EN)
                {
                    english = newTmp;
                }

                _translations.AddRange(newTmp.Select(s => new GenericTranslationDto
                {
                    Key = s.Key,
                    Value = s.Value,
                    Language = i
                }));
            }

            Log.Info($"[RESOURCE_LOADER] {_translations.Count.ToString()} translations loaded");
            return _translations;
        }

        private IEnumerable<(string key, string value)> LoadFromResourceLangFiles(RegionLanguageType lang)
        {
            var entries = new List<(string key, string value)>();
            string marker = lang switch
            {
                RegionLanguageType.EN => "_uk_",
                RegionLanguageType.DE => "_de_",
                RegionLanguageType.FR => "_fr_",
                RegionLanguageType.IT => "_it_",
                RegionLanguageType.ES => "_es_",
                RegionLanguageType.CZ => "_cz_",
                RegionLanguageType.PL => "_pl_",
                RegionLanguageType.TR => "_tr_",
                _ => null
            };

            if (marker == null)
            {
                return entries;
            }

            try
            {
                const string resourceLangPath = "/app/resources/lang";
                if (!Directory.Exists(resourceLangPath))
                {
                    return entries;
                }

                foreach (string file in Directory.GetFiles(resourceLangPath, $"*{marker}*.txt", SearchOption.TopDirectoryOnly))
                {
                    string filePrefix = Path.GetFileNameWithoutExtension(file)?.Replace('.', '_') ?? "lang";
                    foreach (string line in File.ReadAllLines(file, Encoding.Latin1))
                    {
                        string[] parts = line.Split('\t');
                        if (parts.Length < 2 || string.IsNullOrWhiteSpace(parts[0]) || string.IsNullOrWhiteSpace(parts[1]))
                        {
                            continue;
                        }

                        entries.Add(($"{filePrefix}.{parts[0]}", parts[1]));
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error($"[RESOURCE_LOADER] Resource language fallback failed for {lang}", ex);
            }

            return entries;
        }

        private IEnumerable<(string key, string value)> LoadFromDatabaseLangFiles(RegionLanguageType lang)
        {
            var entries = new List<(string key, string value)>();
            try
            {
                string marker = lang switch
                {
                    RegionLanguageType.EN => "_uk_",
                    RegionLanguageType.DE => "_de_",
                    RegionLanguageType.FR => "_fr_",
                    RegionLanguageType.IT => "_it_",
                    RegionLanguageType.ES => "_es_",
                    RegionLanguageType.CZ => "_cz_",
                    RegionLanguageType.PL => "_pl_",
                    RegionLanguageType.TR => "_tr_",
                    _ => null
                };

                if (marker == null)
                {
                    return entries;
                }

                string host = Environment.GetEnvironmentVariable("DATABASE_IP") ?? Environment.GetEnvironmentVariable("POSTGRES_DATABASE_IP") ?? "127.0.0.1";
                string port = Environment.GetEnvironmentVariable("DATABASE_PORT") ?? Environment.GetEnvironmentVariable("POSTGRES_DATABASE_PORT") ?? "5432";
                string db = Environment.GetEnvironmentVariable("DATABASE_NAME") ?? Environment.GetEnvironmentVariable("POSTGRES_DATABASE_NAME") ?? "game";
                string user = Environment.GetEnvironmentVariable("DATABASE_USER") ?? Environment.GetEnvironmentVariable("POSTGRES_DATABASE_USER") ?? "postgres";
                string pass = Environment.GetEnvironmentVariable("DATABASE_PASSWORD") ?? Environment.GetEnvironmentVariable("POSTGRES_DATABASE_PASSWORD") ?? "postgres";

                using var conn = new NpgsqlConnection($"Host={host};Port={port};Database={db};Username={user};Password={pass}");
                conn.Open();

                using var cmd = new NpgsqlCommand("SELECT relative_path, content FROM resources.resource_files WHERE category='lang' AND lower(relative_path) LIKE lower(@needle);", conn);
                cmd.Parameters.AddWithValue("needle", $"%{marker}%");
                using var reader = cmd.ExecuteReader();
                while (reader.Read())
                {
                    string path = reader.GetString(0);
                    string filePrefix = Path.GetFileNameWithoutExtension(path)?.Replace('.', '_') ?? "lang";
                    byte[] content = (byte[])reader[1];
                    string text = Encoding.Latin1.GetString(content);
                    foreach (string line in text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries))
                    {
                        string[] parts = line.Split('\t');
                        if (parts.Length < 2 || string.IsNullOrWhiteSpace(parts[0]) || string.IsNullOrWhiteSpace(parts[1]))
                        {
                            continue;
                        }

                        string key = $"{filePrefix}.{parts[0]}";
                        entries.Add((key, parts[1]));
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error($"[RESOURCE_LOADER] DB language fallback failed for {lang}", ex);
            }

            return entries;
        }
    }
}