<?php

namespace WPQT\Automation;

if (!defined('ABSPATH')) {
    exit;
}

use WPQT\Log\LogService;
use WPQT\Services\ServiceLocator;

if (!class_exists('WPQT\Automation\AutomationService')) {
    class AutomationService
    {
        /**
         * Handles the execution of automations based on the provided parameters.
         *
         * @param int $boardId The ID of the board where the automations are defined.
         * @param int|null $targetId The ID of the target entity for the automation.
         * @param string $targetType The type of the target entity (e.g., 'task', 'stage').
         * @param string $automationTrigger The trigger that initiates the automation (e.g., 'onCreate', 'onUpdate').
         * @param object|null $data Additional data.
         *
         * @return object An object containing two arrays:
         *                - 'executedAutomations': An array of successfully executed automations.
         *                - 'failedAutomations': An array of automations that failed to execute.
         */
        public function handleAutomations($boardId, $targetId, $targetType, $automationTrigger, $data = null)
        {
            $executedAutomations = [];
            $failedAutomations = [];
            $rerunTriggers = [];
            $rerunCounter = 0;
            $maxRerunCounter = 2;

            $results = $this->processAutomations($boardId, $targetId, $targetType, $automationTrigger, $data);

            $executedAutomations = array_merge($executedAutomations, $results->executedAutomations);
            $failedAutomations = array_merge($failedAutomations, $results->failedAutomations);
            $rerunTriggers = array_merge($rerunTriggers, $results->rerunTriggers);

            while (!empty($rerunTriggers) && $rerunCounter < $maxRerunCounter) {
                $rerunCounter++;
                $currentRerunTriggers = $rerunTriggers;
                $rerunTriggers = []; // Reset for the next iteration

                foreach ($currentRerunTriggers as $trigger) {
                    $rerunResults = $this->processAutomations(
                        $trigger->boardId,
                        $trigger->targetId,
                        $trigger->targetType,
                        $trigger->automationTrigger,
                        $trigger->data ?? null
                    );

                    // Merge results from this rerun iteration
                    $executedAutomations = array_merge($executedAutomations, $rerunResults->executedAutomations);
                    $failedAutomations = array_merge($failedAutomations, $rerunResults->failedAutomations);
                    $rerunTriggers = array_merge($rerunTriggers, $rerunResults->rerunTriggers);
                }
            }

            return (object) [
                'executedAutomations' => $executedAutomations,
                'failedAutomations'   => $failedAutomations,
            ];
        }

        /**
         * Processes automations based on the provided parameters.
         *
         * This method retrieves active automations related to the specified board, target,
         * and trigger, and attempts to execute them. It logs any failures and returns
         * the results of the executed and failed automations.
         *
         * @param int $boardId The ID of the board associated with the automations.
         * @param int $targetId The ID of the target entity for the automations.
         * @param string $targetType The type of the target entity (e.g., task, project).
         * @param string $automationTrigger The trigger that initiates the automations.
         * @param mixed|null $data Optional additional data to pass to the automation execution.
         *
         * @return object An object containing:
         *                - `executedAutomations` (array): List of successfully executed automations.
         *                - `failedAutomations` (array): List of automations that failed to execute.
         *                - `rerunTriggers` (array): Parameters for rerunning automations.
         *
         * @throws \Throwable If an unexpected error occurs during automation execution.
         */
        public function processAutomations($boardId, $targetId, $targetType, $automationTrigger, $data = null)
        {
            $executedAutomations = [];
            $failedAutomations = [];
            $rerunTriggers = [];

            $relatedAutomations = ServiceLocator::get('AutomationRepository')->getActiveAutomations($boardId, $targetId, $targetType, $automationTrigger);
            $userId = get_current_user_id();
            $logService = ServiceLocator::get('LogService');
            $shouldLog = $logService->shouldLog($boardId, LogService::SOURCE_AUTOMATION);

            if (!empty($relatedAutomations)) {
                foreach ($relatedAutomations as $automation) {
                    try {
                        $result = $this->executeAutomation($automation, $targetId, $data);

                        if ($result) {
                            if (is_object($result)) {
                                $automation->executionResult = $result->data ?? true;
                                $rerunTriggers = array_merge($rerunTriggers, $result->rerunTriggers ?? []);
                            } else {
                                $automation->executionResult = $result;
                            }
                            if ($shouldLog) {
                                $logMessage = $this->getAutomationLogMessage($automation);
                                $logService->log($logMessage, [
                                    'type'          => $targetType,
                                    'type_id'       => $targetId,
                                    'log_status'    => WP_QT_LOG_STATUS_SUCCESS,
                                    'created_by'    => WP_QT_LOG_CREATED_BY_AUTOMATION,
                                    'created_by_id' => $automation->id,
                                    'pipeline_id'   => $boardId,
                                ]);
                            }
                            $executedAutomations[] = $automation;
                        }
                    } catch (\Throwable $e) {
                        $failedAutomations[] = $automation;
                        if ($shouldLog) {
                            $failureLogMessage = $this->getAutomationFailedLogMessage($automation) . ': ' . $e->getMessage();
                            $logService->log($failureLogMessage, [
                                'type'          => $targetType,
                                'type_id'       => $targetId,
                                'log_status'    => WP_QT_LOG_STATUS_ERROR,
                                'created_by'    => WP_QT_LOG_CREATED_BY_AUTOMATION,
                                'created_by_id' => $automation->id,
                                'pipeline_id'   => $boardId,
                            ]);
                        }
                    }
                }
            }

            return (object) [
                'executedAutomations' => $executedAutomations,
                'failedAutomations'   => $failedAutomations,
                'rerunTriggers'       => $rerunTriggers,
            ];
        }

        private function executeAutomation($automation, $targetId, $data)
        {
            if ($this->isTaskDoneTrigger($automation)) {
                if ($this->isArchiveTaskAction($automation)) {
                    ServiceLocator::get('TaskService')->archiveTask($targetId, true);

                    return true;
                }
                if ($this->isSlackMessageAction($automation)) {
                    $task = ServiceLocator::get('TaskRepository')->getTaskById($targetId);
                    $webhookUrl = $automation->metadata;
                    $userId = $data->doneByUserId ?? null;
                    $apiToken = $data->doneByApiToken ?? null;
                    $marketDoneBy = 'Unknown';

                    if (null !== $userId) {
                        $user = ServiceLocator::get('UserRepository')->getUserByIdAndType($userId, WP_QT_WORDPRESS_USER_TYPE);
                        $marketDoneBy = $user->name;
                    } elseif (null !== $apiToken) {
                        $marketDoneBy = ServiceLocator::get('ApiTokenRepository')->getApiTokenName($apiToken);
                    }

                    $message = "Task *{$task->name}* marked as done by *{$marketDoneBy}*";
                    ServiceLocator::get('SlackService')->sendMessage($webhookUrl, $message, [], true);

                    return true;
                }
            }

            if ($this->isTaskNotDoneTrigger($automation)) {
                if ($this->isArchiveTaskAction($automation)) {
                    ServiceLocator::get('TaskService')->archiveTask($targetId, false);

                    return true;
                }
            }

            if ($this->isTaskCreatedTrigger($automation)) {
                if ($this->isAssignUserAction($automation)) {
                    $userId = $automation->automation_action_target_id;
                    $userType = $automation->automation_action_target_type;
                    $user = ServiceLocator::get('UserRepository')->getUserByIdAndType($userId, $userType);
                    $task = ServiceLocator::get('TaskRepository')->getTaskById($targetId);

                    ServiceLocator::get('UserService')->assignTaskToUser($userId, $targetId, $userType);

                    return (object) [
                       'success' => true,
                       'data'    => (object) [
                           'user' => $user,
                           'task' => $task
                       ],
                    ];
                }

                if ($this->isNewEntityEmailAction($automation)) {
                    $email = $automation->metadata;
                    $task = ServiceLocator::get('TaskRepository')->getTaskById($targetId);
                    $pipeline = ServiceLocator::get('PipelineRepository')->getPipelineById($task->pipeline_id);
                    $templateData = [
                        'taskName'     => $task->name,
                        'boardName'    => $pipeline->name,
                        'creationDate' => ServiceLocator::get('TimeRepository')->convertUTCToLocal($task->created_at)
                    ];
                    $emailMessage = ServiceLocator::get('EmailService')->renderTemplate(WP_QUICKTASKER_NEW_TASK_EMAIL_TEMPLATE, $templateData);
                    ServiceLocator::get('EmailService')->sendEmail($email, 'New Task Created', $emailMessage);

                    return true;
                }

                if ($this->isSlackMessageAction($automation)) {
                    $task = ServiceLocator::get('TaskRepository')->getTaskById($targetId);
                    $webhookUrl = $automation->metadata;
                    $message = "Task *{$task->name}* created";
                    ServiceLocator::get('SlackService')->sendMessage($webhookUrl, $message, [], true);

                    return true;
                }
            }

            if ($this->isTaskDeletedTrigger($automation)) {
                $deletedTask = $data->deletedTask;
                $deletedByUserId = $data->deletedByUserId ?? null;
                $deletedByApiToken = $data->deletedByApiToken ?? null;
                $deletedByName = 'Unknown';

                if (null !== $deletedByUserId) {
                    $deletedByUser = ServiceLocator::get('UserRepository')->getUserByIdAndType($deletedByUserId, WP_QT_WORDPRESS_USER_TYPE);
                    $deletedByName = $deletedByUser->name;
                } elseif (null !== $deletedByApiToken) {
                    $deletedByName = 'Api token: ' . ServiceLocator::get('ApiTokenRepository')->getApiTokenName($deletedByApiToken);
                }

                if ($this->isDeletedEntityEmailAction($automation)) {
                    $email = $automation->metadata;
                    $pipeline = ServiceLocator::get('PipelineRepository')->getPipelineById($deletedTask->pipeline_id);
                    $templateData = [
                        'taskName'          => $deletedTask->name,
                        'boardName'         => $pipeline->name,
                        'deleteDate'        => ServiceLocator::get('TimeRepository')->getLocalTime(),
                        'deletedByUserName' => $deletedByName
                    ];
                    $emailMessage = ServiceLocator::get('EmailService')->renderTemplate(WP_QUICKTASKER_DELETED_TASK_EMAIL_TEMPLATE, $templateData);
                    ServiceLocator::get('EmailService')->sendEmail($email, 'Task Deleted', $emailMessage);

                    return true;
                }

                if ($this->isSlackMessageAction($automation)) {
                    $webhookUrl = $automation->metadata;
                    $message = "Task *{$deletedTask->name}* deleted by *{$deletedByName}*";
                    ServiceLocator::get('SlackService')->sendMessage($webhookUrl, $message, [], true);

                    return true;
                }
            }

            if ($this->isTaskAssignedTrigger($automation)) {
                $assignedUser = $data;
                $task = ServiceLocator::get('TaskRepository')->getTaskById($targetId);

                if ($this->isTaskAssignedEmailAction($automation)) {
                    $email = $automation->metadata;
                    $pipeline = ServiceLocator::get('PipelineRepository')->getPipelineById($task->pipeline_id);

                    $templateData = [
                        'taskName'     => $task->name,
                        'boardName'    => $pipeline->name,
                        'assignedDate' => ServiceLocator::get('TimeRepository')->getLocalTime(),
                        'userName'     => $assignedUser->name,
                    ];

                    $emailMessage = ServiceLocator::get('EmailService')->renderTemplate(WP_QUICKTASKER_ASSIGNED_TASK_EMAIL_TEMPLATE, $templateData);
                    ServiceLocator::get('EmailService')->sendEmail($email, 'Task Assigned', $emailMessage);

                    return true;
                }

                if ($this->isSlackMessageAction($automation)) {
                    $webhookUrl = $automation->metadata;
                    $message = "Task *{$task->name}* assigned to *{$assignedUser->name}*";
                    ServiceLocator::get('SlackService')->sendMessage($webhookUrl, $message, [], true);

                    return true;
                }
            }

            if ($this->isTaskUnassignedTrigger($automation)) {
                $unassignedUser = $data;
                $task = ServiceLocator::get('TaskRepository')->getTaskById($targetId);

                if ($this->isTaskUnassignedEmailAction($automation)) {
                    $email = $automation->metadata;
                    $pipeline = ServiceLocator::get('PipelineRepository')->getPipelineById($task->pipeline_id);

                    $templateData = [
                        'taskName'       => $task->name,
                        'boardName'      => $pipeline->name,
                        'unassignedDate' => ServiceLocator::get('TimeRepository')->getLocalTime(),
                        'userName'       => $unassignedUser->name,
                    ];

                    $emailMessage = ServiceLocator::get('EmailService')->renderTemplate(WP_QUICKTASKER_UNASSIGNED_TASK_EMAIL_TEMPLATE, $templateData);
                    ServiceLocator::get('EmailService')->sendEmail($email, 'Task Unassigned', $emailMessage);

                    return true;
                }

                if ($this->isSlackMessageAction($automation)) {
                    $webhookUrl = $automation->metadata;
                    $message = "Task *{$task->name}* has been unassigned from *{$unassignedUser->name}*";
                    ServiceLocator::get('SlackService')->sendMessage($webhookUrl, $message, [], true);

                    return true;
                }
            }

            if ($this->isTaskPublicCommentAddedTrigger($automation)) {
                $comment = $data;
                $task = ServiceLocator::get('TaskRepository')->getTaskById($targetId);
                $authorUserType = $comment->author_type;
                $commentAuthor = ServiceLocator::get('UserRepository')->getUserByIdAndType($comment->author_id, $authorUserType);

                if ($this->isTaskPublicCommentAddedEmailAction($automation)) {
                    $email = $automation->metadata;
                    $pipeline = ServiceLocator::get('PipelineRepository')->getPipelineById($task->pipeline_id);
                    $templateData = [
                        'taskName'     => $task->name,
                        'boardName'    => $pipeline->name,
                        'creationDate' => ServiceLocator::get('TimeRepository')->convertUTCToLocal($comment->created_at),
                        'message'      => $comment->text,
                        'authorName'   => $commentAuthor->name
                    ];

                    $emailMessage = ServiceLocator::get('EmailService')->renderTemplate(WP_QUICKTASKER_TASK_NEW_PUBLIC_COMMENT_EMAIL_TEMPLATE, $templateData);
                    ServiceLocator::get('EmailService')->sendEmail($email, 'Public Comment Added', $emailMessage);

                    return true;
                }

                if ($this->isSlackMessageAction($automation)) {
                    $webhookUrl = $automation->metadata;
                    $message = "Task *{$task->name}* has a new public comment by *{$commentAuthor->name}*\n {$comment->text}";
                    ServiceLocator::get('SlackService')->sendMessage($webhookUrl, $message, [], true);

                    return true;
                }
            }

            if ($this->isTaskPrivateCommentAddedTrigger($automation)) {
                $comment = $data;
                $task = ServiceLocator::get('TaskRepository')->getTaskById($targetId);
                $commentAuthor = ServiceLocator::get('UserRepository')->getUserByIdAndType($comment->author_id, WP_QT_WORDPRESS_USER_TYPE);

                if ($this->isTaskPrivateCommentAddedEmailAction($automation)) {
                    $email = $automation->metadata;
                    $pipeline = ServiceLocator::get('PipelineRepository')->getPipelineById($task->pipeline_id);
                    $templateData = [
                        'taskName'     => $task->name,
                        'boardName'    => $pipeline->name,
                        'creationDate' => ServiceLocator::get('TimeRepository')->convertUTCToLocal($comment->created_at),
                        'message'      => $comment->text,
                        'authorName'   => $commentAuthor->name
                    ];

                    $emailMessage = ServiceLocator::get('EmailService')->renderTemplate(WP_QUICKTASKER_TASK_NEW_PRIVATE_COMMENT_EMAIL_TEMPLATE, $templateData);
                    ServiceLocator::get('EmailService')->sendEmail($email, 'Private Comment Added', $emailMessage);

                    return true;
                }

                if ($this->isSlackMessageAction($automation)) {
                    $webhookUrl = $automation->metadata;
                    $message = "Task *{$task->name}* has a new private comment by *{$commentAuthor->name}*\n {$comment->text}";
                    ServiceLocator::get('SlackService')->sendMessage($webhookUrl, $message, [], true);

                    return true;
                }
            }

            if ($this->isTaskAttachementAddedTrigger($automation)) {
                if ($this->isTaskAttachmentAddedEmailAction($automation)) {
                    $email = $automation->metadata;
                    $upload = $data->upload;
                    $userId = $data->userId;
                    $task = ServiceLocator::get('TaskRepository')->getTaskById($targetId);
                    $pipeline = ServiceLocator::get('PipelineRepository')->getPipelineById($task->pipeline_id);
                    $user = ServiceLocator::get('UserRepository')->getUserByIdAndType($userId, WP_QT_WORDPRESS_USER_TYPE);

                    $templateData = [
                        'taskName'     => $task->name,
                        'boardName'    => $pipeline->name,
                        'creationDate' => ServiceLocator::get('TimeRepository')->convertUTCToLocal($upload->created_at),
                        'fileName'     => $upload->file_name,
                        'uploaderName' => $user->name
                    ];
                    $emailMessage = ServiceLocator::get('EmailService')->renderTemplate(WP_QUICKTASKER_TASK_NEW_ATTACHMENT_EMAIL_TEMPLATE, $templateData);
                    ServiceLocator::get('EmailService')->sendEmail($email, 'Task attachment added', $emailMessage);

                    return true;
                }
            }

            if ($this->isTaskAttachmentDeletedTrigger($automation)) {
                if ($this->isTaskAttachmentDeletedEmailAction($automation)) {
                    $email = $automation->metadata;
                    $upload = $data->deletedUpload;
                    $userId = $data->userId;
                    $task = ServiceLocator::get('TaskRepository')->getTaskById($targetId);
                    $pipeline = ServiceLocator::get('PipelineRepository')->getPipelineById($task->pipeline_id);
                    $user = ServiceLocator::get('UserRepository')->getUserByIdAndType($userId, WP_QT_WORDPRESS_USER_TYPE);

                    $templateData = [
                        'taskName'    => $task->name,
                        'boardName'   => $pipeline->name,
                        'deleteDate'  => ServiceLocator::get('TimeRepository')->convertUTCToLocal($upload->deleted_at),
                        'fileName'    => $upload->file_name,
                        'deleterName' => $user->name
                    ];

                    $emailMessage = ServiceLocator::get('EmailService')->renderTemplate(WP_QUICKTASKER_TASK_ATTACHMENT_DELETED_EMAIL_TEMPLATE, $templateData);
                    ServiceLocator::get('EmailService')->sendEmail($email, 'Task attachment deleted', $emailMessage);

                    return true;
                }
            }

            if ($this->isWoocommerceNewOrderTrigger($automation)) {
                if ($this->isTaskCreateAction($automation)) {
                    $stagToCreateTask = ServiceLocator::get('StageRepository')->getFirstStage($automation->pipeline_id);
                    $woocommerceOrder = $data->woocommerceOrder;

                    if (null === $stagToCreateTask) {
                        throw new \Exception('Pipeline stage not found.');
                    }

                    if (!$woocommerceOrder) {
                        throw new \Exception('Woocommerce order not found.');
                    }

                    $orderNumber = $woocommerceOrder->get_order_number();
                    $createdTask = ServiceLocator::get('TaskService')->createTask($stagToCreateTask->id, [
                        'name'        => 'Woo #' . $orderNumber,
                        'description' => 'WooCommerce order #' . $orderNumber,
                        'pipelineId'  => $automation->pipeline_id,
                    ]);

                    if (!$createdTask) {
                        throw new \Exception('Failed to create a new task.');
                    }

                    return (object) [
                        'success' => true,
                        'data'    => (object) [
                            'task' => $createdTask
                        ],
                        'rerunTriggers' => [
                            (object) [
                                'boardId'           => $automation->pipeline_id,
                                'targetId'          => $createdTask->id,
                                'targetType'        => WP_QUICKTASKER_AUTOMATION_TARGET_TYPE_TASK,
                                'automationTrigger' => WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_CREATED,
                            ]
                        ]
                    ];
                }
            }

            if ($this->isSeatregNewBookingTrigger($automation)) {
                if ($this->isTaskCreateAction($automation)) {
                    return $this->handleSeatregTaskCreation(
                        $automation,
                        $data->seatregBookings,
                        $data->registration
                    );
                }
            }

            if ($this->isSeatregBookingApprovedTrigger($automation)) {
                if ($this->isTaskCreateAction($automation)) {
                    return $this->handleSeatregTaskCreation(
                        $automation,
                        $data->seatregBookings,
                        $data->registration
                    );
                }
            }

            if ($this->isSeatregBookingPendingTrigger($automation)) {
                if ($this->isTaskCreateAction($automation)) {
                    return $this->handleSeatregTaskCreation(
                        $automation,
                        $data->seatregBookings,
                        $data->registration
                    );
                }
            }

            if ($this->isSeatregBookingApprovedViaManagerTrigger($automation)) {
                if ($this->isTaskCreateAction($automation)) {
                    return $this->handleSeatregTaskCreation(
                        $automation,
                        $data->seatregBookings,
                        $data->registration
                    );
                }
            }

            if ($this->isSeatregBookingPendingViaManagerTrigger($automation)) {
                if ($this->isTaskCreateAction($automation)) {
                    return $this->handleSeatregTaskCreation(
                        $automation,
                        $data->seatregBookings,
                        $data->registration
                    );
                }
            }

            return false;
        }

        private function handleSeatregTaskCreation($automation, $seatregBookings, $registration)
        {
            $stageToCreateTask = ServiceLocator::get('StageRepository')->getFirstStage($automation->pipeline_id);
            $bookingId = $seatregBookings[0]->booking_id;

            if (null === $stageToCreateTask) {
                throw new \Exception('Pipeline stage not found.');
            }

            $createdTask = ServiceLocator::get('TaskService')->createTask($stageToCreateTask->id, [
                'name'        => $registration->registration_name . ' booking',
                'description' => $bookingId,
                'pipelineId'  => $automation->pipeline_id,
            ]);

            if (!$createdTask) {
                throw new \Exception('Failed to create a new task.');
            }

            return (object) [
                'success' => true,
                'data'    => (object) [
                    'task' => $createdTask
                ],
                'rerunTriggers' => [
                    (object) [
                        'boardId'           => $automation->pipeline_id,
                        'targetId'          => $createdTask->id,
                        'targetType'        => WP_QUICKTASKER_AUTOMATION_TARGET_TYPE_TASK,
                        'automationTrigger' => WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_CREATED,
                    ]
                ]
            ];
        }

        /**
         * Checks if the given automation action is to archive a task.
         *
         * @param object $automation The automation object containing action and target type.
         * @return bool Returns true if the automation action is to archive a task and the target type is task, otherwise false.
         */
        private function isArchiveTaskAction($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_ACTION_ARCHIVE_TASK === $automation->automation_action && WP_QUICKTASKER_AUTOMATION_TARGET_TYPE_TASK === $automation->target_type;
        }

        /**
         * Checks if the given automation action is to assign a user.
         *
         * This function verifies if the automation action is of type 'assign user' and
         * if the target type of the automation action is 'user'.
         *
         * @param object $automation The automation object containing action details.
         * @return bool Returns true if the automation action is to assign a user, false otherwise.
         */
        public function isAssignUserAction($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_ACTION_ASSIGN_USER === $automation->automation_action &&
                   in_array($automation->automation_action_target_type, [WP_QUICKTASKER_AUTOMATION_ACTION_TARGET_TYPE_QUICKTASKER, WP_QUICKTASKER_AUTOMATION_ACTION_TARGET_TYPE_WP_USER], true) &&
                   null !== $automation->automation_action_target_id;
        }

        private function isNewEntityEmailAction($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_ACTION_NEW_ENTITY === $automation->automation_action && null !== $automation->metadata;
        }

        /**
         * Checks if the automation action is a Slack message action.
         *
         * @param object $automation The automation object to check.
         * @return bool True if the automation action is a Slack message action and metadata is not null, false otherwise.
         */
        private function isSlackMessageAction($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_ACTION_SEND_SLACK_MESSAGE === $automation->automation_action && null !== $automation->metadata;
        }

        /**
         * Checks if the automation action is a deleted entity email action.
         *
         * @param object $automation The automation object to check.
         * @return bool True if the automation action is a deleted entity email action and metadata is not null, false otherwise.
         */
        private function isDeletedEntityEmailAction($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_ACTION_DELETED_ENTITY_EMAIL === $automation->automation_action && null !== $automation->metadata;
        }

        /**
         * Checks if the automation action is a task assigned email action.
         *
         * @param object $automation The automation object to check.
         * @return bool True if the automation action is a task assigned email action and metadata is not null, false otherwise.
         */
        private function isTaskAssignedEmailAction($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_ACTION_TASK_ASSIGNED_EMAIL === $automation->automation_action && null !== $automation->metadata;
        }

        /**
         * Checks if the automation action is a task unassigned email action.
         *
         * @param object $automation The automation object to check.
         * @return bool True if the automation action is a task unassigned email action and metadata is not null, false otherwise.
         */
        private function isTaskUnassignedEmailAction($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_ACTION_TASK_UNASSIGNED_EMAIL === $automation->automation_action && null !== $automation->metadata;
        }

        /**
         * Checks if the automation action is a task public comment added email action.
         *
         * @param object $automation The automation object to check.
         * @return bool True if the automation action is a task public comment added email action and metadata is not null, false otherwise.
         */
        private function isTaskPublicCommentAddedEmailAction($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_ACTION_TASK_PUBLIC_COMMENT_ADDED_EMAIL === $automation->automation_action && null !== $automation->metadata;
        }

        /**
         * Checks if the automation action is a task private comment added email action.
         *
         * @param object $automation The automation object to check.
         * @return bool True if the automation action is a task private comment added email action and metadata is not null, false otherwise.
         */
        private function isTaskPrivateCommentAddedEmailAction($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_ACTION_TASK_PRIVATE_COMMENT_ADDED_EMAIL === $automation->automation_action && null !== $automation->metadata;
        }

        private function isTaskAttachmentAddedEmailAction($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_ACTION_TASK_ATTACHMENT_ADDED_EMAIL === $automation->automation_action && null !== $automation->metadata;
        }

        private function isTaskAttachmentDeletedEmailAction($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_ACTION_TASK_ATTACHMENT_DELETED_EMAIL === $automation->automation_action && null !== $automation->metadata;
        }

        /**
         * Determines if the given automation object represents a "create task" action.
         *
         * @param object $automation The automation object to evaluate.
         *                           It is expected to have a property `automation_action`.
         * @return bool Returns true if the automation action is equal to
         *              WP_QUICKTASKER_AUTOMATION_ACTION_CREATE_TASK, otherwise false.
         */
        public function isTaskCreateAction($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_ACTION_CREATE_TASK === $automation->automation_action;
        }

        /**
         * Checks if the automation trigger is set to 'task done'.
         *
         * @param object $automation The automation object containing the trigger information.
         * @return bool Returns true if the automation trigger is 'task done', false otherwise.
         */
        public function isTaskDoneTrigger($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_DONE === $automation->automation_trigger;
        }

        /**
         * Checks if the automation trigger is set to "task not done".
         *
         * This function evaluates whether the provided automation object's trigger
         * is equal to the constant `WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_NOT_DONE`.
         *
         * @param object $automation The automation object to check.
         * @return bool Returns true if the automation trigger is "task not done", false otherwise.
         */
        private function isTaskNotDoneTrigger($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_NOT_DONE === $automation->automation_trigger;
        }

        /**
         * Checks if the automation trigger is set to "task created".
         *
         * @param object $automation The automation object containing the trigger information.
         * @return bool Returns true if the automation trigger is "task created", false otherwise.
         */
        private function isTaskCreatedTrigger($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_CREATED === $automation->automation_trigger;
        }

        /**
         * Checks if the automation trigger is set to task deleted.
         *
         * @param object $automation The automation object containing the trigger information.
         * @return bool Returns true if the automation trigger is set to task deleted, false otherwise.
         */
        private function isTaskDeletedTrigger($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_DELETED === $automation->automation_trigger;
        }

        /**
         * Checks if the automation trigger is set to task assigned.
         *
         * @param object $automation The automation object to check.
         * @return bool Returns true if the automation trigger is task assigned, false otherwise.
         */
        private function isTaskAssignedTrigger($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_ASSIGNED === $automation->automation_trigger;
        }

        /**
         * Checks if the automation trigger is set to task unassigned.
         *
         * @param object $automation The automation object to check.
         * @return bool Returns true if the automation trigger is task unassigned, false otherwise.
         */
        private function isTaskUnassignedTrigger($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_UNASSIGNED === $automation->automation_trigger;
        }

        /**
         * Checks if the automation trigger is set to task public comment added.
         *
         * @param object $automation The automation object to check.
         * @return bool Returns true if the automation trigger is task public comment added, false otherwise.
         */
        private function isTaskPublicCommentAddedTrigger($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_PUBLIC_COMMENT_ADDED === $automation->automation_trigger;
        }

        /**
         * Checks if the automation trigger is set to task private comment added.
         *
         * @param object $automation The automation object to check.
         * @return bool Returns true if the automation trigger is task private comment added, false otherwise.
         */
        private function isTaskPrivateCommentAddedTrigger($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_PRIVATE_COMMENT_ADDED === $automation->automation_trigger;
        }

        /**
         * Checks if the automation trigger is set to "task attachment added".
         *
         * @param object $automation The automation object to check.
         * @return bool Returns true if the automation trigger is "task attachment added", false otherwise.
         */
        private function isTaskAttachementAddedTrigger($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_ATTACHMENT_ADDED === $automation->automation_trigger;
        }

        /**
         * Checks if the automation trigger is set to task attachment deleted.
         *
         * @param object $automation The automation object containing the trigger information.
         * @return bool Returns true if the automation trigger is task attachment deleted, false otherwise.
         */
        private function isTaskAttachmentDeletedTrigger($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_ATTACHMENT_DELETED === $automation->automation_trigger;
        }

        /**
         * Checks if the given automation is triggered by a new WooCommerce order.
         *
         * This method evaluates whether the automation's trigger matches the
         * predefined constant for a WooCommerce "order added" event.
         *
         * @param object $automation The automation object to check.
         * @return bool True if the automation trigger is for a new WooCommerce order, false otherwise.
         */
        private function isWoocommerceNewOrderTrigger($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_TRIGGER_WOOCOMMERCE_ORDER_ADDED === $automation->automation_trigger;
        }

        /**
         * Checks if the automation is triggered by a new booking in Seatreg.
         *
         * @param object $automation The automation object to check.
         * @return bool Returns true if the automation trigger is for a new Seatreg booking, false otherwise.
         */
        private function isSeatregNewBookingTrigger($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_TRIGGER_SEATREG_BOOKING_CREATED === $automation->automation_trigger;
        }

        /**
         * Checks if the automation is triggered by an approved booking in Seatreg.
         *
         * @param object $automation The automation object to check.
         * @return bool Returns true if the automation trigger is for an approved Seatreg booking, false otherwise.
         */
        private function isSeatregBookingApprovedTrigger($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_TRIGGER_SEATREG_BOOKING_APPROVED === $automation->automation_trigger;
        }

        /**
         * Checks if the automation is triggered by an approved booking via manager in Seatreg.
         *
         * @param object $automation The automation object to check.
         * @return bool Returns true if the automation trigger is for an approved booking via manager in Seatreg, false otherwise.
         */
        private function isSeatregBookingApprovedViaManagerTrigger($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_TRIGGER_SEATREG_BOOKING_APPROVED_VIA_MANAGER === $automation->automation_trigger;
        }

        /**
         * Checks if the automation is triggered by a pending booking in Seatreg.
         *
         * @param object $automation The automation object to check.
         * @return bool Returns true if the automation trigger is for a pending Seatreg booking, false otherwise.
         */
        private function isSeatregBookingPendingTrigger($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_TRIGGER_SEATREG_BOOKING_PENDING === $automation->automation_trigger;
        }

        /**
         * Checks if the automation is triggered by a pending booking via manager in Seatreg.
         *
         * @param object $automation The automation object to check.
         * @return bool Returns true if the automation trigger is for a pending booking via manager in Seatreg, false otherwise.
         */
        private function isSeatregBookingPendingViaManagerTrigger($automation)
        {
            return WP_QUICKTASKER_AUTOMATION_TRIGGER_SEATREG_BOOKING_PENDING_VIA_MANAGER === $automation->automation_trigger;
        }

        /**
         * Generates a log message for the given automation.
         *
         * @param object $automation The automation object containing details about the automation.
         * @return string The formatted log message.
         */
        private function getAutomationLogMessage($automation)
        {
            return 'Automation triggered on ' . $this->getAutomationDescription($automation);
        }

        /**
         * Returns "{trigger label} → {action label}" for an automation. Public so
         * API endpoints can include it in CRUD log messages.
         */
        public function getAutomationDescription($automation)
        {
            $trigger = $this->getTriggerLabel($automation->automation_trigger);
            $action = $this->getActionLabel($automation->automation_action);

            return "{$trigger} → {$action}";
        }

        /**
         * Generates a log message for a failed automation.
         *
         * @param object $automation The automation object containing details about the failed automation.
         * @return string The formatted log message.
         */
        private function getAutomationFailedLogMessage($automation)
        {
            return 'Automation failed on ' . $this->getAutomationDescription($automation);
        }

        /**
         * Returns a human-readable label for an automation trigger constant.
         * Falls back to the raw value if a label is not defined, so newly added
         * triggers without a label still produce a non-empty log message.
         */
        private function getTriggerLabel($trigger)
        {
            $labels = [
                WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_DONE                            => 'Task marked done',
                WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_NOT_DONE                        => 'Task marked not done',
                WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_CREATED                         => 'Task created',
                WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_DELETED                         => 'Task deleted',
                WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_ASSIGNED                        => 'Task assigned',
                WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_UNASSIGNED                      => 'Task unassigned',
                WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_PUBLIC_COMMENT_ADDED            => 'Public comment added',
                WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_PRIVATE_COMMENT_ADDED           => 'Private comment added',
                WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_ATTACHMENT_ADDED                => 'Attachment added',
                WP_QUICKTASKER_AUTOMATION_TRIGGER_TASK_ATTACHMENT_DELETED              => 'Attachment deleted',
                WP_QUICKTASKER_AUTOMATION_TRIGGER_WOOCOMMERCE_ORDER_ADDED              => 'WooCommerce order added',
                WP_QUICKTASKER_AUTOMATION_TRIGGER_SEATREG_BOOKING_CREATED              => 'Seatreg booking created',
                WP_QUICKTASKER_AUTOMATION_TRIGGER_SEATREG_BOOKING_APPROVED             => 'Seatreg booking approved',
                WP_QUICKTASKER_AUTOMATION_TRIGGER_SEATREG_BOOKING_PENDING              => 'Seatreg booking pending',
                WP_QUICKTASKER_AUTOMATION_TRIGGER_SEATREG_BOOKING_PENDING_VIA_MANAGER  => 'Seatreg booking pending (via manager)',
                WP_QUICKTASKER_AUTOMATION_TRIGGER_SEATREG_BOOKING_APPROVED_VIA_MANAGER => 'Seatreg booking approved (via manager)',
            ];

            return $labels[$trigger] ?? $trigger;
        }

        /**
         * Returns a human-readable label for an automation action constant.
         * Falls back to the raw value if a label is not defined.
         */
        private function getActionLabel($action)
        {
            $labels = [
                WP_QUICKTASKER_AUTOMATION_ACTION_ARCHIVE_TASK                     => 'Archive task',
                WP_QUICKTASKER_AUTOMATION_ACTION_ASSIGN_USER                      => 'Assign user',
                WP_QUICKTASKER_AUTOMATION_ACTION_NEW_ENTITY                       => 'Send new-entity email',
                WP_QUICKTASKER_AUTOMATION_ACTION_DELETED_ENTITY_EMAIL             => 'Send deleted-entity email',
                WP_QUICKTASKER_AUTOMATION_ACTION_TASK_ASSIGNED_EMAIL              => 'Send task-assigned email',
                WP_QUICKTASKER_AUTOMATION_ACTION_TASK_UNASSIGNED_EMAIL            => 'Send task-unassigned email',
                WP_QUICKTASKER_AUTOMATION_ACTION_TASK_PUBLIC_COMMENT_ADDED_EMAIL  => 'Send public-comment email',
                WP_QUICKTASKER_AUTOMATION_ACTION_TASK_PRIVATE_COMMENT_ADDED_EMAIL => 'Send private-comment email',
                WP_QUICKTASKER_AUTOMATION_ACTION_TASK_ATTACHMENT_ADDED_EMAIL      => 'Send attachment-added email',
                WP_QUICKTASKER_AUTOMATION_ACTION_TASK_ATTACHMENT_DELETED_EMAIL    => 'Send attachment-deleted email',
                WP_QUICKTASKER_AUTOMATION_ACTION_CREATE_TASK                      => 'Create task',
                WP_QUICKTASKER_AUTOMATION_ACTION_SEND_SLACK_MESSAGE               => 'Send Slack message',
            ];

            return $labels[$action] ?? $action;
        }

        /**
         * Creates a new automation entry in the database.
         *
         * @param int $pipelineId The ID of the pipeline.
         * @param int|null $targetId The ID of the target, can be null.
         * @param string $targetType The type of the target.
         * @param string $trigger The trigger for the automation.
         * @param string $action The action to be performed by the automation.
         * @param int|null $automationActionTargetId The ID of the target for the action, can be null.
         * @param string|null $automationActionTargetType The type of the target for the action, can be null.
         * @param string|null $metadata Additional metadata for the automation, can be null.
         * @return array The created automation data.
         * @throws \Exception If the automation creation fails.
         */
        public function createAutomation($pipelineId, $targetId, $targetType, $trigger, $action, $automationActionTargetId = null, $automationActionTargetType = null, $metadata = null)
        {
            global $wpdb;

            $encryptMeta = ServiceLocator::get('AutomationRepository')->isSensitiveMetaAutomation($action);

            $data = [
                'pipeline_id'                   => $pipelineId,
                'target_id'                     => $targetId,
                'target_type'                   => $targetType,
                'automation_trigger'            => $trigger,
                'automation_action'             => $action,
                'automation_action_target_id'   => $automationActionTargetId,
                'automation_action_target_type' => $automationActionTargetType,
                'metadata'                      => $encryptMeta ? ServiceLocator::get('SecretsService')->encrypt($metadata) : $metadata,
                'created_at'                    => ServiceLocator::get('TimeRepository')->getCurrentUTCTime(),
                'updated_at'                    => ServiceLocator::get('TimeRepository')->getCurrentUTCTime()
            ];

            $result = $wpdb->insert(
                TABLE_WP_QUICKTASKER_AUTOMATIONS,
                $data
            );

            if (!$result) {
                throw new \Exception('Failed to create an automation.');
            }

            return ServiceLocator::get('AutomationRepository')->getAutomation($wpdb->insert_id);
        }

        /**
         * Updates the active state of an automation in the database.
         *
         * @param int $automationId The ID of the automation to update.
         * @param bool|int $active The new active state (e.g., 1 for active, 0 for inactive).
         *
         * @throws \Exception If the update operation fails.
         *
         * @return array|null The updated automation data retrieved from the repository, or null if not found.
         */
        public function updateAutomationActiveState($automationId, $active)
        {
            global $wpdb;

            $result = $wpdb->update(
                TABLE_WP_QUICKTASKER_AUTOMATIONS,
                ['active' => $active],
                ['id'     => $automationId]
            );

            if (false === $result) {
                throw new \Exception('Failed to update the automation active state.');
            }

            return ServiceLocator::get('AutomationRepository')->getAutomation($automationId);
        }

        /**
         * Deletes an automation by its ID.
         *
         * This function retrieves the automation by its ID and deletes it from the database.
         * If the automation is not found, it throws an exception.
         * If the deletion fails, it throws an exception.
         *
         * @param int $automationId The ID of the automation to delete.
         * @return object The deleted automation object.
         * @throws \Exception If the automation is not found or the deletion fails.
         */
        public function deleteAutomation($automationId)
        {
            global $wpdb;

            $automation = ServiceLocator::get('AutomationRepository')->getAutomation($automationId);

            if (!$automation) {
                throw new \Exception('Automation not found.');
            }

            $result = $wpdb->delete(
                TABLE_WP_QUICKTASKER_AUTOMATIONS,
                ['id' => $automationId]
            );

            if (!$result) {
                throw new \Exception('Failed to delete the automation.');
            }

            return $automation;
        }
    }
}
