using System.CommandLine;
using System.Text.Json;
using ZB.MOM.WW.ScadaBridge.CLI;
using ZB.MOM.WW.ScadaBridge.CLI.Commands;

namespace ZB.MOM.WW.ScadaBridge.CLI.Tests.Commands;

/// <summary>
/// M9-T28b: <c>template alarm add/update</c> and <c>template script add/update</c>
/// must expose a <c>--trigger-kind</c> option (advisory|strict) that writes
/// <c>"analysisKind":"Strict"</c> into the trigger-config JSON for expression
/// triggers, matching the T28a backend contract exactly.
/// </summary>
public class TemplateTriggerKindTests
{
    // ── TriggerConfigJson.InjectAnalysisKind — the shared helper used by
    //    alarm-update / script-add / script-update ──────────────────────────────

    // Inject strict into an Expression config that has no prior analysisKind
    [Fact]
    public void InjectAnalysisKind_Strict_AddsKey()
    {
        var json = "{\"expression\":\"x > 0\"}";
        var result = TriggerConfigJson.InjectAnalysisKind(json, "strict");

        Assert.NotNull(result);
        using var doc = JsonDocument.Parse(result!);
        Assert.Equal("Strict", doc.RootElement.GetProperty("analysisKind").GetString());
    }

    // Inject advisory (null) into config that had Strict → key removed
    [Fact]
    public void InjectAnalysisKind_Advisory_RemovesKey()
    {
        var json = "{\"expression\":\"x > 0\",\"analysisKind\":\"Strict\"}";
        var result = TriggerConfigJson.InjectAnalysisKind(json, null);

        Assert.NotNull(result);
        using var doc = JsonDocument.Parse(result!);
        Assert.False(doc.RootElement.TryGetProperty("analysisKind", out _));
    }

    // Inject strict into config that already had Strict → still Strict (idempotent)
    [Fact]
    public void InjectAnalysisKind_Strict_Idempotent()
    {
        var json = "{\"expression\":\"x > 0\",\"analysisKind\":\"Strict\"}";
        var result = TriggerConfigJson.InjectAnalysisKind(json, "strict");

        Assert.NotNull(result);
        using var doc = JsonDocument.Parse(result!);
        Assert.Equal("Strict", doc.RootElement.GetProperty("analysisKind").GetString());
    }

    // Null config → returns null (no config to inject into)
    [Fact]
    public void InjectAnalysisKind_NullConfig_ReturnsNull()
    {
        var result = TriggerConfigJson.InjectAnalysisKind(null, "strict");
        Assert.Null(result);
    }

    // Case-insensitive: "STRICT" → canonical "Strict"
    [Fact]
    public void InjectAnalysisKind_Strict_CaseInsensitive()
    {
        var json = "{\"expression\":\"x > 0\"}";
        var result = TriggerConfigJson.InjectAnalysisKind(json, "STRICT");

        Assert.NotNull(result);
        using var doc = JsonDocument.Parse(result!);
        Assert.Equal("Strict", doc.RootElement.GetProperty("analysisKind").GetString());
    }

    // Other keys preserved when injecting
    [Fact]
    public void InjectAnalysisKind_OtherKeysPreserved()
    {
        var json = "{\"expression\":\"x > 0\",\"mode\":\"OnTrue\"}";
        var result = TriggerConfigJson.InjectAnalysisKind(json, "strict");

        Assert.NotNull(result);
        using var doc = JsonDocument.Parse(result!);
        Assert.Equal("x > 0", doc.RootElement.GetProperty("expression").GetString());
        Assert.Equal("OnTrue", doc.RootElement.GetProperty("mode").GetString());
        Assert.Equal("Strict", doc.RootElement.GetProperty("analysisKind").GetString());
    }

    // ── Option surface: alarm add + update have --trigger-kind ───────────────

    [Fact]
    public void AlarmAdd_HasTriggerKindOption()
    {
        var add = AlarmGroup().Subcommands.Single(c => c.Name == "add");
        Assert.Contains("--trigger-kind", add.Options.Select(o => o.Name));
    }

    [Fact]
    public void AlarmUpdate_HasTriggerKindOption()
    {
        var update = AlarmGroup().Subcommands.Single(c => c.Name == "update");
        Assert.Contains("--trigger-kind", update.Options.Select(o => o.Name));
    }

    // ── Option surface: script add + update have --trigger-kind ─────────────

    [Fact]
    public void ScriptAdd_HasTriggerKindOption()
    {
        var add = ScriptGroup().Subcommands.Single(c => c.Name == "add");
        Assert.Contains("--trigger-kind", add.Options.Select(o => o.Name));
    }

