using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Backend.Galaxy;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Shared.Contracts;

namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Backend;

/// <summary>
///     Galaxy backend that uses the live <c>ZB</c> repository for <see cref="DiscoverAsync"/> —
///     real gobject hierarchy + attributes flow through to the Proxy without needing the MXAccess
///     COM client. Runtime data-plane calls (Read/Write/Subscribe/Alarm/History) still surface
///     as "MXAccess code lift pending" until the COM client port lands. This is the highest-value
///     intermediate state because Discover is what powers the OPC UA address-space build, so
///     downstream Proxy + parity tests can exercise the complete tree shape today.
/// </summary>
public sealed class DbBackedGalaxyBackend(GalaxyRepository repository) : IGalaxyBackend
{
    private long _nextSessionId;
    private long _nextSubscriptionId;

    // DB-only backend doesn't have a runtime data plane; never raises events.
#pragma warning disable CS0067
    public event System.EventHandler<OnDataChangeNotification>? OnDataChange;
    public event System.EventHandler<GalaxyAlarmEvent>? OnAlarmEvent;
    public event System.EventHandler<HostConnectivityStatus>? OnHostStatusChanged;
#pragma warning restore CS0067

    public Task<OpenSessionResponse> OpenSessionAsync(OpenSessionRequest req, CancellationToken ct)
    {
        var id = Interlocked.Increment(ref _nextSessionId);
        return Task.FromResult(new OpenSessionResponse { Success = true, SessionId = id });
    }

    public Task CloseSessionAsync(CloseSessionRequest req, CancellationToken ct) => Task.CompletedTask;

    public async Task<DiscoverHierarchyResponse> DiscoverAsync(DiscoverHierarchyRequest req, CancellationToken ct)
    {
        try
        {
            var hierarchy = await repository.GetHierarchyAsync(ct).ConfigureAwait(false);
            var attributes = await repository.GetAttributesAsync(ct).ConfigureAwait(false);

            // Group attributes by their owning gobject for the IPC payload.
            var attrsByGobject = attributes
                .GroupBy(a => a.GobjectId)
                .ToDictionary(g => g.Key, g => g.Select(MapAttribute).ToArray());

            var parentByChild = hierarchy
                .ToDictionary(o => o.GobjectId, o => o.ParentGobjectId);
            var nameByGobject = hierarchy
                .ToDictionary(o => o.GobjectId, o => o.TagName);

            var objects = hierarchy.Select(o => new GalaxyObjectInfo
            {
                ContainedName = string.IsNullOrEmpty(o.ContainedName) ? o.TagName : o.ContainedName,
                TagName = o.TagName,
                ParentContainedName = parentByChild.TryGetValue(o.GobjectId, out var p)
                                      && p != 0
                                      && nameByGobject.TryGetValue(p, out var pName)
                    ? pName
                    : null,
                TemplateCategory = MapCategory(o.CategoryId),
                Attributes = attrsByGobject.TryGetValue(o.GobjectId, out var a) ? a : System.Array.Empty<GalaxyAttributeInfo>(),
            }).ToArray();

            return new DiscoverHierarchyResponse { Success = true, Objects = objects };
        }
        catch (Exception ex) when (ex is System.Data.SqlClient.SqlException
                                   or InvalidOperationException
                                   or TimeoutException)
        {
            return new DiscoverHierarchyResponse
            {
                Success = false,
                Error = $"Galaxy ZB repository error: {ex.Message}",
                Objects = System.Array.Empty<GalaxyObjectInfo>(),
            };
        }
    }

    public Task<ReadValuesResponse> ReadValuesAsync(ReadValuesRequest req, CancellationToken ct)
        => Task.FromResult(new ReadValuesResponse
        {
            Success = false,
            Error = "MXAccess code lift pending (Phase 2 Task B.1) — DB-backed backend covers Discover only",
            Values = System.Array.Empty<GalaxyDataValue>(),
        });

