using System;
using System.Collections.Generic;
using ZB.MOM.WW.MxGateway.Contracts;

namespace ZB.MOM.WW.MxGateway.Worker.Bootstrap;

/// <summary>
/// Parses worker command-line arguments and environment variables.
/// </summary>
public sealed class WorkerOptionsParser
{
    private const string SessionIdOptionName = "--session-id";
    private const string PipeNameOptionName = "--pipe-name";
    private const string ProtocolVersionOptionName = "--protocol-version";

    private readonly IWorkerEnvironment _environment;

    /// <summary>
    /// Initializes the parser with a worker environment.
    /// </summary>
    /// <param name="environment">Worker environment for reading configuration.</param>
    public WorkerOptionsParser(IWorkerEnvironment environment)
    {
        _environment = environment ?? throw new ArgumentNullException(nameof(environment));
    }

    /// <summary>
    /// Parses command-line arguments and returns bootstrap configuration or errors.
    /// </summary>
    /// <param name="args">Command-line arguments to parse.</param>
    /// <returns>Bootstrap result containing configuration or error messages.</returns>
    public WorkerBootstrapResult Parse(string[] args)
    {
        if (args is null)
        {
            throw new ArgumentNullException(nameof(args));
        }

        Dictionary<string, string> values = new(StringComparer.OrdinalIgnoreCase);
        List<string> errors = [];

        for (int index = 0; index < args.Length; index++)
        {
            string arg = args[index];
            if (!IsKnownOption(arg))
            {
                errors.Add($"Unknown option '{arg}'.");
                continue;
            }

            if (index + 1 >= args.Length || args[index + 1].StartsWith("--", StringComparison.Ordinal))
            {
                errors.Add($"Option '{arg}' requires a value.");
                continue;
            }

            values[arg] = args[index + 1];
            index++;
        }

        string? sessionId = ReadRequired(values, SessionIdOptionName, errors);
        string? pipeName = ReadRequired(values, PipeNameOptionName, errors);
        string? protocolVersionText = ReadRequired(values, ProtocolVersionOptionName, errors);

        if (errors.Count > 0)
        {
            return WorkerBootstrapResult.Failure(WorkerExitCode.InvalidArguments, errors);
        }

        if (!uint.TryParse(protocolVersionText, out uint protocolVersion)
            || protocolVersion != GatewayContractInfo.WorkerProtocolVersion)
        {
            return WorkerBootstrapResult.Failure(
                WorkerExitCode.InvalidProtocolVersion,
                [$"Unsupported protocol version '{protocolVersionText}'."]);
        }

        string? nonce = _environment.GetEnvironmentVariable(WorkerOptions.NonceEnvironmentVariableName);

        if (string.IsNullOrWhiteSpace(nonce))
        {
            return WorkerBootstrapResult.Failure(
                WorkerExitCode.MissingNonce,
                ["Required worker nonce environment variable is missing."]);
        }

        return WorkerBootstrapResult.Success(new WorkerOptions(
            sessionId!,
            pipeName!,
            protocolVersion,
            nonce!));
    }

    private static string? ReadRequired(
        IReadOnlyDictionary<string, string> values,
        string optionName,
        List<string> errors)
    {
        if (!values.TryGetValue(optionName, out string value)
            || string.IsNullOrWhiteSpace(value))
        {
            errors.Add($"Required option '{optionName}' is missing.");
            return null;
        }

        return value;
    }

    private static bool IsKnownOption(string optionName)
    {
        return optionName is SessionIdOptionName or PipeNameOptionName or ProtocolVersionOptionName;
    }
}
