<?php

namespace App\Service;

use App\Entity\Task;
use App\Entity\TaskSchema;
use App\Enum\TaskStatus;
use App\Helper\DateHelper;
use App\Repository\TaskSchemaRepository;
use Doctrine\ORM\EntityManagerInterface;

class TaskGenerator
{
    public function __construct(
        private EntityManagerInterface $em,
        private TaskSchemaRepository $schemaRepo,
    ) {
    }

    public function generateNewTasks(): void
    {
        $this->deleteExpiredSchemas();

        $today = new \DateTimeImmutable('today');
        $schemas = $this->schemaRepo->findActive();

        foreach ($schemas as $schema) {
            if ($schema->getRepeatType() === null) {
                $date = $schema->getDate();
                if ($date !== null && $date->format('Y-m-d') === $today->format('Y-m-d')) {
                    $this->createTask($schema, $today);
                }
            } elseif ($this->matchesDate($schema, $today)) {
                $this->createTask($schema, $today);
            }
        }

        $this->em->flush();
    }

    public function removeTasks(TaskSchema $schema): void
    {
        foreach ($schema->getTasks() as $task) {
            if ($task->getStatus() === TaskStatus::Past) continue;
            $this->em->remove($task);
        }
    }

    public function generateTasks(TaskSchema $schema): void
    {
        $dates = $this->getDates($schema);

        foreach ($dates as $date) {
            $this->createTask($schema, $date);
        }
    }

    private function createTask(TaskSchema $schema, \DateTimeImmutable $date): void
    {
        $task = new Task();
        $task->setName($schema->getName());
        $task->setDate($date);
        $task->setStatus($schema->getTaskStatus());
        $task->setSchema($schema);
        $this->em->persist($task);
    }

    private function deleteExpiredSchemas(): void
    {
        $expired = $this->schemaRepo->findExpired();
        foreach ($expired as $schema) {
            $this->removeTasks($schema);
            $this->em->remove($schema);
        }
    }

    /** @return list<\DateTimeImmutable> */
    private function getDates(TaskSchema $schema): array
    {
        if ($schema->getRepeatType() === null) {
            $date = $schema->getDate();
            return DateHelper::isInRange($date) ? [$date] : [];
        }

        [$from, $end] = DateHelper::getDateRange($schema->getStart(), $schema->getEnd());
        $dates = [];

        for ($date = $from; $date <= $end; $date = $date->modify('+1 day')) {
            if ($this->matchesDate($schema, $date)) {
                $dates[] = $date;
            }
        }

        return $dates;
    }

    private function matchesDate(TaskSchema $schema, \DateTimeImmutable $date): bool
    {
        $type = $schema->getRepeatType();
        $repeat = $schema->getRepeat();

        if ($type === 'daily') {
            return true;
        }

        if ($type === 'weekly' || $type === '2week' || $type === '4week') {
            $weekday = (int) $date->format('N') - 1;
            if (!$repeat[$type][$weekday]) return false;
            if ($type === 'weekly') return true;

            $start = $schema->getStart() ?? new \DateTimeImmutable('today');
            return DateHelper::getWeeksDiff($start, $date) % ($type === '2week' ? 2 : 4) === 0;
        }

        if ($type === 'monthly') {
            $monthday = (int) $date->format('j') - 1;
            return $repeat['monthly'][$monthday];
        }

        if ($type === 'days') {
            return in_array($date->format('Y-m-d'), $repeat['days'], true);
        }

        return false;
    }
}
