using System.Data.Common;
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Notification;
using ZB.MOM.WW.ScadaBridge.Commons.Types;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Scripts;

namespace ZB.MOM.WW.ScadaBridge.ScriptAnalysis;

/// <summary>
/// A <b>compile-only</b> globals type that mirrors the real SiteRuntime
/// <c>ScriptGlobals</c> (+ <c>ScriptRuntimeContext</c> helper surface)
/// member-for-member, so a real instance / shared / on-trigger-handler script
/// BINDS against it at design time. It is NEVER executed — every member body
/// throws <see cref="NotSupportedException"/> or returns <c>default</c>. The
/// design-time deploy gate compiles candidate scripts against this type
/// via <see cref="RoslynScriptCompiler.Compile(string, Type, System.Collections.Generic.IEnumerable{Microsoft.CodeAnalysis.MetadataReference}, System.Collections.Generic.IEnumerable{string})"/>
/// to catch undefined symbols and signature mismatches without touching the
/// site runtime.
///
/// <para>
/// Keeping this surface faithful is enforced by the
/// <c>RoslynScriptCompilerTests</c> "representative real script" corpus — if a
/// member or signature drifts from the runtime <c>ScriptGlobals</c>, that test
/// fails.
/// </para>
/// </summary>
public sealed class ScriptCompileSurface
{
    private const string CompileOnly = "compile-only surface";

    /// <summary>Mirrors <c>ScriptGlobals.Instance</c>.</summary>
    public CompileInstance Instance { get; set; } = null!;

    /// <summary>Mirrors <c>ScriptGlobals.Parameters</c>.</summary>
    public ScriptParameters Parameters { get; set; } = new();

    /// <summary>Mirrors <c>ScriptGlobals.CancellationToken</c>.</summary>
    public CancellationToken CancellationToken { get; set; }

    /// <summary>Mirrors <c>ScriptGlobals.Alarm</c>.</summary>
    public AlarmContext? Alarm { get; set; }

    /// <summary>Mirrors <c>ScriptGlobals.Scope</c>.</summary>
    public ScriptScope Scope { get; set; } = ScriptScope.Root;

    /// <summary>Mirrors <c>ScriptGlobals.ExternalSystem</c> (delegates to Instance).</summary>
    public CompileExternalSystem ExternalSystem => Instance.ExternalSystem;

    /// <summary>Mirrors <c>ScriptGlobals.Database</c>.</summary>
    public CompileDatabase Database => Instance.Database;

    /// <summary>Mirrors <c>ScriptGlobals.Notify</c>.</summary>
    public CompileNotify Notify => Instance.Notify;

    /// <summary>Mirrors <c>ScriptGlobals.Scripts</c>.</summary>
    public CompileScripts Scripts => Instance.Scripts;

    /// <summary>Mirrors <c>ScriptGlobals.Attributes</c>.</summary>
    public CompileAttributeAccessor Attributes => throw new NotSupportedException(CompileOnly);

    /// <summary>Mirrors <c>ScriptGlobals.Children</c>.</summary>
    public CompileChildrenAccessor Children => throw new NotSupportedException(CompileOnly);

    /// <summary>Mirrors <c>ScriptGlobals.Parent</c>.</summary>
    public CompileCompositionAccessor? Parent => throw new NotSupportedException(CompileOnly);

    /// <summary>Mirrors <c>ScriptGlobals.Alarms</c>.</summary>
    public CompileAlarmsAccessor Alarms => throw new NotSupportedException(CompileOnly);

    /// <summary>Compile-only mirror of <c>ScriptRuntimeContext</c> (the <c>Instance</c> global).</summary>
    public sealed class CompileInstance
    {
        /// <summary>Mirrors <c>ScriptRuntimeContext.GetAttribute</c>.</summary>
        /// <param name="attributeName">The name of the attribute to read.</param>
        /// <returns>The attribute's current value.</returns>
        public Task<object?> GetAttribute(string attributeName) => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>ScriptRuntimeContext.SetAttribute</c>.</summary>
        /// <param name="attributeName">The name of the attribute to write.</param>
        /// <param name="value">The value to write to the attribute.</param>
        /// <returns>A task that represents the asynchronous operation.</returns>
        public Task SetAttribute(string attributeName, string value) => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>ScriptRuntimeContext.CallScript</c>.</summary>
        /// <param name="scriptName">The name of the script to invoke.</param>
        /// <param name="parameters">Optional parameters passed to the script.</param>
        /// <returns>The result returned by the invoked script.</returns>
        public Task<object?> CallScript(string scriptName, object? parameters = null) => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>ScriptRuntimeContext.ExternalSystem</c>.</summary>
        public CompileExternalSystem ExternalSystem => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>ScriptRuntimeContext.Database</c>.</summary>
        public CompileDatabase Database => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>ScriptRuntimeContext.Notify</c>.</summary>
        public CompileNotify Notify => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>ScriptRuntimeContext.Scripts</c>.</summary>
        public CompileScripts Scripts => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>ScriptRuntimeContext.Tracking</c>.</summary>
        public CompileTracking Tracking => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>ScriptRuntimeContext.Alarms</c>.</summary>
        public CompileAlarmsAccessor Alarms => throw new NotSupportedException(CompileOnly);
    }

