<?php

namespace App\ScheduledTasks;

use App\Models\ScheduledSlotProfile;
use Illuminate\Support\Collection;
use Mtc\MercuryDataModels\ScheduledTask;

/**
 * Cross-tenant advisory load profile per 5-minute time slot, backing the
 * "busy slot" warning on the schedule form. One row per tenant per slot;
 * a tenant's rows are rebuilt whole on every task change, so nothing drifts.
 */
class SlotLoadService
{
    private const SLOT_MINUTES = 5;

    /** Frequency weights: how much of a daily-equivalent load one run time adds */
    private const WEIGHTS = [
        'daily' => 1.0,
        'weekdays' => 0.71,
        'weekly' => 0.14,
        'biweekly' => 0.07,
        'monthly' => 0.03,
    ];

    /**
     * Rebuild the current tenant's contribution from its task definitions.
     */
    public function rebuildForCurrentTenant(): void
    {
        $tenantId = (string) tenant('id');

        $loads = [];

        ScheduledTask::query()
            ->enabled()
            ->scheduleTriggered()
            ->get(['schedule_rules'])
            ->each(function (ScheduledTask $task) use (&$loads) {
                foreach ($task->schedule_rules ?? [] as $rule) {
                    $weight = self::WEIGHTS[$rule['frequency'] ?? ''] ?? 0;
                    $slot = $this->slotFor($rule['time'] ?? '00:00');
                    $loads[$slot] = ($loads[$slot] ?? 0) + $weight;
                }
            });

        ScheduledSlotProfile::query()->where('tenant_id', $tenantId)->delete();

        if (!empty($loads)) {
            ScheduledSlotProfile::query()->insert(
                collect($loads)
                    ->map(fn (float $load, string $slot) => [
                        'tenant_id' => $tenantId,
                        'slot' => $slot,
                        'load' => round($load, 2),
                        'created_at' => now(),
                        'updated_at' => now(),
                    ])
                    ->values()
                    ->toArray()
            );
        }
    }

    /**
     * Load tiers for the requested times, with quieter nearby suggestions for busy ones.
     *
     * @param array<int, string> $times H:i values
     */
    public function check(array $times): Collection
    {
        $threshold = (float) config('scheduled_tasks.slot_busy_threshold', 10);

        return collect($times)->map(function (string $time) use ($threshold) {
            $load = $this->loadFor($this->slotFor($time));

            return [
                'time' => $time,
                'busy' => $load >= $threshold,
                'suggestions' => $load >= $threshold ? $this->quieterNearby($time, $threshold) : [],
            ];
        });
    }

    private function loadFor(string $slot): float
    {
        return (float) ScheduledSlotProfile::query()
            ->where('slot', $slot)
            ->sum('load');
    }

    /**
     * @return array<int, string>
     */
    private function quieterNearby(string $time, float $threshold): array
    {
        [$hour, $minute] = array_map('intval', explode(':', $time));
        $base = $hour * 60 + $minute;

        return collect([-20, 20, -40, 40, -60, 60])
            ->map(fn (int $offset) => ($base + $offset + 1440) % 1440)
            ->map(fn (int $minutes) => sprintf('%02d:%02d', intdiv($minutes, 60), $minutes % 60))
            ->filter(fn (string $candidate) => $this->loadFor($this->slotFor($candidate)) < $threshold)
            ->take(2)
            ->values()
            ->toArray();
    }

    private function slotFor(string $time): string
    {
        [$hour, $minute] = array_map('intval', explode(':', $time));

        return sprintf('%02d:%02d', $hour, intdiv($minute, self::SLOT_MINUTES) * self::SLOT_MINUTES);
    }
}