    public Task<WriteValuesResponse> WriteValuesAsync(WriteValuesRequest req, CancellationToken ct)
    {
        var results = new WriteValueResult[req.Writes.Length];
        for (var i = 0; i < req.Writes.Length; i++)
        {
            results[i] = new WriteValueResult
            {
                TagReference = req.Writes[i].TagReference,
                StatusCode = 0x80020000u,
                Error = "MXAccess code lift pending (Phase 2 Task B.1)",
            };
        }
        return Task.FromResult(new WriteValuesResponse { Results = results });
    }

    public Task<SubscribeResponse> SubscribeAsync(SubscribeRequest req, CancellationToken ct)
    {
        var sid = Interlocked.Increment(ref _nextSubscriptionId);
        return Task.FromResult(new SubscribeResponse
        {
            Success = true,
            SubscriptionId = sid,
            ActualIntervalMs = req.RequestedIntervalMs,
        });
    }

    public Task UnsubscribeAsync(UnsubscribeRequest req, CancellationToken ct) => Task.CompletedTask;
    public Task SubscribeAlarmsAsync(AlarmSubscribeRequest req, CancellationToken ct) => Task.CompletedTask;
    public Task AcknowledgeAlarmAsync(AlarmAckRequest req, CancellationToken ct) => Task.CompletedTask;

    public Task<HistoryReadResponse> HistoryReadAsync(HistoryReadRequest req, CancellationToken ct)
        => Task.FromResult(new HistoryReadResponse
        {
            Success = false,
            Error = "MXAccess + Historian code lift pending (Phase 2 Task B.1)",
            Tags = System.Array.Empty<HistoryTagValues>(),
        });

    public Task<HistoryReadProcessedResponse> HistoryReadProcessedAsync(
        HistoryReadProcessedRequest req, CancellationToken ct)
        => Task.FromResult(new HistoryReadProcessedResponse
        {
            Success = false,
            Error = "MXAccess + Historian code lift pending (Phase 2 Task B.1)",
            Values = System.Array.Empty<GalaxyDataValue>(),
        });

    public Task<HistoryReadAtTimeResponse> HistoryReadAtTimeAsync(
        HistoryReadAtTimeRequest req, CancellationToken ct)
        => Task.FromResult(new HistoryReadAtTimeResponse
        {
            Success = false,
            Error = "MXAccess + Historian code lift pending (Phase 2 Task B.1)",
            Values = System.Array.Empty<GalaxyDataValue>(),
        });

    public Task<HistoryReadEventsResponse> HistoryReadEventsAsync(
        HistoryReadEventsRequest req, CancellationToken ct)
        => Task.FromResult(new HistoryReadEventsResponse
        {
            Success = false,
            Error = "MXAccess + Historian code lift pending (Phase 2 Task B.1)",
            Events = System.Array.Empty<GalaxyHistoricalEvent>(),
        });

    public Task<RecycleStatusResponse> RecycleAsync(RecycleHostRequest req, CancellationToken ct)
        => Task.FromResult(new RecycleStatusResponse { Accepted = true, GraceSeconds = 15 });

    private static GalaxyAttributeInfo MapAttribute(GalaxyAttributeRow row) => new()
    {
        AttributeName = row.AttributeName,
        MxDataType = row.MxDataType,
        IsArray = row.IsArray,
        ArrayDim = row.ArrayDimension is int d and > 0 ? (uint)d : null,
        SecurityClassification = row.SecurityClassification,
        IsHistorized = row.IsHistorized,
        IsAlarm = row.IsAlarm,
    };

    /// <summary>
    ///     Galaxy <c>template_definition.category_id</c> → human-readable name.
    ///     Mirrors v1 Host's <c>AlarmObjectFilter</c> mapping.
    /// </summary>
    private static string MapCategory(int categoryId) => categoryId switch
    {
        1 => "$WinPlatform",
        3 => "$AppEngine",
        4 => "$Area",
        10 => "$UserDefined",
        11 => "$ApplicationObject",
        13 => "$Area",
        17 => "$DeviceIntegration",
        24 => "$ViewEngine",
        26 => "$ViewApp",
        _ => $"category-{categoryId}",
    };
}
