using System;
using System.Collections.Generic;
using System.IO;
using ZB.MOM.WW.MxGateway.Worker.Bootstrap;
using ZB.MOM.WW.MxGateway.Worker.Ipc;

namespace ZB.MOM.WW.MxGateway.Worker;

/// <summary>Entry point for the worker process.</summary>
public static class WorkerApplication
{
    /// <summary>Initializes and runs the worker with default environment and logging.</summary>
    /// <param name="args">Command-line arguments.</param>
    /// <returns>The process exit code.</returns>
    public static int Run(string[] args)
    {
        return Run(
            args,
            new EnvironmentVariableWorkerEnvironment(),
            new WorkerConsoleLogger(Console.Error));
    }

    /// <summary>Initializes and runs the worker with custom environment and logging.</summary>
    /// <param name="args">Command-line arguments.</param>
    /// <param name="environment">Worker environment for resolving configuration.</param>
    /// <param name="logger">Worker logger for diagnostics.</param>
    /// <returns>The process exit code.</returns>
    public static int Run(
        string[] args,
        IWorkerEnvironment environment,
        IWorkerLogger logger)
    {
        return Run(
            args,
            environment,
            logger,
            new WorkerPipeClient(logger));
    }

    /// <summary>Parses arguments, bootstraps the handshake, and runs the worker until shutdown.</summary>
    /// <param name="args">Command-line arguments.</param>
    /// <param name="environment">Worker environment for resolving configuration.</param>
    /// <param name="logger">Worker logger for diagnostics.</param>
    /// <param name="pipeClient">Named pipe client for gateway communication.</param>
    /// <returns>The process exit code.</returns>
    public static int Run(
        string[] args,
        IWorkerEnvironment environment,
        IWorkerLogger logger,
        IWorkerPipeClient pipeClient)
    {
        if (args is null)
        {
            throw new ArgumentNullException(nameof(args));
        }

        if (environment is null)
        {
            throw new ArgumentNullException(nameof(environment));
        }

        if (logger is null)
        {
            throw new ArgumentNullException(nameof(logger));
        }

        if (pipeClient is null)
        {
            throw new ArgumentNullException(nameof(pipeClient));
        }

        try
        {
            WorkerOptionsParser parser = new(environment);
            WorkerBootstrapResult result = parser.Parse(args);

            if (!result.Succeeded)
            {
                logger.Error("WorkerBootstrapFailed", new Dictionary<string, object?>
                {
                    ["exit_code"] = result.ExitCode,
                    ["errors"] = string.Join(";", result.Errors),
                });

                return (int)result.ExitCode;
            }

            WorkerOptions options = result.Options
                ?? throw new InvalidOperationException("Successful bootstrap result did not include worker options.");

            logger.Information("WorkerBootstrapSucceeded", new Dictionary<string, object?>
            {
                ["session_id"] = options.SessionId,
                ["pipe_name"] = options.PipeName,
                ["protocol_version"] = options.ProtocolVersion,
                ["nonce"] = options.Nonce,
            });

            pipeClient.RunAsync(options).GetAwaiter().GetResult();

            logger.Information("WorkerPipeSessionCompleted", new Dictionary<string, object?>
            {
                ["session_id"] = options.SessionId,
                ["pipe_name"] = options.PipeName,
                ["protocol_version"] = options.ProtocolVersion,
            });

            return (int)WorkerExitCode.Success;
        }
        catch (WorkerFrameProtocolException exception)
        {
            logger.Error("WorkerPipeProtocolFailure", new Dictionary<string, object?>
            {
                ["exit_code"] = WorkerExitCode.ProtocolViolation,
                ["error_code"] = exception.ErrorCode,
                ["exception_type"] = exception.GetType().FullName,
            });

            return (int)WorkerExitCode.ProtocolViolation;
        }
        catch (Exception exception) when (exception is IOException or TimeoutException)
        {
            logger.Error("WorkerPipeConnectionFailed", new Dictionary<string, object?>
            {
                ["exit_code"] = WorkerExitCode.PipeConnectionFailed,
                ["exception_type"] = exception.GetType().FullName,
            });

            return (int)WorkerExitCode.PipeConnectionFailed;
        }
        catch (Exception exception)
        {
            logger.Error("WorkerBootstrapUnexpectedFailure", new Dictionary<string, object?>
            {
                ["exit_code"] = WorkerExitCode.UnexpectedFailure,
                ["exception_type"] = exception.GetType().FullName,
            });

            return (int)WorkerExitCode.UnexpectedFailure;
        }
    }
}
