using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Npgsql;
using PhoenixLib.Logging;

namespace Plugin.ResourceLoader.Services
{
    public class DbFirstResourceHydratorService : IHostedService
    {
        private readonly ResourceLoadingConfiguration _configuration;

        public DbFirstResourceHydratorService(ResourceLoadingConfiguration configuration)
        {
            _configuration = configuration;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            bool dbFirst = string.Equals(Environment.GetEnvironmentVariable("DB_FIRST"), "true", StringComparison.OrdinalIgnoreCase)
                           || string.Equals(Environment.GetEnvironmentVariable("RESOURCE_DB_FIRST"), "true", StringComparison.OrdinalIgnoreCase);
            if (!dbFirst)
            {
                return Task.CompletedTask;
            }

            bool strictDbOnly = string.Equals(Environment.GetEnvironmentVariable("STRICT_DB_ONLY"), "true", StringComparison.OrdinalIgnoreCase);

            try
            {
                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();

                var counters = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
                {
                    ["dat"] = 0,
                    ["lang"] = 0,
                    ["maps"] = 0,
                    ["config_scripts"] = 0
                };

                using var cmd = new NpgsqlCommand("SELECT category, relative_path, content FROM resource_files;", conn);
                using var reader = cmd.ExecuteReader();
                while (reader.Read())
                {
                    string category = reader.GetString(0);
                    string relativePath = reader.GetString(1).Replace('/', Path.DirectorySeparatorChar);
                    byte[] content = (byte[])reader[2];

                    string? destination = category switch
                    {
                        "dat" => Path.Combine(_configuration.ResourcePaths, relativePath),
                        "lang" => Path.Combine(_configuration.ResourcePaths, relativePath),
                        "maps" => Path.Combine(_configuration.ResourcePaths, relativePath),
                        "config_scripts" => Path.Combine(Directory.GetCurrentDirectory(), relativePath),
                        _ => null
                    };

                    if (destination == null)
                    {
                        continue;
                    }

                    string? dir = Path.GetDirectoryName(destination);
                    if (!string.IsNullOrWhiteSpace(dir))
                    {
                        Directory.CreateDirectory(dir);
                    }

                    File.WriteAllBytes(destination, content);
                    if (counters.ContainsKey(category))
                    {
                        counters[category]++;
                    }
                }

                if (strictDbOnly && (counters["dat"] == 0 || counters["lang"] == 0 || counters["maps"] == 0))
                {
                    throw new InvalidOperationException("STRICT_DB_ONLY enabled but one or more required resource categories are missing in resource_files.");
                }

                Log.Info($"[DB_FIRST] Hydrated files from DB: dat={counters["dat"]} lang={counters["lang"]} maps={counters["maps"]} scripts={counters["config_scripts"]}");
            }
            catch (Exception ex)
            {
                Log.Error("[DB_FIRST] Failed to hydrate resources from database", ex);
                if (strictDbOnly)
                {
                    throw;
                }
            }

            return Task.CompletedTask;
        }

        public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
    }
}
