<?php

declare(strict_types=1);

namespace App\Service\Leave;

use App\Entity\ContractSuspension;
use DateTimeImmutable;

final class SuspensionDaysCalculator
{
    /**
     * Count calendar days suspended within a month window [monthStart, monthEnd].
     *
     * @param list<ContractSuspension> $suspensions
     */
    public function countSuspendedDaysInMonth(
        DateTimeImmutable $monthStart,
        DateTimeImmutable $monthEnd,
        array $suspensions
    ): int {
        $total = 0;

        foreach ($suspensions as $suspension) {
            $sStart = $suspension->getStartDate();
            $sEnd   = $suspension->getEndDate() ?? $monthEnd;

            $overlapStart = $sStart > $monthStart ? $sStart : $monthStart;
            $overlapEnd   = $sEnd < $monthEnd ? $sEnd : $monthEnd;

            if ($overlapStart > $overlapEnd) {
                continue;
            }

            $total += ((int) $overlapEnd->diff($overlapStart)->format('%a')) + 1;
        }

        return $total;
    }

    /**
     * Return adjusted suspensions where the first month of each suspension is excluded (grace period).
     *
     * @param list<ContractSuspension> $suspensions
     *
     * @return list<ContractSuspension>
     */
    public function applyFirstMonthGrace(array $suspensions): array
    {
        $adjusted = [];

        foreach ($suspensions as $suspension) {
            $gracedStart = $suspension->getStartDate()->modify('+1 month');
            $end         = $suspension->getEndDate();

            if ($end instanceof DateTimeImmutable && $gracedStart > $end) {
                continue;
            }

            $copy = new ContractSuspension();
            $copy->setStartDate($gracedStart);
            $copy->setEndDate($end);

            $adjusted[] = $copy;
        }

        return $adjusted;
    }

    /**
     * Count business days (Mon-Fri, excl. public holidays) suspended within a period.
     *
     * @param list<ContractSuspension> $suspensions
     * @param array<string, string>    $publicHolidays map of Y-m-d => label
     */
    public function countSuspendedBusinessDays(
        DateTimeImmutable $periodStart,
        DateTimeImmutable $periodEnd,
        array $suspensions,
        array $publicHolidays
    ): int {
        $total = 0;

        foreach ($suspensions as $suspension) {
            $sStart = $suspension->getStartDate();
            $sEnd   = $suspension->getEndDate() ?? $periodEnd;

            $overlapStart = $sStart > $periodStart ? $sStart : $periodStart;
            $overlapEnd   = $sEnd < $periodEnd ? $sEnd : $periodEnd;

            if ($overlapStart > $overlapEnd) {
                continue;
            }

            for ($cursor = $overlapStart; $cursor <= $overlapEnd; $cursor = $cursor->modify('+1 day')) {
                $weekDay = (int) $cursor->format('N');
                $dayKey  = $cursor->format('Y-m-d');
                if ($weekDay <= 5 && !isset($publicHolidays[$dayKey])) {
                    ++$total;
                }
            }
        }

        return $total;
    }
}
