<?php

namespace BitApps\PiPro\src\Cron;

// Prevent direct script access
if (!\defined('ABSPATH')) {
    exit;
}


use BitApps\Pi\Config as FreeConfig;
use BitApps\Pi\Helpers\Utility;
use BitApps\PiPro\Deps\BitApps\WPKit\Helpers\JSON;
use BitApps\PiPro\Deps\BitApps\WPKit\Hooks\Hooks;
use BitApps\PiPro\Services\CronService;
use BitApps\PiPro\src\Tools\Schedule\ScheduleTool;

/**
 * CustomScheduleManager.
 *
 * Manages custom WordPress cron schedules for Bit Pi Pro flows.
 * This class handles the registration of custom cron schedules, setting up scheduled events,
 * and managing the execution of scheduled flows based on various time intervals.
 *
 * Features:
 * - Registers custom cron schedules (minute, hour, day, week, month)
 * - Sets up WordPress cron events for flow execution
 * - Manages scheduled action hooks for different flow nodes
 * - Supports flexible scheduling with custom intervals
 *
 * @since 1.7.0
 */
class CustomScheduleManager
{
    /**
     * Delay units mapping for cron schedules.
     * Maps time units to their equivalent seconds for WordPress cron system.
     *
     * @var array<string, int>
     */
    private const DELAY_UNITS = [
        'minute' => 60,
        'hour'   => 3600,
        'day'    => 86400,
        'week'   => 604800,
        'month'  => 2592000,
    ];

    /**
     * Initialize all schedule-related functionality.
     *
     * This method sets up the complete scheduling system by:
     * - Registering custom cron schedules with WordPress
     * - Setting up scheduled action hooks for flow execution
     * - Creating cron events for scheduled flows
     */
    public function initSchedules()
    {
        add_filter('cron_schedules', [$this, 'registerCustomCronSchedules']);
        $this->registerScheduledActionHooks();
        $this->setupFlowCronEvents();
    }

    /**
     * Register scheduled action hooks for flow execution.
     *
     * This method retrieves cached schedule nodes and registers WordPress action hooks
     * for each scheduled flow. The hooks are used to trigger flow execution at scheduled times.
     *
     * Process:
     * 1. Retrieves schedule data from transient cache
     * 2. Decodes JSON data for each schedule node
     * 3. Creates unique action hooks for each schedule interval
     * 4. Registers hooks with ScheduleTool::scheduleTriggerHandler as callback
     */
    public function registerScheduledActionHooks()
    {
        $schedulesNodes = Utility::getTransientCache('schedules');

        if (!$schedulesNodes) {
            return;
        }

        $schedulesNodes = JSON::maybeDecode($schedulesNodes, true);

        foreach ($schedulesNodes as $nodeId => $schedulesNode) {
            if (empty($schedulesNode)) {
                continue;
            }

            $schedulesNode = JSON::maybeDecode($schedulesNode, true);

            if (empty($schedulesNode)) {
                continue;
            }

            foreach ($schedulesNode as $index => $item) {
                if (isset($item['interval'])) {
                    $actionHook = FreeConfig::VAR_PREFIX . 'flow_schedule_event_' . $nodeId . '_' . $index;
                    Hooks::addAction($actionHook, [new ScheduleTool(), 'scheduleTriggerHandler'], 10, 3);
                }
            }
        }
    }

