using System;
using System.Collections.Generic;
using ExperionCrawler.Core.Application.Feedforward;
using ExperionCrawler.Infrastructure.Control;
using Xunit;

namespace ExperionCrawler.Tests;

/// <summary>WO-1 (P-5 confidence 자동강등) 엔진 통합 검증. Downgrade는 private이라 Tick 경유로 관측.</summary>
public class FeedforwardEngineTests
{
    private static ColumnConfig Cfg(double feedFilterTau = 300, double moveThr = 0) => new()
    {
        Id = 1, Name = "C-TEST", Enabled = true, FeedTag = "f", ProductKey = "P",
        ScanSec = 2, FeedFilterTauSec = feedFilterTau, FeedMoveThresholdPerMin = moveThr,
        Streams = new[]
        {
            new StreamConfig { Key = "P", FlowTag = "ficq-6118", Role = StreamRole.Commanded,
                               Grade = Confidence.A, TargetCoeff = 0.95 }
        }
    };

    private static PvSnapshot Snap(double feed, double streamVal, bool streamGood) => new(
        new TagSample("f", feed, true, DateTime.UtcNow),
        null,
        Array.Empty<TagSample>(),
        new Dictionary<string, TagSample> { ["P"] = new TagSample("ficq-6118", streamVal, streamGood, DateTime.UtcNow) });

    [Fact]   // 정상·신선 → config 등급(A) 유지, 사유 없음
    public void Fresh_normal_keeps_config_grade()
    {
        var res = new FeedforwardEngine().Tick(Cfg(), Snap(100, 95, true), new ColumnState(), DateTime.UtcNow);
        var p = res.Streams[0];
        Assert.Equal(Confidence.A, p.Grade);
        Assert.Null(p.GradeReason);
    }

    [Fact]   // PV 신선도 불량 → 한 단계 강등(A→B) + 사유
    public void Stale_pv_downgrades_one_level()
    {
        var res = new FeedforwardEngine().Tick(Cfg(), Snap(100, 80, false), new ColumnState(), DateTime.UtcNow);
        var p = res.Streams[0];
        Assert.Equal(Confidence.B, p.Grade);
        Assert.Contains("신선도", p.GradeReason);
    }

    [Fact]   // stale + 과도 동시 → 두 단계 강등(A→C) Clamp 확인
    public void Stale_plus_transient_clamps_to_C()
    {
        var cfg = Cfg(feedFilterTau: 0, moveThr: 1);   // 필터 off + 작은 임계 → 큰 피드점프가 과도 유발
        var engine = new FeedforwardEngine();
        var st = new ColumnState();
        engine.Tick(cfg, Snap(100, 80, false), st, DateTime.UtcNow);          // tick1: 시드
        var res = engine.Tick(cfg, Snap(1000, 80, false), st, DateTime.UtcNow); // tick2: 피드 급변 → 과도
        var p = res.Streams[0];
        Assert.True(res.Transient);
        Assert.Equal(Confidence.C, p.Grade);   // A→(stale)B→(transient)C, C에서 클램프
    }
}
