using Microsoft.EntityFrameworkCore;
using StackExchange.Redis;

public class AlertThresholdService : IAlertThresholdService
{
    private readonly AppDbContext _db;
    private readonly IConnectionMultiplexer _redis;
    private readonly IAuditService _audit;

    public AlertThresholdService(
        AppDbContext db,
        IConnectionMultiplexer redis,
        IAuditService audit)
    {
        _db = db;
        _redis = redis;
        _audit = audit;
    }

    public async Task<AlertThreshold> CreateAsync(AlertThresholdRequest req)
    {
        var exists = await _db.AlertThresholds.AnyAsync(t => t.ObservationCode == req.ObservationCode);
        if (exists)
            throw new ConflictException(
                "A threshold for this observation code already exists.",
                "THRESHOLD_CODE_CONFLICT");

        var threshold = new AlertThreshold
        {
            Id = Guid.NewGuid(),
            ObservationCode = req.ObservationCode,
            DisplayName = req.DisplayName,
            Unit = req.Unit,
            CriticalLow = req.CriticalLow,
            WarningLow = req.WarningLow,
            WarningHigh = req.WarningHigh,
            CriticalHigh = req.CriticalHigh,
            CreatedAt = DateTimeOffset.UtcNow
        };
        _db.AlertThresholds.Add(threshold);
        await _db.SaveChangesAsync();

        await _audit.WriteAsync(
            AuditAction.ThresholdCreated,
            "AlertThreshold",
            threshold.Id,
            newValue: new
            {
                threshold.ObservationCode,
                threshold.CriticalLow,
                threshold.WarningLow,
                threshold.WarningHigh,
                threshold.CriticalHigh
            });

        await InvalidateCacheAsync(threshold.ObservationCode);
        return threshold;
    }

    public async Task<List<AlertThreshold>> ListAsync() =>
        await _db.AlertThresholds.OrderBy(t => t.ObservationCode).ToListAsync();

    public async Task<AlertThreshold> GetByIdAsync(Guid id)
    {
        var threshold = await _db.AlertThresholds.FindAsync(id);
        if (threshold is null)
            throw new NotFoundException("Threshold not found.", "THRESHOLD_NOT_FOUND");
        return threshold;
    }

    public async Task<AlertThreshold> UpdateAsync(Guid id, AlertThresholdRequest req)
    {
        var threshold = await _db.AlertThresholds.FindAsync(id);
        if (threshold is null)
            throw new NotFoundException("Threshold not found.", "THRESHOLD_NOT_FOUND");

        var previous = new
        {
            threshold.ObservationCode,
            threshold.CriticalLow,
            threshold.WarningLow,
            threshold.WarningHigh,
            threshold.CriticalHigh,
            threshold.SuppressionWindowMinutes
        };

        threshold.DisplayName = req.DisplayName;
        threshold.Unit = req.Unit;
        threshold.CriticalLow = req.CriticalLow;
        threshold.WarningLow = req.WarningLow;
        threshold.WarningHigh = req.WarningHigh;
        threshold.CriticalHigh = req.CriticalHigh;

        await _db.SaveChangesAsync();

        await _audit.WriteAsync(
            AuditAction.ThresholdUpdated,
            "AlertThreshold",
            threshold.Id,
            previousValue: previous,
            newValue: new
            {
                threshold.ObservationCode,
                threshold.CriticalLow,
                threshold.WarningLow,
                threshold.WarningHigh,
                threshold.CriticalHigh,
                threshold.SuppressionWindowMinutes
            });

        await InvalidateCacheAsync(threshold.ObservationCode);
        return threshold;
    }

    public async Task DeleteAsync(Guid id)
    {
        var threshold = await _db.AlertThresholds.FindAsync(id);
        if (threshold is null)
            throw new NotFoundException("Threshold not found.", "THRESHOLD_NOT_FOUND");

        var previous = new
        {
            threshold.ObservationCode,
            threshold.DisplayName,
            threshold.Unit,
            threshold.CriticalLow,
            threshold.WarningLow,
            threshold.WarningHigh,
            threshold.CriticalHigh,
            threshold.SuppressionWindowMinutes
        };

        var observationCode = threshold.ObservationCode;

        _db.AlertThresholds.Remove(threshold);
        await _db.SaveChangesAsync();

        await _audit.WriteAsync(
            AuditAction.ThresholdDeleted,
            "AlertThreshold",
            id,
            previousValue: previous);

        await InvalidateCacheAsync(observationCode);
    }

    private async Task InvalidateCacheAsync(string observationCode)
    {
        var cache = _redis.GetDatabase();
        await cache.KeyDeleteAsync($"threshold:{observationCode}");
    }
}