<?php

namespace BitApps\PiPro\src\Integrations\FluentBoards;

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

use BitApps\Pi\Services\FlowService;
use BitApps\PiPro\src\Integrations\IntegrationHelper;
use FluentBoards\App\Models\Board;
use FluentBoards\App\Models\Task;
use FluentBoards\App\Models\User;

final class FluentBoardsTrigger
{
    // ------------------------- BOARD EVENTS -------------------------

    public static function boardCreated(object $board): void
    {
        self::execute(__FUNCTION__, self::prepareData($board));
    }

    public static function boardUpdated(object $board, object $oldBoard): void
    {
        $data = [
            'new_board' => self::prepareData($board),
            'old_board' => self::prepareData($oldBoard),
        ];

        self::execute(__FUNCTION__, $data);
    }

    public static function boardArchived(object $board): void
    {
        self::execute(__FUNCTION__, self::prepareData($board));
    }

    public static function boardRestored(object $board): void
    {
        self::execute(__FUNCTION__, self::prepareData($board));
    }

    public static function boardDeleted(object $board): void
    {
        self::execute(__FUNCTION__, self::prepareData($board));
    }

    public static function boardMemberAdded(int $boardId, object $boardMember): void
    {
        self::executeBoardMemberAddedTriggers(__FUNCTION__, $boardId, $boardMember);
    }

    public static function boardViewerAdded(int $boardId, object $boardMember): void
    {
        self::executeBoardMemberAddedTriggers(__FUNCTION__, $boardId, $boardMember);
    }

    // ------------------------- TASK EVENTS -------------------------

    public static function taskCreated(object $task): void
    {
        self::execute(__FUNCTION__, self::prepareData($task));
    }

    public static function taskUpdated(object $task, string $updatedType): void
    {
        $data = [
            'updated_type' => $updatedType,
            'task'         => self::prepareData($task),
        ];

        self::execute(__FUNCTION__, $data);
    }

    public static function taskStageUpdated(object $task, int $oldTaskId): void
    {
        $data = [
            'old_task_id' => $oldTaskId,
            'task'        => self::prepareData($task),
        ];

        self::execute(__FUNCTION__, $data);
    }

    public static function taskContentUpdated(object $newTask, string $updatedColumn, object $oldTask): void
    {
        $data = [
            'updated_column' => $updatedColumn,
            'new_task'       => self::prepareData($newTask),
            'old_task'       => self::prepareData($oldTask),
        ];

        self::execute(__FUNCTION__, $data);
    }

    public static function taskArchived(object $task): void
    {
        self::execute(__FUNCTION__, self::prepareData($task));
    }

    public static function taskAssigneeAdded(object $task, int $assigneeId): void
    {
        self::executeTaskAssigneeTriggers(__FUNCTION__, $task, $assigneeId);
    }

    public static function taskAssigneeRemoved(object $task, int $assigneeId): void
    {
        self::executeTaskAssigneeTriggers(__FUNCTION__, $task, $assigneeId);
    }

    public static function taskPriorityUpdated(object $task, string $oldPriority): void
    {
        $data = [
            'old_priority' => $oldPriority,
            'task'         => self::prepareData($task),
        ];

        self::execute(__FUNCTION__, $data);
    }

    public static function taskMovedBetweenBoards(object $task, object $oldBoard, object $newBoard): void
    {
        $data = [
            'task'      => self::prepareData($task),
            'old_board' => self::prepareData($oldBoard),
            'new_board' => self::prepareData($newBoard),
        ];

        self::execute(__FUNCTION__, $data);
    }

    public static function taskCloned(object $task, object $clonedTask): void
    {
        $data = [
            'task'        => self::prepareData($task),
            'cloned_task' => self::prepareData($clonedTask),
        ];

        self::execute(__FUNCTION__, $data);
    }

    public static function taskDeleted(object $task): void
    {
        self::execute(__FUNCTION__, self::prepareData($task));
    }

    public static function taskStartDateUpdated(object $task, string $oldDate): void
    {
        $data = [
            'old_date' => $oldDate,
            'task'     => self::prepareData($task),
        ];

        self::execute(__FUNCTION__, $data);
    }

    public static function taskDueDateUpdated(object $task, string $oldDate): void
    {
        $data = [
            'old_date' => $oldDate,
            'task'     => self::prepareData($task),
        ];

        self::execute(__FUNCTION__, $data);
    }

    public static function taskDueDateRemoved(object $task): void
    {
        self::execute(__FUNCTION__, self::prepareData($task));
    }

    public static function taskLabelAdded(object $task, object $label, string $action): void
    {
        self::executeTaskLabelTriggers(__FUNCTION__, $task, $label, $action, 'added');
    }

