<?php

namespace App\Listeners;

use App\Events\ScheduledTaskFinished;
use App\Jobs\RunScheduledTaskJob;
use Illuminate\Support\Carbon;
use Mtc\MercuryDataModels\ScheduledTask;
use Mtc\MercuryDataModels\ScheduledTaskRun;

class TriggerChildScheduledTasks
{
    public function handle(ScheduledTaskFinished $event): void
    {
        ScheduledTask::query()
            ->enabled()
            ->where('trigger_type', ScheduledTask::TRIGGER_AFTER_TASK)
            ->where('parent_task_id', $event->task->id)
            ->each(function (ScheduledTask $child) use ($event) {
                // Runtime cycle guard: never trigger a child whose ancestry
                // contains the finishing task's own chain
                if ($this->wouldLoop($child, $event->run)) {
                    return;
                }

                $run = $child->runs()->create([
                    'status' => ScheduledTaskRun::STATUS_PENDING,
                    'scheduled_for' => Carbon::now(),
                    'trigger' => ScheduledTaskRun::TRIGGER_AFTER_TASK,
                    'triggered_by_run_id' => $event->run->id,
                ]);

                if ($this->conditionMatches($child, $event)) {
                    RunScheduledTaskJob::dispatch($run);
                } else {
                    $run->markSkipped(sprintf(
                        'Parent %s; child runs %s',
                        $event->succeeded() ? 'succeeded' : 'failed',
                        str_replace('_', ' ', $child->parent_condition)
                    ));
                }
            });
    }

    private function conditionMatches(ScheduledTask $child, ScheduledTaskFinished $event): bool
    {
        return match ($child->parent_condition) {
            ScheduledTask::CONDITION_ALWAYS => true,
            ScheduledTask::CONDITION_ON_FAILURE => !$event->succeeded(),
            default => $event->succeeded(),
        };
    }

    /**
     * Walk the chain of runs that led here; refuse when the child already ran in it.
     */
    private function wouldLoop(ScheduledTask $child, ScheduledTaskRun $run): bool
    {
        $guard = 10;
        $cursor = $run;

        while ($cursor && $guard-- > 0) {
            if ($cursor->scheduled_task_id === $child->id) {
                return true;
            }

            $cursor = $cursor->triggeredBy;
        }

        return $guard < 0;
    }
}