    /**
     * Register custom cron schedules with WordPress.
     *
     * This method adds custom scheduling intervals to WordPress cron system
     * based on the configured flow schedules. It processes cached schedule data
     * and creates custom intervals for different time units (minute, hour, day, week, month).
     *
     * @param array $schedules Existing WordPress cron schedules
     *
     * @return array Modified schedules array with custom Bit Pi intervals
     */
    public function registerCustomCronSchedules($schedules)
    {
        $cachedScheduleNodes = Utility::getTransientCache('schedules');

        $scheduleNodes = JSON::maybeDecode($cachedScheduleNodes, true);

        if (empty($scheduleNodes)) {
            return $schedules;
        }

        foreach ($scheduleNodes as $scheduleNode) {
            if (empty($scheduleNode)) {
                continue;
            }

            $flowSchedules = JSON::maybeDecode($scheduleNode, true);

            if (empty($flowSchedules)) {
                continue;
            }

            foreach ($flowSchedules as $flowSchedule) {
                if (!isset($flowSchedule['interval'])) {
                    continue;
                }

                $scheduleUnit = $flowSchedule['interval'];

                $data = CronService::getInterValueByUnit($scheduleUnit, $flowSchedule);

                $intervalValue = $data['interval'] ?? null;

                if (!empty($scheduleUnit) && !isset(self::DELAY_UNITS[$scheduleUnit])) {
                    continue;
                }

                if (!empty($intervalValue) && !empty($scheduleUnit)) {
                    $customScheduleName = FreeConfig::VAR_PREFIX . 'every_' . $intervalValue . '_' . $scheduleUnit;
                    $intervalInSeconds = $intervalValue * self::DELAY_UNITS[$scheduleUnit];
                    $scheduleDisplayName = \sprintf('Every %1$s %2$s', $intervalValue, ucfirst($scheduleUnit));
                } else {
                    $customScheduleName = FreeConfig::VAR_PREFIX . 'every_' . $scheduleUnit;

                    $intervalInSeconds = self::DELAY_UNITS[$scheduleUnit] ?? null;

                    if ($intervalInSeconds === null) {
                        continue;
                    }

                    $scheduleDisplayName = \sprintf('Every One %s', ucfirst($scheduleUnit));
                }

                if (isset($schedules[$customScheduleName])) {
                    continue;
                }

                $schedules[$customScheduleName] = [
                    'interval' => $intervalInSeconds,
                    'display'  => $scheduleDisplayName . ' ( Bit Pi )',
                ];
            }
        }


        return $schedules;
    }

    /**
     * Set up WordPress cron events for scheduled flows.
     *
     * This method creates actual WordPress cron events based on the configured schedules.
     * It processes cached schedule data and schedules events using wp_schedule_event()
     * for each flow that has a valid interval configuration.
     *
     * Process:
     * 1. Retrieves schedule nodes from cache
     * 2. Processes each schedule configuration
     * 3. Determines recurrence pattern and hook arguments
     * 4. Checks if event is already scheduled to avoid duplicates
     * 5. Schedules new events with calculated run times
     */
    private function setupFlowCronEvents()
    {
        $schedulesNodes = Utility::getTransientCache('schedules');

        if (!$schedulesNodes) {
            return;
        }

        $schedulesNodes = JSON::maybeDecode($schedulesNodes, true);

        foreach ($schedulesNodes as $nodeId => $schedulesNode) {
            if (empty($schedulesNode)) {
                continue;
            }

            $schedulesNode = JSON::maybeDecode($schedulesNode, true);

            if (empty($schedulesNode)) {
                continue;
            }

            foreach ($schedulesNode as $index => $item) {
                if (isset($item['interval'])) {
                    $scheduleUnit = $item['interval'];

                    $data = CronService::getInterValueByUnit($scheduleUnit, $item);

                    $intervalValue = $data['interval'] ?? null;

                    if (!empty($intervalValue) && !empty($scheduleUnit)) {
                        $recurrence = FreeConfig::VAR_PREFIX . 'every_' . $intervalValue . '_' . $scheduleUnit;
                    } else {
                        $recurrence = FreeConfig::VAR_PREFIX . 'every_' . $scheduleUnit;
                    }

                    $hookArgs = [];

                    $actionHook = FreeConfig::VAR_PREFIX . 'flow_schedule_event_' . $nodeId . '_' . $index;

                    $hookArgs['nodeId'] = $nodeId;

                    $hookArgs['dayOfMonth'] = isset($data['dayOfMonth']) ? $data['dayOfMonth'] : null;

                    $hookArgs['day'] = isset($data['day']) ? $data['day'] : null;

                    $wpNextPostTimeStamp = wp_next_scheduled($actionHook, $hookArgs);

                    if ($wpNextPostTimeStamp) {
                        continue;
                    }

                    $scheduleRunTime = CronService::getScheduleRunTime($scheduleUnit, $item);

                    wp_schedule_event($scheduleRunTime, $recurrence, $actionHook, $hookArgs);
                }
            }
        }
    }
}
