<?php

declare(strict_types=1);

namespace App\Support\OpsUx;

use App\Models\OperationRun;
use App\Support\OperationCatalog;
use App\Support\Operations\OperationRunFreshnessState;
use Illuminate\Support\Facades\Cache;

final class RunDurationInsights
{
    public static function elapsedSeconds(OperationRun $run): ?int
    {
        $start = $run->started_at ?? $run->created_at;

        if (! $start) {
            return null;
        }

        $end = $run->completed_at ?? now();

        $seconds = $end->diffInSeconds($start);

        if (is_int($seconds)) {
            return $seconds;
        }

        return (int) round((float) $seconds);
    }

    public static function elapsedHuman(OperationRun $run): string
    {
        $start = $run->started_at ?? $run->created_at;

        if (! $start) {
            return '—';
        }

        $end = $run->completed_at ?? now();

        return $end->diffForHumans($start, true);
    }

    public static function elapsedCompact(OperationRun $run): string
    {
        $seconds = self::elapsedSeconds($run);

        if (! is_int($seconds) || $seconds <= 0) {
            return 'now';
        }

        if ($seconds < 60) {
            return sprintf('%d sec.', $seconds);
        }

        if ($seconds < 3600) {
            return sprintf('%d min.', (int) round($seconds / 60));
        }

        if ($seconds < 86400) {
            return sprintf('%d hr.', (int) round($seconds / 3600));
        }

        return sprintf('%d day%s', (int) round($seconds / 86400), $seconds < 172800 ? '' : 's');
    }

    public static function completedRecency(OperationRun $run): string
    {
        return $run->completed_at?->diffForHumans() ?? 'just now';
    }

    public static function expectedSeconds(OperationRun $run): ?int
    {
        $catalog = OperationCatalog::expectedDurationSeconds((string) $run->type);

        if (is_int($catalog)) {
            return $catalog;
        }

        $tenantId = (int) ($run->managed_environment_id ?? 0);
        $type = (string) ($run->type ?? '');

        if ($tenantId <= 0 || $type === '') {
            return null;
        }

        $cacheKey = "opsux:expected-duration:tenant:{$tenantId}:type:".$type;

        return Cache::remember($cacheKey, now()->addMinutes(10), function () use ($tenantId, $type): ?int {
            $durations = OperationRun::query()
                ->where('managed_environment_id', $tenantId)
                ->where('type', $type)
                ->whereNotNull('started_at')
                ->whereNotNull('completed_at')
                ->where('created_at', '>=', now()->subDays(30))
                ->latest('id')
                ->limit(200)
                ->get(['started_at', 'completed_at'])
                ->map(function (OperationRun $run): ?int {
                    if (! $run->started_at || ! $run->completed_at) {
                        return null;
                    }

                    $seconds = $run->completed_at->diffInSeconds($run->started_at);

                    if (is_int($seconds)) {
                        return $seconds;
                    }

                    return (int) round((float) $seconds);
                })
                ->filter(fn (?int $seconds): bool => is_int($seconds) && $seconds > 0)
                ->sort()
                ->values();

            if ($durations->isEmpty()) {
                return null;
            }

            $count = $durations->count();
            $middle = intdiv($count - 1, 2);

            $median = (int) $durations[$middle];

            return $median > 0 ? $median : null;
        });
    }

    public static function expectedHuman(OperationRun $run): ?string
    {
        $seconds = self::expectedSeconds($run);

        if (! is_int($seconds) || $seconds <= 0) {
            return null;
        }

        if ($seconds < 60) {
            return 'Typically under 1 minute';
        }

        $minutes = (int) round($seconds / 60);

        return "Typically ~{$minutes} min";
    }

    public static function stuckGuidance(OperationRun $run): ?string
    {
        if ($run->freshnessState() === OperationRunFreshnessState::LikelyStale) {
            return 'Past the lifecycle window. Review worker health and logs before retrying.';
        }

        $uxStatus = OperationStatusNormalizer::toUxStatus($run->status, $run->outcome);

        if (! in_array($uxStatus, ['queued', 'running'], true)) {
            return null;
        }

        $elapsed = self::elapsedSeconds($run);

        if (! is_int($elapsed) || $elapsed <= 0) {
            return null;
        }

        $expected = self::expectedSeconds($run);

        $isLikelyStuck = is_int($expected)
            ? ($elapsed > max(600, $expected * 2))
            : ($elapsed > 900);

        if (! $isLikelyStuck) {
            return null;
        }

        return 'Taking longer than expected. If this does not complete soon, verify the queue worker is running and check logs for errors.';
    }
}
