using System.Globalization;
using Hc900Crawler.Core.Application.Feedforward;
using Hc900Crawler.Core.Application.Interfaces;

namespace Hc900Crawler.Infrastructure.Control;

/// <summary>
/// WP3-C: 라이브 데이터 수집 + FeedRampCalculator 호출. 읽기 전용(쓰기 없음).
/// Sim Override(WP0) 활성 시 해당 태그는 override값 우선. extras 태그는 C-6111 스코프 하드코딩(작업지시서 §5).
/// </summary>
public sealed class FeedRampAdvisorService
{
    private readonly IFeedforwardConfigStore _cfgStore;
    private readonly IExperionDbService _db;
    private readonly ISimOverrideStore _sim;

    public FeedRampAdvisorService(IFeedforwardConfigStore cfgStore, IExperionDbService db, ISimOverrideStore sim)
    { _cfgStore = cfgStore; _db = db; _sim = sim; }

    public async Task<FeedRampAdvisory?> ComputeAsync(
        int columnId, double targetFeed, double deltaIAllow,
        double sensibleGain, double feedTempRef, double floodLimit, double n, CancellationToken ct = default)
    {
        var cfg = (await _cfgStore.LoadAllAsync(ct)).FirstOrDefault(c => c.Id == columnId);
        if (cfg is null) return null;

        static string PvTag(string baseTag)
        { return baseTag.EndsWith(".pv", StringComparison.OrdinalIgnoreCase) ? baseTag : baseTag + ".pv"; }

        string picaTag = PvTag(cfg.PressureTag ?? "pica-6111");
        const string piTag = "pi-6111b.pv", tiTag = "ti-6103.pv", steamTag = "ficq-6115.pv", opTag = "tica-6111a.op";

        var tags = new List<string> { PvTag(cfg.FeedTag), picaTag, piTag, tiTag, steamTag, opTag };
        tags.AddRange(cfg.Streams.Select(s => PvTag(s.FlowTag)));

        var rows = (await _db.GetRealtimeRecordsByTagNamesAsync(tags))
                   .ToDictionary(r => r.TagName, r => r);

        bool TryRead(string tag, out double v)
        {
            if (_sim.Enabled && _sim.TryGet(tag, out v)) return true;                 // override(신선)
            if (rows.TryGetValue(tag, out var r) && r.LiveValue is not null
                && double.TryParse(r.LiveValue, NumberStyles.Float, CultureInfo.InvariantCulture, out v)
                && (DateTime.UtcNow - r.Timestamp.ToUniversalTime()).TotalSeconds <= cfg.StaleSec)
                return true;
            v = double.NaN; return false;
        }

        TagSample Sample(string baseTag)
        {
            var tag = PvTag(baseTag);
            bool ok = TryRead(tag, out var v);
            return new TagSample(tag, ok ? v : double.NaN, ok, DateTime.UtcNow);
        }
        double? Opt(string tag) => TryRead(tag, out var v) ? v : (double?)null;

        var feed = Sample(cfg.FeedTag);
        var streams = cfg.Streams.ToDictionary(s => s.Key, s => Sample(s.FlowTag));
        var pv = new PvSnapshot(feed, null, Array.Empty<TagSample>(), streams);

        var extra = new RampExtraInputs(
            Pica: Opt(picaTag), Pi6111b: Opt(piTag), Ti6103: Opt(tiTag),
            Fiq6115: Opt(steamTag), Op: Opt(opTag),
            FloodLimit: floodLimit, FloodExponentN: n);

        return FeedRampCalculator.Compute(cfg, pv, targetFeed, deltaIAllow, sensibleGain, feedTempRef, extra, _sim.Enabled);
    }
}