    /// <summary>
    /// Compile-only mirror of <c>AlarmsAccessor</c> (MES alarm-status API §5.2).
    /// <see cref="CurrentAsync"/> returns the SAME <see cref="ScriptAlarm"/> type the runtime
    /// accessor returns — not a compile-only stand-in — so a script that reads
    /// <c>a.Severity</c> / <c>a.AckTime</c> binds identically at design time and at the site.
    /// </summary>
    public sealed class CompileAlarmsAccessor
    {
        /// <summary>Mirrors <c>AlarmsAccessor.CurrentAsync</c>.</summary>
        /// <param name="cancellationToken">Token used to cancel the read.</param>
        /// <returns>The instance's current alarm conditions.</returns>
        public Task<IReadOnlyList<ScriptAlarm>> CurrentAsync(CancellationToken cancellationToken = default)
            => throw new NotSupportedException(CompileOnly);
    }

    /// <summary>Compile-only mirror of <c>ScriptRuntimeContext.ExternalSystemHelper</c>.</summary>
    public sealed class CompileExternalSystem
    {
        /// <summary>Mirrors <c>ExternalSystemHelper.Call</c>.</summary>
        /// <param name="systemName">The name of the external system to call.</param>
        /// <param name="methodName">The name of the method to invoke on the external system.</param>
        /// <param name="parameters">Optional parameters passed to the call.</param>
        /// <param name="cancellationToken">Token used to cancel the call.</param>
        /// <returns>The result of the external system call.</returns>
        public Task<ExternalCallResult> Call(
            string systemName,
            string methodName,
            IReadOnlyDictionary<string, object?>? parameters = null,
            CancellationToken cancellationToken = default)
            => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>ExternalSystemHelper.CachedCall</c>.</summary>
        /// <param name="systemName">The name of the external system to call.</param>
        /// <param name="methodName">The name of the method to invoke on the external system.</param>
        /// <param name="parameters">Optional parameters passed to the call.</param>
        /// <param name="cancellationToken">Token used to cancel the call.</param>
        /// <returns>A tracking handle for the store-and-forward operation.</returns>
        public Task<TrackedOperationId> CachedCall(
            string systemName,
            string methodName,
            IReadOnlyDictionary<string, object?>? parameters = null,
            CancellationToken cancellationToken = default)
            => throw new NotSupportedException(CompileOnly);
    }

    /// <summary>Compile-only mirror of <c>ScriptRuntimeContext.DatabaseHelper</c>.</summary>
    public sealed class CompileDatabase
    {
        /// <summary>Mirrors <c>DatabaseHelper.Connection</c>.</summary>
        /// <param name="name">The name of the configured database connection.</param>
        /// <param name="cancellationToken">Token used to cancel the operation.</param>
        /// <returns>An open connection to the named database.</returns>
        public Task<DbConnection> Connection(string name, CancellationToken cancellationToken = default)
            => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>DatabaseHelper.CachedWrite</c>.</summary>
        /// <param name="name">The name of the configured database connection.</param>
        /// <param name="sql">The SQL statement to execute.</param>
        /// <param name="parameters">Optional parameters bound to the SQL statement.</param>
        /// <param name="cancellationToken">Token used to cancel the operation.</param>
        /// <returns>A tracking handle for the store-and-forward write.</returns>
        public Task<TrackedOperationId> CachedWrite(
            string name,
            string sql,
            IReadOnlyDictionary<string, object?>? parameters = null,
            CancellationToken cancellationToken = default)
            => throw new NotSupportedException(CompileOnly);
    }

