using IWS5.Core.Enums;
using IWS5.Core.Models.Processing;
using IWS5.Server.DataAccess;
using Microsoft.Data.SqlClient;
using ILogger = Serilog.ILogger;

namespace IWS5.Server.EventProcessors;

public class DecisionProcessor(ILogger logger, IConfiguration configuration) : EventProcessor(logger, configuration)
{
    public override async Task<bool> ProcessEventAsync(EventModel eventData, int processId)
    {
        Logger.Information(
            "Starting decision event processing for WorkItemId: {WorkItemId}, EventId: {EventId}, Action: {LastAction}",
            eventData.WorkItemId, eventData.CurrentEventId, eventData.LastAction);

        await using (var sqlConnection = new SqlConnection(ConnectionString))
        {
            await sqlConnection.OpenAsync();
            Logger.Debug("Database connection opened for decision processing");

            try
            {
                int ptr = eventData.LastAction.IndexOf(":");
                if (ptr == -1)
                {
                    Logger.Warning("Invalid LastAction format, no ':' separator found: {LastAction}",
                        eventData.LastAction);
                    throw new InvalidOperationException($"Invalid LastAction format: {eventData.LastAction}");
                }

                string lastActionDecision = eventData.LastAction.Substring(ptr + 1).ToLower();
                Logger.Debug("Extracted decision from LastAction: '{Decision}'", lastActionDecision);

                Logger.Debug(
                    "Looking up next event for ProcessId: {ProcessId}, EventName: {EventName}, Decision: {Decision}",
                    processId, eventData.CurrentEventName, lastActionDecision);

                var sqlStr =
                    @"SELECT NextEventId FROM ATEventDefinition With (NoWait)
                          where ProcessId = @currentProcessID
                            AND EventName = @currentEventName
                            And EventTypeId = @eventTypeId
                            And LOWER(Action) = @lastActionDecision";

                var decisionEventId = Repository.GetRecordValue(sqlConnection, sqlStr, new Dictionary<string, object>
                {
                    { "@currentProcessID", processId },
                    { "@currentEventName", eventData.CurrentEventName },
                    { "@eventTypeId", (int)ATEventType.Decision },
                    { "@lastActionDecision", lastActionDecision }
                });

                if (decisionEventId == null)
                {
                    Logger.Warning(
                        "No next event found for decision. ProcessId: {ProcessId}, EventName: {EventName}, Decision: {Decision}",
                        processId, eventData.CurrentEventName, lastActionDecision);
                    throw new InvalidOperationException(
                        $"No next event defined for decision '{lastActionDecision}' in event '{eventData.CurrentEventName}'");
                }

                Logger.Information("Found next event: {NextEventId} for decision: {Decision}", decisionEventId,
                    lastActionDecision);

                Logger.Debug("Updating work item reference to EventId: {NextEventId} for WorkItemId: {WorkItemId}",
                    decisionEventId, eventData.WorkItemId);

                const string updateSql = @"
                UPDATE ATWorkItemReference
                SET EventID = @nextEventId,
                    PendEventId = 0,
                    PendQueueId = null,
                    PendExpirationTime = NULL,
                    ServerRequest = @serverRequest,
                    ServerRequestParam = NULL,
                    LastActionDate = @lastActionDate,
                    SourceQueueId = NULL
                WHERE WorkItemID = @workItemId";

                using var cmd = new SqlCommand(updateSql, sqlConnection);
                cmd.Parameters.AddWithValue("@nextEventId", decisionEventId);
                cmd.Parameters.AddWithValue("@serverRequest", (int)ServerRequest.ForwardExistingWorkItem);
                cmd.Parameters.AddWithValue("@lastActionDate", DateTime.Now);
                cmd.Parameters.AddWithValue("@workItemId", eventData.WorkItemId);

                int rowsAffected = await cmd.ExecuteNonQueryAsync();

                if (rowsAffected == 0)
                {
                    Logger.Warning("No rows affected when updating work item reference for WorkItemId: {WorkItemId}",
                        eventData.WorkItemId);
                }
                else
                {
                    Logger.Debug("Successfully updated {RowsAffected} work item reference(s)", rowsAffected);
                }

                Logger.Information(
                    "Successfully completed decision event processing for WorkItemId: {WorkItemId}, moved to EventId: {NextEventId}",
                    eventData.WorkItemId, decisionEventId);

                return true;
            }
            catch (Exception ex)
            {
                Logger.Error(ex,
                    "Error processing decision event for WorkItemId: {WorkItemId}, EventId: {EventId}, LastAction: {LastAction}",
                    eventData.WorkItemId, eventData.CurrentEventId, eventData.LastAction);
                throw new InvalidOperationException($"Error processing decision event: {ex.Message}", ex);
            }
        }
    }
}