// PRE-GENERATED (design session). Pure logic: .tweq <-> EqState, with validation/clamp.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
using DansoriEQ.Core.Eq;

namespace DansoriEQ.Core.Presets;

public static class PresetSerializer
{
    private static readonly JsonSerializerOptions Opts = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
        WriteIndented = true,
        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
    };

    public static string Serialize(EqPreset p) => JsonSerializer.Serialize(p, Opts);

    /// <summary>Parse + validate an imported .tweq. Throws InvalidDataException on bad input.</summary>
    public static EqPreset Deserialize(string json)
    {
        var p = JsonSerializer.Deserialize<EqPreset>(json, Opts)
                ?? throw new InvalidDataException("빈 프리셋입니다.");
        if (!string.Equals(p.Format, "dansorieq.eq", StringComparison.OrdinalIgnoreCase))
            throw new InvalidDataException("지원하지 않는 파일 형식입니다.");
        if (p.Version > 1)
            throw new InvalidDataException("더 최신 버전의 프리셋입니다. 앱을 업데이트하세요.");

        // Clamp untrusted values into sane ranges.
        foreach (var f in p.Eq.Filters)
        {
            f.Fc = Clamp(f.Fc, 20, 20000);
            f.Gain = Clamp(f.Gain, -24, 24);
            f.Q = Clamp(f.Q, 0.1, 10);
        }
        p.Eq.PreampDb = Clamp(p.Eq.PreampDb, -24, 6);
        if (p.History.Count > 200) p.History = p.History.GetRange(0, 200);
        return p;
    }

    public static EqState ToEqState(EqPreset p)
    {
        var s = new EqState { PreampDb = p.Eq.PreampDb };
        foreach (var f in p.Eq.Filters)
            s.Filters.Add(new Filter
            {
                Type = FilterTypeExtensions.FromApoToken(f.Type),
                Fc = f.Fc,
                GainDb = f.Gain,
                Q = f.Q,
                Enabled = f.Enabled
            });
        return s;
    }

    public static EqPreset FromEqState(EqState s, PresetTarget target, IEnumerable<HistoryItem> history,
                                       DateTimeOffset createdUtc, string? note = null, EffectsConfig? effects = null)
    {
        var p = new EqPreset { CreatedUtc = createdUtc, AuthorNote = note, Target = target };
        p.Eq.PreampDb = s.PreampDb;
        foreach (var f in s.Filters)
            p.Eq.Filters.Add(new PresetFilter
            {
                Type = f.Type.ToApoToken(),
                Fc = f.Fc,
                Gain = f.GainDb,
                Q = f.Q,
                Enabled = f.Enabled
            });
        if (effects is { IsDefault: false })
            p.Effects = new PresetEffects
            {
                PreampLeftDb = effects.PreampLeftDb,
                PreampRightDb = effects.PreampRightDb,
                CrossfeedLevel = effects.CrossfeedLevel,
                BassBoostDb = effects.BassBoostDb
            };
        p.History = new List<HistoryItem>(history);
        return p;
    }

    /// <summary>Extracts effects from a preset (defaults when absent).</summary>
    public static EffectsConfig ToEffects(EqPreset p)
    {
        if (p.Effects is null) return new EffectsConfig();
        return new EffectsConfig
        {
            PreampLeftDb = p.Effects.PreampLeftDb,
            PreampRightDb = p.Effects.PreampRightDb,
            CrossfeedLevel = p.Effects.CrossfeedLevel,
            BassBoostDb = p.Effects.BassBoostDb
        };
    }

    private static double Clamp(double v, double lo, double hi) => v < lo ? lo : (v > hi ? hi : v);
}