    /// <summary>Compile-only mirror of <c>ScriptRuntimeContext.NotifyHelper</c>.</summary>
    public sealed class CompileNotify
    {
        /// <summary>Mirrors <c>NotifyHelper.To</c>.</summary>
        /// <param name="listName">The name of the notification list to target.</param>
        /// <returns>A target used to send a notification to the named list.</returns>
        public CompileNotifyTarget To(string listName) => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>NotifyHelper.Status</c>.</summary>
        /// <param name="notificationId">The identifier of the notification to look up.</param>
        /// <returns>The delivery status of the notification.</returns>
        public Task<NotificationDeliveryStatus> Status(string notificationId) => throw new NotSupportedException(CompileOnly);
    }

    /// <summary>Compile-only mirror of <c>ScriptRuntimeContext.NotifyTarget</c>.</summary>
    public sealed class CompileNotifyTarget
    {
        /// <summary>Mirrors <c>NotifyTarget.Send</c>.</summary>
        /// <param name="subject">The notification subject.</param>
        /// <param name="message">The notification message body.</param>
        /// <param name="cancellationToken">Token used to cancel the operation.</param>
        /// <returns>The identifier of the enqueued notification.</returns>
        public Task<string> Send(string subject, string message, CancellationToken cancellationToken = default)
            => throw new NotSupportedException(CompileOnly);
    }

    /// <summary>Compile-only mirror of <c>ScriptRuntimeContext.ScriptCallHelper</c>.</summary>
    public sealed class CompileScripts
    {
        /// <summary>Mirrors <c>ScriptCallHelper.CallShared</c>.</summary>
        /// <param name="scriptName">The name of the shared script to invoke.</param>
        /// <param name="parameters">Optional parameters passed to the script.</param>
        /// <param name="cancellationToken">Token used to cancel the operation.</param>
        /// <returns>The result returned by the invoked shared script.</returns>
        public Task<object?> CallShared(
            string scriptName,
            object? parameters = null,
            CancellationToken cancellationToken = default)
            => throw new NotSupportedException(CompileOnly);
    }

    /// <summary>Compile-only mirror of <c>ScriptRuntimeContext.TrackingHelper</c>.</summary>
    public sealed class CompileTracking
    {
        /// <summary>Mirrors <c>TrackingHelper.Status</c>.</summary>
        /// <param name="trackedOperationId">The tracking handle returned by a cached call or write.</param>
        /// <param name="cancellationToken">Token used to cancel the operation.</param>
        /// <returns>The current tracking status snapshot, or <c>null</c> if not found.</returns>
        public Task<TrackingStatusSnapshot?> Status(
            TrackedOperationId trackedOperationId,
            CancellationToken cancellationToken = default)
            => throw new NotSupportedException(CompileOnly);
    }

    /// <summary>Compile-only mirror of <c>AttributeAccessor</c>.</summary>
    public sealed class CompileAttributeAccessor
    {
        /// <summary>Mirrors <c>AttributeAccessor.this[string]</c>.</summary>
        /// <param name="key">The name of the attribute to get or set.</param>
        public object? this[string key]
        {
            get => throw new NotSupportedException(CompileOnly);
            set => throw new NotSupportedException(CompileOnly);
        }

