using System;
using System.Collections.Generic;
using System.IO;
using MoonSharp.Interpreter;
using Npgsql;
using PhoenixLib.Logging;
using WingsAPI.Scripting;
using WingsAPI.Scripting.Attribute;
using WingsAPI.Scripting.LUA;
using WingsAPI.Scripting.Object.Timespace;
using WingsAPI.Scripting.ScriptManager;

namespace Plugin.TimeSpaces;

[ScriptObject]
public class DebuggableScriptGenerator
{
    public int TimeSpaceId { get; set; }
    public Closure GenerateMission { get; set; }
}

public class LuaTimeSpaceScriptManager : ITimeSpaceScriptManager
{
    private readonly Dictionary<long, ScriptTimeSpace> _cache = new();
    private readonly IScriptFactory _scriptFactory;
    private readonly ScriptFactoryConfiguration _scriptFactoryConfiguration;
    private readonly STimeSpaceValidator _validator;

    public LuaTimeSpaceScriptManager(IScriptFactory scriptFactory, ScriptFactoryConfiguration scriptFactoryConfiguration, STimeSpaceValidator validator)
    {
        _scriptFactory = scriptFactory;
        _scriptFactoryConfiguration = scriptFactoryConfiguration;
        _validator = validator;
    }

    public void Load()
    {
        EnsureTimespaceScriptsDirectoryHydrated();
        Directory.CreateDirectory(_scriptFactoryConfiguration.TimeSpacesDirectory);

        IEnumerable<string> files = Directory.GetFiles(_scriptFactoryConfiguration.TimeSpacesDirectory, "*.lua");
        foreach (string file in files)
        {
            try
            {
                ScriptTimeSpace raid = _scriptFactory.LoadScript<ScriptTimeSpace>(file);
                if (raid == null)
                {
                    Log.Warn($"Failed to load timespace script {file}");
                    continue;
                }

                Log.Warn($"[TIMESPACE_SCRIPT_MANAGER] Loaded {Path.GetFileName(file)} for timespace: {raid.TimeSpaceId}");
                _cache[raid.TimeSpaceId] = raid;
            }
            catch (InvalidScriptException e)
            {
                Log.Error($"[TIMESPACE_SCRIPT_MANAGER] InvalidScript: {file}", e);
            }
            catch (ScriptRuntimeException e)
            {
                Log.Error($"[TIMESPACE_SCRIPT_MANAGER][SCRIPT ERROR] {file}, {e.DecoratedMessage}", e);
            }
            catch (Exception e)
            {
                Log.Error($"[TIMESPACE_SCRIPT_MANAGER][ERROR] {file}", e);
            }
        }

        Log.Info($"Loaded {_cache.Count} timespace from scripts");
    }

    public ScriptTimeSpace GetScriptedTimeSpace(long id)
    {
        if (!_cache.TryGetValue(id, out ScriptTimeSpace timeSpace))
        {
            return null;
        }

        return timeSpace;
    }

    private void EnsureTimespaceScriptsDirectoryHydrated()
    {
        bool dbFirst = string.Equals(Environment.GetEnvironmentVariable("DB_FIRST"), "true", StringComparison.OrdinalIgnoreCase)
                       || string.Equals(Environment.GetEnvironmentVariable("RESOURCE_DB_FIRST"), "true", StringComparison.OrdinalIgnoreCase);
        if (!dbFirst)
        {
            return;
        }

        if (Directory.Exists(_scriptFactoryConfiguration.TimeSpacesDirectory))
        {
            return;
        }

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

            using var cmd = new NpgsqlCommand("SELECT relative_path, content FROM resource_files WHERE category='config_scripts' AND relative_path LIKE 'config/scripts/timespaces/%';", conn);
            using var reader = cmd.ExecuteReader();
            int count = 0;
            while (reader.Read())
            {
                string relative = reader.GetString(0).Replace('/', Path.DirectorySeparatorChar);
                byte[] bytes = (byte[])reader[1];
                string fullPath = Path.Combine(Directory.GetCurrentDirectory(), relative);
                Directory.CreateDirectory(Path.GetDirectoryName(fullPath) ?? _scriptFactoryConfiguration.TimeSpacesDirectory);
                File.WriteAllBytes(fullPath, bytes);
                count++;
            }

            if (count > 0)
            {
                Log.Info($"[DB_FIRST] Hydrated {count} timespace scripts from resource_files");
            }
        }
        catch (Exception ex)
        {
            Log.Error("[DB_FIRST] Could not hydrate timespace scripts from database", ex);
        }
    }
}