using System.Text.Json;
using ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.Cluster;

namespace ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.Deployment;

/// <summary>
/// <see cref="IAsyncLifetime"/> fixture for the InstanceConfigure E2E tests. Provisions, on the real
/// running <c>site-a</c>: a zztest template with a single bindable <c>Double</c> attribute, a zztest
/// data-connection (so the bindings UI has a connection to assign), a zztest area (for the
/// area-reassignment test), and one instance created with no area. The instance is NOT deployed —
/// bindings/overrides/area assignment are pre-deploy configuration operations.
///
/// <para>
/// <b>Why the attribute is created with <c>--data-source "Value"</c>:</b>
/// <c>InstanceConfigure.razor.cs</c> populates <c>_bindingDataSourceAttrs</c> by filtering
/// <c>GetAttributesByTemplateIdAsync</c> to attributes where
/// <c>!string.IsNullOrEmpty(a.DataSourceReference)</c> (line 581 of InstanceConfigure.razor).
/// A plain <c>Double</c> attribute created <em>without</em> <c>--data-source</c> has
/// <c>DataSourceReference = null</c>, so it would not appear in the Connection Bindings panel
/// and the bindings-UI tests would silently see "No data-sourced attributes". The attribute
/// DOES appear unconditionally in <c>_overrideAttrs</c> (filtered only on <c>!IsLocked</c>,
/// line 592), so overrides work with or without a tag path. The fixture therefore sets
/// <c>dataSourceReference: "Value"</c> when adding the attribute so both panels are exercised.
/// </para>
/// </summary>
public sealed class InstanceConfigureFixture : IAsyncLifetime
{
    private const string SiteAIdentifier = "site-a";

    public int SiteAId { get; private set; }
    public int TemplateId { get; private set; }
    public int ConnectionId { get; private set; }
    public int AreaId { get; private set; }
    public int InstanceId { get; private set; }

    /// <summary>The single bindable/overridable attribute name on the fixture template.</summary>
    public string AttributeName => "Value";

    /// <summary>The single non-locked HiLo alarm on the fixture template (for the merge override test).</summary>
    public string AlarmName => "HiHi";

    /// <summary>
    /// A second, non-HiLo (ValueMatch) alarm on the fixture template. HiLo overrides MERGE into
    /// inherited setpoints, but ValueMatch / RangeViolation / RateOfChange overrides WHOLE-REPLACE
    /// the inherited trigger config — so this alarm exercises the whole-replace path that the HiLo
    /// alarm cannot. Bound to the same <see cref="AttributeName"/> (<c>Value</c>) so the
    /// AlarmTriggerEditor's attribute picker already has it selected.
    /// </summary>
    public string NonHiLoAlarmName => "Match";

    /// <summary>Trigger type of <see cref="NonHiLoAlarmName"/>.</summary>
    public string NonHiLoAlarmTriggerType => "ValueMatch";

    /// <summary>The inherited (template) ValueMatch value baked into <see cref="NonHiLoAlarmName"/>.</summary>
    public string NonHiLoInheritedMatchValue => "100";

    /// <summary>The fixture data-connection name (for locating it in the bindings UI dropdown).</summary>
    public string ConnectionName { get; private set; } = string.Empty;

    public bool Available { get; private set; }

    public async Task InitializeAsync()
    {
        Available = await ClusterAvailability.IsAvailableAsync();
        if (!Available)
        {
            return;
        }

        try
        {
            SiteAId = await CliRunner.ResolveSiteIdAsync(SiteAIdentifier);
            TemplateId = await CliRunner.CreateTemplateAsync(CliRunner.UniqueName("cfgtmpl"));
            // The attribute must have DataSourceReference set (via --data-source) so it appears
            // in _bindingDataSourceAttrs on the InstanceConfigure page. Without it the Connection
            // Bindings panel shows "No data-sourced attributes" and binding tests cannot run.
            // See the class-level XML doc for the full analysis.
            await CliRunner.AddAttributeAsync(TemplateId, AttributeName, "Double", dataSourceReference: AttributeName);
            await CliRunner.AddAlarmAsync(TemplateId, AlarmName, "HiLo", priority: 500,
                attribute: AttributeName, hi: 80, hiHi: 95);
            // Second, non-HiLo alarm so the whole-replace override path is covered (HiLo merges,
            // ValueMatch/RangeViolation/RateOfChange whole-replace). Bound to the same attribute.
            await CliRunner.AddAlarmAsync(TemplateId, NonHiLoAlarmName, NonHiLoAlarmTriggerType,
                priority: 400, attribute: AttributeName, matchValue: NonHiLoInheritedMatchValue);
            ConnectionName = CliRunner.UniqueName("conn");
            ConnectionId = await CliRunner.CreateDataConnectionAsync(SiteAId, ConnectionName);
            AreaId = await CliRunner.CreateAreaAsync(SiteAId, CliRunner.UniqueName("cfgarea"));
            InstanceId = await CliRunner.CreateInstanceAsync(CliRunner.UniqueName("cfginst"), TemplateId, SiteAId);
        }
        catch
        {
            await SafeCleanupAsync();
            Available = false;
            throw;
        }
    }

    public async Task DisposeAsync()
    {
        if (!Available)
        {
            return;
        }

        await SafeCleanupAsync();
    }

    private async Task SafeCleanupAsync()
    {
        await CliRunner.DeleteInstanceAsync(InstanceId);
        await CliRunner.DeleteDataConnectionAsync(ConnectionId);
        await CliRunner.DeleteAreaAsync(AreaId);
        await CliRunner.DeleteTemplateAsync(TemplateId);
    }
}
