using Extrudex.Domain.Interfaces;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace Extrudex.Infrastructure.Services;

/// <summary>
/// Detects low-stock filament spools by comparing the remaining weight percentage
/// against a configurable threshold. The threshold can be set via:
///   1. EXTRUDEX_LOW_STOCK_THRESHOLD env var (highest priority, e.g. "25")
///   2. FilamentAlerts:LowStockThresholdPercent in appsettings.json
///   3. Default: 20% (a standard spool is "low" when ≤20% remains)
/// </summary>
public class LowStockDetector : ILowStockDetector
{
    private readonly ILogger<LowStockDetector> _logger;

    /// <summary>
    /// The percentage threshold below which a spool is considered low stock.
    /// For example, 20 means a spool is "low" when ≤20% of its filament remains.
    /// </summary>
    public decimal LowStockThresholdPercent { get; }

    /// <summary>
    /// Initializes a new instance of the <see cref="LowStockDetector"/> class.
    /// Reads the low-stock threshold from configuration with env var override support.
    /// </summary>
    /// <param name="configuration">Application configuration for threshold settings.</param>
    /// <param name="logger">Logger for diagnostic output.</param>
    public LowStockDetector(IConfiguration configuration, ILogger<LowStockDetector> logger)
    {
        _logger = logger;

        // Priority: env var > appsettings > default (20%)
        var envThreshold = Environment.GetEnvironmentVariable("EXTRUDEX_LOW_STOCK_THRESHOLD");
        var configThreshold = configuration.GetValue<decimal?>("FilamentAlerts:LowStockThresholdPercent");

        if (!string.IsNullOrEmpty(envThreshold) && decimal.TryParse(envThreshold, out var parsedEnv))
        {
            LowStockThresholdPercent = Math.Clamp(parsedEnv, 0m, 100m);
            _logger.LogInformation(
                "Low-stock threshold set from env var EXTRUDEX_LOW_STOCK_THRESHOLD: {Threshold}%",
                LowStockThresholdPercent);
        }
        else if (configThreshold.HasValue)
        {
            LowStockThresholdPercent = Math.Clamp(configThreshold.Value, 0m, 100m);
            _logger.LogInformation(
                "Low-stock threshold set from config FilamentAlerts:LowStockThresholdPercent: {Threshold}%",
                LowStockThresholdPercent);
        }
        else
        {
            LowStockThresholdPercent = 20m;
            _logger.LogInformation(
                "Low-stock threshold using default: {Threshold}%", LowStockThresholdPercent);
        }
    }

    /// <inheritdoc />
    public bool IsLowStock(decimal weightRemainingGrams, decimal weightTotalGrams)
    {
        if (weightTotalGrams <= 0m)
        {
            _logger.LogDebug(
                "Spool with total weight {Total}g cannot be evaluated for low stock — treating as not low",
                weightTotalGrams);
            return false;
        }

        var remainingPercent = GetRemainingWeightPercent(weightRemainingGrams, weightTotalGrams);
        var isLow = remainingPercent <= LowStockThresholdPercent;

        if (isLow)
        {
            _logger.LogDebug(
                "Spool is LOW STOCK: {Remaining}g / {Total}g = {Percent:F1}% (threshold: {Threshold}%)",
                weightRemainingGrams, weightTotalGrams, remainingPercent, LowStockThresholdPercent);
        }

        return isLow;
    }

    /// <inheritdoc />
    public decimal GetRemainingWeightPercent(decimal weightRemainingGrams, decimal weightTotalGrams)
    {
        if (weightTotalGrams <= 0m)
            return 0m;

        return Math.Round(
            (weightRemainingGrams / weightTotalGrams) * 100m,
            1,
            MidpointRounding.AwayFromZero);
    }
}