using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Admin.Services;

namespace ZB.MOM.WW.OtOpcUa.Admin.Tests;

[Trait("Category", "Unit")]
public sealed class UnsImpactAnalyzerTests
{
    private static UnsTreeSnapshot TwoAreaSnapshot() => new()
    {
        DraftGenerationId = 1,
        RevisionToken = new DraftRevisionToken("rev-1"),
        Areas =
        [
            new UnsAreaSummary("area-pack", "Packaging", ["line-oven", "line-wrap"]),
            new UnsAreaSummary("area-asm", "Assembly",  ["line-weld"]),
        ],
        Lines =
        [
            new UnsLineSummary("line-oven", "Oven-2",   EquipmentCount: 14, TagCount: 237),
            new UnsLineSummary("line-wrap", "Wrapper",  EquipmentCount: 3,  TagCount: 40),
            new UnsLineSummary("line-weld", "Welder",   EquipmentCount: 5,  TagCount: 80),
        ],
    };

    [Fact]
    public void LineMove_Counts_Affected_Equipment_And_Tags()
    {
        var snapshot = TwoAreaSnapshot();
        var move = new UnsMoveOperation(
            Kind: UnsMoveKind.LineMove,
            SourceClusterId: "c1", TargetClusterId: "c1",
            SourceLineId: "line-oven",
            TargetAreaId: "area-asm");

        var preview = UnsImpactAnalyzer.Analyze(snapshot, move);

        preview.AffectedEquipmentCount.ShouldBe(14);
        preview.AffectedTagCount.ShouldBe(237);
        preview.RevisionToken.Value.ShouldBe("rev-1");
        preview.HumanReadableSummary.ShouldContain("'Oven-2'");
        preview.HumanReadableSummary.ShouldContain("'Assembly'");
    }

    [Fact]
    public void CrossCluster_LineMove_Throws()
    {
        var snapshot = TwoAreaSnapshot();
        var move = new UnsMoveOperation(
            Kind: UnsMoveKind.LineMove,
            SourceClusterId: "c1", TargetClusterId: "c2",
            SourceLineId: "line-oven",
            TargetAreaId: "area-asm");

        Should.Throw<CrossClusterMoveRejectedException>(
            () => UnsImpactAnalyzer.Analyze(snapshot, move));
    }

    [Fact]
    public void LineMove_With_UnknownSource_Throws_Validation()
    {
        var snapshot = TwoAreaSnapshot();
        var move = new UnsMoveOperation(
            UnsMoveKind.LineMove, "c1", "c1",
            SourceLineId: "line-does-not-exist",
            TargetAreaId: "area-asm");

        Should.Throw<UnsMoveValidationException>(
            () => UnsImpactAnalyzer.Analyze(snapshot, move));
    }

    [Fact]
    public void LineMove_With_UnknownTarget_Throws_Validation()
    {
        var snapshot = TwoAreaSnapshot();
        var move = new UnsMoveOperation(
            UnsMoveKind.LineMove, "c1", "c1",
            SourceLineId: "line-oven",
            TargetAreaId: "area-nowhere");

        Should.Throw<UnsMoveValidationException>(
            () => UnsImpactAnalyzer.Analyze(snapshot, move));
    }

    [Fact]
    public void LineMove_To_Area_WithSameName_Warns_AboutAmbiguity()
    {
        var snapshot = new UnsTreeSnapshot
        {
            DraftGenerationId = 1,
            RevisionToken = new DraftRevisionToken("rev-1"),
            Areas =
            [
                new UnsAreaSummary("area-a", "Packaging", ["line-1"]),
                new UnsAreaSummary("area-b", "Assembly",  ["line-2"]),
            ],
            Lines =
            [
                new UnsLineSummary("line-1", "Oven", 10, 100),
                new UnsLineSummary("line-2", "Oven", 5, 50),
            ],
        };
        var move = new UnsMoveOperation(
            UnsMoveKind.LineMove, "c1", "c1",
            SourceLineId: "line-1",
            TargetAreaId: "area-b");

        var preview = UnsImpactAnalyzer.Analyze(snapshot, move);

        preview.CascadeWarnings.ShouldContain(w => w.Contains("already has a line named 'Oven'"));
    }

    [Fact]
    public void AreaRename_Cascades_AcrossAllLines()
    {
        var snapshot = TwoAreaSnapshot();
        var move = new UnsMoveOperation(
            Kind: UnsMoveKind.AreaRename,
            SourceClusterId: "c1", TargetClusterId: "c1",
            SourceAreaId: "area-pack",
            NewName: "Packaging-West");

        var preview = UnsImpactAnalyzer.Analyze(snapshot, move);

        preview.AffectedEquipmentCount.ShouldBe(14 + 3, "sum of lines in 'Packaging'");
        preview.AffectedTagCount.ShouldBe(237 + 40);
        preview.HumanReadableSummary.ShouldContain("'Packaging-West'");
    }

    [Fact]
    public void LineMerge_CrossArea_Warns()
    {
        var snapshot = TwoAreaSnapshot();
        var move = new UnsMoveOperation(
            Kind: UnsMoveKind.LineMerge,
            SourceClusterId: "c1", TargetClusterId: "c1",
            SourceLineId: "line-oven",
            TargetLineId: "line-weld");

        var preview = UnsImpactAnalyzer.Analyze(snapshot, move);

        preview.AffectedEquipmentCount.ShouldBe(14);
        preview.CascadeWarnings.ShouldContain(w => w.Contains("different areas"));
    }

    [Fact]
    public void LineMerge_SameArea_NoWarning()
    {
        var snapshot = TwoAreaSnapshot();
        var move = new UnsMoveOperation(
            Kind: UnsMoveKind.LineMerge,
            SourceClusterId: "c1", TargetClusterId: "c1",
            SourceLineId: "line-oven",
            TargetLineId: "line-wrap");

        var preview = UnsImpactAnalyzer.Analyze(snapshot, move);

        preview.CascadeWarnings.ShouldBeEmpty();
    }

    [Fact]
    public void DraftRevisionToken_Matches_OnEqualValues()
    {
        var a = new DraftRevisionToken("rev-1");
        var b = new DraftRevisionToken("rev-1");
        var c = new DraftRevisionToken("rev-2");

        a.Matches(b).ShouldBeTrue();
        a.Matches(c).ShouldBeFalse();
        a.Matches(null).ShouldBeFalse();
    }
}
