using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace ScadaLink.SiteEventLogging;

/// <summary>
/// Records operational events to a local SQLite database.
/// Only the active node generates events. Not replicated to standby.
/// On failover, the new active node starts a fresh log.
/// </summary>
public class SiteEventLogger : ISiteEventLogger, IDisposable
{
    private readonly SqliteConnection _connection;
    private readonly ILogger<SiteEventLogger> _logger;
    private readonly object _writeLock = new();
    private bool _disposed;

    public SiteEventLogger(
        IOptions<SiteEventLogOptions> options,
        ILogger<SiteEventLogger> logger,
        string? connectionStringOverride = null)
    {
        _logger = logger;

        var connectionString = connectionStringOverride
            ?? $"Data Source={options.Value.DatabasePath};Cache=Shared";
        _connection = new SqliteConnection(connectionString);
        _connection.Open();

        InitializeSchema();
    }

    internal SqliteConnection Connection => _connection;

    private void InitializeSchema()
    {
        using var cmd = _connection.CreateCommand();
        cmd.CommandText = """
            CREATE TABLE IF NOT EXISTS site_events (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                timestamp TEXT NOT NULL,
                event_type TEXT NOT NULL,
                severity TEXT NOT NULL,
                instance_id TEXT,
                source TEXT NOT NULL,
                message TEXT NOT NULL,
                details TEXT
            );
            CREATE INDEX IF NOT EXISTS idx_events_timestamp ON site_events(timestamp);
            CREATE INDEX IF NOT EXISTS idx_events_type ON site_events(event_type);
            CREATE INDEX IF NOT EXISTS idx_events_instance ON site_events(instance_id);
            """;
        cmd.ExecuteNonQuery();
    }

    public Task LogEventAsync(
        string eventType,
        string severity,
        string? instanceId,
        string source,
        string message,
        string? details = null)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(eventType);
        ArgumentException.ThrowIfNullOrWhiteSpace(severity);
        ArgumentException.ThrowIfNullOrWhiteSpace(source);
        ArgumentException.ThrowIfNullOrWhiteSpace(message);

        var timestamp = DateTimeOffset.UtcNow.ToString("o");

        lock (_writeLock)
        {
            if (_disposed) return Task.CompletedTask;

            try
            {
                using var cmd = _connection.CreateCommand();
                cmd.CommandText = """
                    INSERT INTO site_events (timestamp, event_type, severity, instance_id, source, message, details)
                    VALUES ($timestamp, $event_type, $severity, $instance_id, $source, $message, $details)
                    """;
                cmd.Parameters.AddWithValue("$timestamp", timestamp);
                cmd.Parameters.AddWithValue("$event_type", eventType);
                cmd.Parameters.AddWithValue("$severity", severity);
                cmd.Parameters.AddWithValue("$instance_id", (object?)instanceId ?? DBNull.Value);
                cmd.Parameters.AddWithValue("$source", source);
                cmd.Parameters.AddWithValue("$message", message);
                cmd.Parameters.AddWithValue("$details", (object?)details ?? DBNull.Value);
                cmd.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to record event: {EventType} from {Source}", eventType, source);
            }
        }

        return Task.CompletedTask;
    }

    public void Dispose()
    {
        if (_disposed) return;
        _disposed = true;
        _connection.Dispose();
    }
}