    [Fact]
    public void ScriptUpdate_HasTriggerKindOption()
    {
        var update = ScriptGroup().Subcommands.Single(c => c.Name == "update");
        Assert.Contains("--trigger-kind", update.Options.Select(o => o.Name));
    }

    // ── AlarmTriggerConfigJson.Build — analysisKind injection ────────────────

    [Fact]
    public void AlarmConfigBuild_Expression_StrictKind_InjectsAnalysisKindStrict()
    {
        // --trigger-kind strict with --expression set → config carries analysisKind:"Strict"
        var json = AlarmTriggerConfigJson.Build(
            triggerType: "Expression",
            attribute: null,
            matchValue: null, notEquals: false,
            min: null, max: null,
            thresholdPerSecond: null, windowSeconds: null, direction: null,
            loLo: null, lo: null, hi: null, hiHi: null,
            expression: "Temp > 80",
            analysisKind: "strict");

        Assert.NotNull(json);
        using var doc = JsonDocument.Parse(json!);
        Assert.Equal("Strict", doc.RootElement.GetProperty("analysisKind").GetString());
    }

    [Fact]
    public void AlarmConfigBuild_Expression_AdvisoryKind_OmitsAnalysisKindKey()
    {
        // --trigger-kind advisory (default) → key omitted
        var json = AlarmTriggerConfigJson.Build(
            triggerType: "Expression",
            attribute: null,
            matchValue: null, notEquals: false,
            min: null, max: null,
            thresholdPerSecond: null, windowSeconds: null, direction: null,
            loLo: null, lo: null, hi: null, hiHi: null,
            expression: "Temp > 80",
            analysisKind: "advisory");

        Assert.NotNull(json);
        using var doc = JsonDocument.Parse(json!);
        Assert.False(doc.RootElement.TryGetProperty("analysisKind", out _));
    }

    [Fact]
    public void AlarmConfigBuild_Expression_NullKind_OmitsAnalysisKindKey()
    {
        // Omitted --trigger-kind → null → Advisory default → key omitted
        var json = AlarmTriggerConfigJson.Build(
            triggerType: "Expression",
            attribute: null,
            matchValue: null, notEquals: false,
            min: null, max: null,
            thresholdPerSecond: null, windowSeconds: null, direction: null,
            loLo: null, lo: null, hi: null, hiHi: null,
            expression: "Temp > 80",
            analysisKind: null);

        Assert.NotNull(json);
        using var doc = JsonDocument.Parse(json!);
        Assert.False(doc.RootElement.TryGetProperty("analysisKind", out _));
    }

    [Fact]
    public void AlarmConfigBuild_NonExpression_StrictKind_DoesNotEmitAnalysisKind()
    {
        // analysisKind is meaningless for non-expression triggers — must be suppressed
        var json = AlarmTriggerConfigJson.Build(
            triggerType: "RangeViolation",
            attribute: "Temp",
            matchValue: null, notEquals: false,
            min: 0, max: 100,
            thresholdPerSecond: null, windowSeconds: null, direction: null,
            loLo: null, lo: null, hi: null, hiHi: null,
            expression: null,
            analysisKind: "strict");

        Assert.NotNull(json);
        using var doc = JsonDocument.Parse(json!);
        Assert.False(doc.RootElement.TryGetProperty("analysisKind", out _));
    }

    [Fact]
    public void AlarmConfigBuild_Expression_StrictKind_CaseInsensitive()
    {
        // "STRICT" (uppercase) must also produce the canonical "Strict" value
        var json = AlarmTriggerConfigJson.Build(
            triggerType: "Expression",
            attribute: null,
            matchValue: null, notEquals: false,
            min: null, max: null,
            thresholdPerSecond: null, windowSeconds: null, direction: null,
            loLo: null, lo: null, hi: null, hiHi: null,
            expression: "x > 0",
            analysisKind: "STRICT");

        Assert.NotNull(json);
        using var doc = JsonDocument.Parse(json!);
        Assert.Equal("Strict", doc.RootElement.GetProperty("analysisKind").GetString());
    }

    private static readonly Option<string> Url = new("--url") { Recursive = true };
    private static readonly Option<string> Username = new("--username") { Recursive = true };
    private static readonly Option<string> Password = new("--password") { Recursive = true };
    private static readonly Option<string> Format = CliOptions.CreateFormatOption();

    private static Command AlarmGroup()
        => TemplateCommands.Build(Url, Format, Username, Password)
            .Subcommands.Single(c => c.Name == "alarm");

    private static Command ScriptGroup()
        => TemplateCommands.Build(Url, Format, Username, Password)
            .Subcommands.Single(c => c.Name == "script");
}