    public static function taskLabelRemoved(object $task, object $label, string $action): void
    {
        self::executeTaskLabelTriggers(__FUNCTION__, $task, $label, $action, 'removed');
    }

    // ------------------------- LABEL EVENTS -------------------------

    public static function boardLabelCreated(object $label): void
    {
        self::execute(__FUNCTION__, self::prepareData($label));
    }

    public static function boardLabelUpdated(object $label): void
    {
        self::execute(__FUNCTION__, self::prepareData($label));
    }

    public static function boardLabelDeleted(object $label): void
    {
        self::execute(__FUNCTION__, self::prepareData($label));
    }

    // ------------------------- STAGE EVENTS -------------------------

    public static function boardStageUpdated(int $boardId, array $updatedStage, object $oldStage): void
    {
        $board = Board::findOrFail($boardId);

        if (!$board || empty($updatedStage)) {
            return;
        }

        $data = [
            'updated_stage' => $updatedStage,
            'old_stage'     => self::prepareData($oldStage),
            'board'         => self::prepareData($board),
        ];

        self::execute(__FUNCTION__, $data);
    }

    public static function boardStageArchived(int $boardId, object $stage): void
    {
        $board = Board::findOrFail($boardId);

        if (!$board) {
            return;
        }

        $data = [
            'board' => self::prepareData($board),
            'stage' => self::prepareData($stage),
        ];

        self::execute(__FUNCTION__, $data);
    }

    public static function boardStageRestored(int $boardId, string $stageTitle): void
    {
        $board = Board::findOrFail($boardId);

        if (!$board) {
            return;
        }

        $data = [
            'board'       => self::prepareData($board),
            'stage_title' => $stageTitle,
        ];

        self::execute(__FUNCTION__, $data);
    }

    // ------------------------- FLUENT BOARDS PRO EVENTS -------------------------

    public static function subtaskAdded(object $parentTask, object $subtask): void
    {
        $data = [
            'subtask'     => self::prepareData($subtask),
            'parent_task' => self::prepareData($parentTask),
        ];

        self::execute(__FUNCTION__, $data);
    }

    public static function subtaskDeleted(int $parentTaskId, string $subtaskTitle): void
    {
        $parentTask = Task::findOrFail($parentTaskId);

        if (!$parentTask) {
            return;
        }

        $data = [
            'subtask_title' => $subtaskTitle,
            'parent_task'   => self::prepareData($parentTask),
        ];

        self::execute(__FUNCTION__, $data);
    }

    public static function attachmentAdded(object $attachment): void
    {
        self::execute(__FUNCTION__, self::prepareData($attachment));
    }

    public static function attachmentDeleted(object $attachment): void
    {
        self::execute(__FUNCTION__, self::prepareData($attachment));
    }

    /**
     * Convert an object to array safely.
     *
     * @param mixed|object $object object or any value
     *
     * @return null|array
     */
    private static function prepareData($object)
    {
        return (\is_object($object) && method_exists($object, 'toArray')) ? $object->toArray() : null;
    }

    /**
     * Execute board member added/viewer added triggers.
     */
    private static function executeBoardMemberAddedTriggers(string $machineSlug, int $boardId, object $boardMember): void
    {
        $board = Board::findOrFail($boardId);

        if (!$board || !$member = self::prepareData($boardMember)) {
            return;
        }

        $data = [
            'board'        => self::prepareData($board),
            'board_member' => $member,
        ];

        self::execute($machineSlug, $data);
    }

    /**
     * Execute task assignee added/removed triggers.
     */
    private static function executeTaskAssigneeTriggers(string $machineSlug, object $task, int $assigneeId): void
    {
        $assignee = User::find($assigneeId);

        if (!$assignee) {
            return;
        }

        $data = [
            'task'     => self::prepareData($task),
            'assignee' => self::prepareData($assignee),
        ];

        self::execute($machineSlug, $data);
    }

    /**
     * Execute task label added/removed triggers.
     */
    private static function executeTaskLabelTriggers(string $machineSlug, object $task, object $label, string $action, string $expectedAction): void
    {
        if ($action !== $expectedAction) {
            return;
        }

        $data = [
            'task'  => self::prepareData($task),
            'label' => self::prepareData($label),
        ];

        self::execute($machineSlug, $data);
    }

    /**
     * Execute the flow trigger.
     */
    private static function execute(string $machineSlug, array $data): void
    {
        $flows = FlowService::exists('FluentBoards', $machineSlug);
        if (!$flows) {
            return;
        }

        IntegrationHelper::handleFlowForForm($flows, $data);
    }
}
