<?php

namespace Licenzo\Services\Logging;

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

use DateTime;
use Licenzo\Framework\Support\Collection;
use Licenzo\Model\Subscription;
use Licenzo\Model\SubscriptionSchedule;
use Licenzo\Services\Logging\Concerns\CapturesContext;
use Licenzo\Services\Logging\Concerns\HandlesErrors;
use Licenzo\Services\Logging\Concerns\ParsesDateTime;

/**
 * ScheduleManager
 * 
 * Manages subscription schedules with automatic data population.
 * Minimal user input required - auto-populates all fields from context.
 * 
 * Usage:
 *   ScheduleManager::schedule($subscription, 'cancel', '+30 days');
 *   ScheduleManager::schedule($subscription, 'upgrade', '2024-12-31', ['new_plan_id' => 123]);
 *   ScheduleManager::execute($schedule, function() { ... });
 */
class ScheduleManager
{
    use CapturesContext, HandlesErrors, ParsesDateTime;

    /**
     * Create a new schedule with minimal input
     *
     * @param Subscription $subscription The subscription model
     * @param string $actionType Action type constant (e.g., 'cancel', 'upgrade')
     * @param string|DateTime $scheduledAt When to execute (supports relative strings like '+30 days')
     * @param array $actionData Optional action-specific data
     * @param string|null $notes Optional human-readable notes
     * @return SubscriptionSchedule|null
     */
    public static function schedule(
        Subscription $subscription,
        string $actionType,
        $scheduledAt,
        array $actionData = [],
        ?string $notes = null
    ): ?SubscriptionSchedule {
        return static::safeExecute(function () use ($subscription, $actionType, $scheduledAt, $actionData, $notes) {
            // Build complete schedule data
            $data = static::buildScheduleData($subscription, $actionType, $scheduledAt, $actionData, $notes);

            // Create and return the schedule
            return SubscriptionSchedule::create($data);
        }, 'ScheduleManager::schedule');
    }

    /**
     * Execute a schedule with callback
     *
     * @param SubscriptionSchedule $schedule
     * @param callable $callback Callback to execute the action
     * @return bool
     */
    public static function execute(SubscriptionSchedule $schedule, callable $callback): bool
    {
        return static::safeExecute(function () use ($schedule, $callback) {
            // Check if schedule can be executed
            if (!$schedule->canExecute()) {
                return false;
            }

            try {
                // Execute the callback
                $result = $callback($schedule);

                // Mark as executed
                $schedule->update([
                    'status' => SubscriptionSchedule::STATUS_EXECUTED,
                    'executed_at' => gmdate('Y-m-d H:i:s'),
                    'execution_result' => is_array($result) ? $result : ['success' => true],
                ]);

                return true;
            } catch (\Throwable $e) {
                // Mark as failed
                $schedule->update([
                    'status' => SubscriptionSchedule::STATUS_FAILED,
                    'failure_reason' => $e->getMessage(),
                ]);

                static::handleError($e, 'ScheduleManager::execute');
                return false;
            }
        }, 'ScheduleManager::execute') ?? false;
    }

    /**
     * Cancel a pending schedule
     *
     * @param SubscriptionSchedule $schedule
     * @return bool
     */
    public static function cancel(SubscriptionSchedule $schedule): bool
    {
        return static::safeExecute(function () use ($schedule) {
            if (!$schedule->canCancel()) {
                return false;
            }

            return $schedule->update([
                'status' => SubscriptionSchedule::STATUS_CANCELED,
            ]);
        }, 'ScheduleManager::cancel') ?? false;
    }

    /**
     * Get pending schedules for a subscription
     *
     * @param Subscription|null $subscription Optional subscription filter
     * @return Collection
     */
    public static function getPending(?Subscription $subscription = null): Collection
    {
        $query = SubscriptionSchedule::where('status', SubscriptionSchedule::STATUS_PENDING);

        if ($subscription) {
            $query->where('subscription_id', $subscription->id);
        }

        return $query->orderBy('scheduled_at', 'asc')->get();
    }

    /**
     * Get schedules that are due for execution
     *
     * @return Collection
     */
    public static function getDue(): Collection
    {
        return SubscriptionSchedule::where('status', SubscriptionSchedule::STATUS_PENDING)
            ->where('scheduled_at', '<=', gmdate('Y-m-d H:i:s'))
            ->orderBy('scheduled_at', 'asc')
            ->get();
    }

    /**
     * Get all schedules for a subscription
     *
     * @param Subscription $subscription
     * @param string|null $status Optional status filter
     * @return Collection
     */
    public static function getForSubscription(Subscription $subscription, ?string $status = null): Collection
    {
        $query = SubscriptionSchedule::where('subscription_id', $subscription->id);

        if ($status) {
            $query->where('status', $status);
        }

        return $query->orderBy('scheduled_at', 'desc')->get();
    }

    /**
     * Build complete schedule data with auto-population
     *
     * @param Subscription $subscription
     * @param string $actionType
     * @param mixed $scheduledAt
     * @param array $actionData
     * @param string|null $notes
     * @return array
     */
    protected static function buildScheduleData(
        Subscription $subscription,
        string $actionType,
        $scheduledAt,
        array $actionData,
        ?string $notes
    ): array {
        return [
            // Core IDs
            'subscription_id' => $subscription->id,
            'customer_id' => $subscription->customer_id,

            // Action details
            'action_type' => $actionType,
            'action_data' => $actionData,

            // Scheduling
            'scheduled_at' => static::parseDateTime($scheduledAt),
            'executed_at' => null,

            // Status
            'status' => SubscriptionSchedule::STATUS_PENDING,

            // Execution details
            'execution_result' => null,
            'failure_reason' => null,

            // Metadata
            'triggered_by' => static::getCurrentUserId(),
            'notes' => $notes,
        ];
    }

    /**
     * Mark schedule as executed with result
     *
     * @param SubscriptionSchedule $schedule
     * @param array $result
     * @return bool
     */
    public static function markExecuted(SubscriptionSchedule $schedule, array $result = []): bool
    {
        return static::safeExecute(function () use ($schedule, $result) {
            return $schedule->update([
                'status' => SubscriptionSchedule::STATUS_EXECUTED,
                'executed_at' => gmdate('Y-m-d H:i:s'),
                'execution_result' => $result,
            ]);
        }, 'ScheduleManager::markExecuted') ?? false;
    }

    /**
     * Mark schedule as failed with reason
     *
     * @param SubscriptionSchedule $schedule
     * @param string $reason
     * @return bool
     */
    public static function markFailed(SubscriptionSchedule $schedule, string $reason): bool
    {
        return static::safeExecute(function () use ($schedule, $reason) {
            return $schedule->update([
                'status' => SubscriptionSchedule::STATUS_FAILED,
                'failure_reason' => $reason,
            ]);
        }, 'ScheduleManager::markFailed') ?? false;
    }
}

