<?php

namespace Openguru\OpenCartFramework\Scheduler;

use Cron\CronExpression;
use Openguru\OpenCartFramework\Container\Container;

class Job
{
    private Container $container;

    /** @var string|callable|TaskInterface */
    private $action;

    private string $expression = '* * * * *';

    private ?string $name;

    public function __construct(Container $container, $action, ?string $name = null)
    {
        $this->container = $container;
        $this->action = $action;
        $this->name = $name;
    }

    public function at(string $expression): self
    {
        $this->expression = $expression;
        return $this;
    }

    public function everyMinute(): self
    {
        return $this->at('* * * * *');
    }

    public function everyFiveMinutes(): self
    {
        return $this->at('*/5 * * * *');
    }

    public function everyTenMinutes(): self
    {
        return $this->at('*/10 * * * *');
    }

    public function everyFifteenMinutes(): self
    {
        return $this->at('*/15 * * * *');
    }

    public function everyHour(): self
    {
        return $this->at('0 * * * *');
    }

    public function dailyAt(int $hour, int $minute = 0): self
    {
        return $this->at(sprintf('%d %d * * *', $minute, $hour));
    }

    public function isDue(): bool
    {
        return (new CronExpression($this->expression))->isDue();
    }

    public function run(): void
    {
        if (is_string($this->action) && class_exists($this->action)) {
            /** @var TaskInterface $task */
            $task = $this->container->get($this->action);
            $task->execute();
        } elseif (is_callable($this->action)) {
            call_user_func($this->action);
        } elseif ($this->action instanceof TaskInterface) {
            $this->action->execute();
        }
    }

    public function getName(): string
    {
        if ($this->name) {
            return $this->name;
        }

        if (is_string($this->action)) {
            return $this->action;
        }

        return 'Closure';
    }

    public function getId(): string
    {
        return md5($this->getName());
    }

    public function getExpression(): string
    {
        return $this->expression;
    }
}
