using System.Runtime.CompilerServices;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using Npgsql;

namespace AiLogia.Bot.Services;

public interface ISystemLogService
{
    Task ErrorAsync(string message, object? context = null, [CallerMemberName] string? memberName = null, [CallerFilePath] string? filePath = null);
    Task ExceptionAsync(Exception ex, object? context = null, [CallerMemberName] string? memberName = null, [CallerFilePath] string? filePath = null);
    Task InfoAsync(string message, object? context = null, [CallerMemberName] string? memberName = null, [CallerFilePath] string? filePath = null);
    Task WarnAsync(string message, object? context = null, [CallerMemberName] string? memberName = null, [CallerFilePath] string? filePath = null);
}

public class SystemLogService : ISystemLogService
{
    private static readonly string _host = Environment.MachineName;
    private readonly string _connectionString;
    private readonly string _logDirectory = Path.Combine(AppContext.BaseDirectory, "logs");
    private readonly string _env;

    public SystemLogService(IConfiguration configuration)
    {
        _connectionString = configuration.GetConnectionString("Postgres") ??
                            throw new InvalidOperationException("Missing Postgres connection string");

        _env = configuration.GetValue<string>("Environment") ?? string.Empty;

        if (!Directory.Exists(_logDirectory))
            Directory.CreateDirectory(_logDirectory);
    }

    public async Task InfoAsync(string message, object? context = null,
        [CallerMemberName] string? memberName = null,
        [CallerFilePath] string? filePath = null)
    {
        await LogAsync("INFO", message, context, memberName, filePath, ConsoleColor.Gray);
    }

    public async Task WarnAsync(string message, object? context = null,
        [CallerMemberName] string? memberName = null,
        [CallerFilePath] string? filePath = null)
    {
        await LogAsync("WARN", message, context, memberName, filePath, ConsoleColor.Yellow);
    }

    public async Task ErrorAsync(string message, object? context = null,
        [CallerMemberName] string? memberName = null,
        [CallerFilePath] string? filePath = null)
    {
        await LogAsync("ERROR", message, context, memberName, filePath, ConsoleColor.Red);
    }

    public async Task ExceptionAsync(Exception ex, object? context = null,
        [CallerMemberName] string? memberName = null,
        [CallerFilePath] string? filePath = null)
    {
        var message = $"[{ex.GetType().Name}] {ex.Message}\n{ex.StackTrace}";
        await LogAsync("EXCEPTION", message, context, memberName, filePath, ConsoleColor.Red);
    }

    private async Task LogAsync(string level, string message, object? context,
        string? memberName, string? filePath, ConsoleColor color)
    {
        var className = Path.GetFileNameWithoutExtension(filePath ?? "Unknown");
        var source = $"{className}.{memberName}";
        var timestamp = DateTime.UtcNow;
        var contextJson = context != null ? JsonSerializer.Serialize(context) : null;

        var msg = $"[{timestamp:yyyy-MM-dd HH:mm:ss}] [{level.ToUpper()}] [{source}] {message}";

        // Console
        Console.ForegroundColor = color;
        Console.WriteLine(msg);
        Console.ResetColor();

        // File
        var logFile = Path.Combine(_logDirectory, $"{DateTime.UtcNow:yyyy-MM-dd}.log");
        var fileLine = $"[{timestamp:O}] [{level}] [{source}] {message}";
        if (contextJson != null)
            fileLine += $" | context: {contextJson}";
        await File.AppendAllLinesAsync(logFile, new[] { fileLine });

        // DB
        try
        {
            await using var conn = new NpgsqlConnection(_connectionString);
            await conn.OpenAsync();
            var cmd = new NpgsqlCommand(@"
                INSERT INTO system_logs (level, source, message, context, environment, hostname)
                VALUES (@level, @source, @message, @context, @env, @host)", conn);
            cmd.Parameters.AddWithValue("level", level);
            cmd.Parameters.AddWithValue("source", source);
            cmd.Parameters.AddWithValue("message", message);
            cmd.Parameters.AddWithValue("context", NpgsqlTypes.NpgsqlDbType.Jsonb, (object?)contextJson ?? DBNull.Value);
            cmd.Parameters.AddWithValue("env", _env);
            cmd.Parameters.AddWithValue("host", _host);
            await cmd.ExecuteNonQueryAsync();
        }
        catch (Exception dbEx)
        {
            Console.ForegroundColor = ConsoleColor.DarkRed;
            Console.WriteLine($"[LOGGER ERROR] Failed to write to DB: {dbEx.Message}");
            Console.ResetColor();
        }
    }
}