        /// <summary>Mirrors <c>AttributeAccessor.GetAsync</c>.</summary>
        /// <param name="key">The name of the attribute to read.</param>
        /// <returns>The attribute's current value.</returns>
        public Task<object?> GetAsync(string key) => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>AttributeAccessor.SetAsync</c>.</summary>
        /// <param name="key">The name of the attribute to write.</param>
        /// <param name="value">The value to write to the attribute.</param>
        /// <returns>A task that represents the asynchronous operation.</returns>
        public Task SetAsync(string key, object? value) => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>AttributeAccessor.Resolve</c>.</summary>
        /// <param name="key">The name of the attribute to resolve.</param>
        /// <returns>The resolved attribute path.</returns>
        public string Resolve(string key) => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>AttributeAccessor.WaitAsync</c>.</summary>
        /// <param name="key">The name of the attribute to wait on.</param>
        /// <param name="targetValue">The value to wait for.</param>
        /// <param name="timeout">The maximum time to wait.</param>
        /// <param name="requireGoodQuality">Whether the attribute must also have good quality before the wait succeeds.</param>
        /// <returns><c>true</c> if the target value was observed before the timeout elapsed; otherwise <c>false</c>.</returns>
        public Task<bool> WaitAsync(string key, object? targetValue, TimeSpan timeout, bool requireGoodQuality = false) => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>AttributeAccessor.WaitAsync</c>.</summary>
        /// <param name="key">The name of the attribute to wait on.</param>
        /// <param name="predicate">A predicate evaluated against successive attribute values.</param>
        /// <param name="timeout">The maximum time to wait.</param>
        /// <param name="requireGoodQuality">Whether the attribute must also have good quality before the wait succeeds.</param>
        /// <returns><c>true</c> if the predicate was satisfied before the timeout elapsed; otherwise <c>false</c>.</returns>
        public Task<bool> WaitAsync(string key, Func<object?, bool> predicate, TimeSpan timeout, bool requireGoodQuality = false) => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>AttributeAccessor.WaitForAsync</c>.</summary>
        /// <param name="key">The name of the attribute to wait on.</param>
        /// <param name="targetValue">The value to wait for.</param>
        /// <param name="timeout">The maximum time to wait.</param>
        /// <param name="requireGoodQuality">Whether the attribute must also have good quality before the wait succeeds.</param>
        /// <returns>The outcome of the wait, including whether it succeeded or timed out.</returns>
        public Task<WaitResult> WaitForAsync(string key, object? targetValue, TimeSpan timeout, bool requireGoodQuality = false) => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>AttributeAccessor.WaitForAsync</c>.</summary>
        /// <param name="key">The name of the attribute to wait on.</param>
        /// <param name="predicate">A predicate evaluated against successive attribute values.</param>
        /// <param name="timeout">The maximum time to wait.</param>
        /// <param name="requireGoodQuality">Whether the attribute must also have good quality before the wait succeeds.</param>
        /// <returns>The outcome of the wait, including whether it succeeded or timed out.</returns>
        public Task<WaitResult> WaitForAsync(string key, Func<object?, bool> predicate, TimeSpan timeout, bool requireGoodQuality = false) => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>AttributeAccessor.WriteBatchAndWaitAsync</c>.</summary>
        /// <param name="values">The set of attribute values to write as a batch.</param>
        /// <param name="flagKey">The name of the flag attribute signaling the write.</param>
        /// <param name="flagValue">The value written to the flag attribute.</param>
        /// <param name="responseKey">The name of the attribute to observe for a response.</param>
        /// <param name="responseValue">The value to wait for on the response attribute.</param>
        /// <param name="timeout">The maximum time to wait for the response.</param>
        /// <returns><c>true</c> if the response value was observed before the timeout elapsed; otherwise <c>false</c>.</returns>
        public Task<bool> WriteBatchAndWaitAsync(IReadOnlyDictionary<string, object?> values, string flagKey, object? flagValue, string responseKey, object? responseValue, TimeSpan timeout) => throw new NotSupportedException(CompileOnly);
    }

    /// <summary>Compile-only mirror of <c>ChildrenAccessor</c>.</summary>
    public sealed class CompileChildrenAccessor
    {
        /// <summary>Mirrors <c>ChildrenAccessor.this[string]</c>.</summary>
        /// <param name="compositionName">The name of the composed child to access.</param>
        public CompileCompositionAccessor this[string compositionName] => throw new NotSupportedException(CompileOnly);
    }

    /// <summary>Compile-only mirror of <c>CompositionAccessor</c>.</summary>
    public sealed class CompileCompositionAccessor
    {
        /// <summary>Mirrors <c>CompositionAccessor.Attributes</c>.</summary>
        public CompileAttributeAccessor Attributes => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>CompositionAccessor.CallScript</c>.</summary>
        /// <param name="scriptName">The name of the script to invoke.</param>
        /// <param name="parameters">Optional parameters passed to the script.</param>
        /// <returns>The result returned by the invoked script.</returns>
        public Task<object?> CallScript(string scriptName, object? parameters = null) => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>CompositionAccessor.ResolveScript</c>.</summary>
        /// <param name="scriptName">The name of the script to resolve.</param>
        /// <returns>The resolved script path.</returns>
        public string ResolveScript(string scriptName) => throw new NotSupportedException(CompileOnly);

        /// <summary>Mirrors <c>CompositionAccessor.Path</c>.</summary>
        public string Path => throw new NotSupportedException(CompileOnly);
    }
}
