<?php

namespace App\Support;

use App\Models\Roster;
use App\Models\User;
use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;
use Illuminate\Support\Str;

class RosterData
{
    public static function normalizeTeam(?string $team, string $fallback = 'designers'): string
    {
        $normalized = Str::of((string) $team)
            ->trim()
            ->lower()
            ->replace(['team', ' '], '')
            ->value();

        return $normalized === 'qa' ? 'qa' : ($normalized === 'designers' || $normalized === 'designer' ? 'designers' : $fallback);
    }

    public static function normalizeName(?string $value): string
    {
        $normalized = Str::of((string) $value)
            ->lower()
            ->replaceMatches('/[^a-z0-9]+/', '')
            ->value();

        return match ($normalized) {
            'tahafarrukh', 'tahafarukh', 'tahafarookh', 'tahafarruk' => 'tahafarrukh',
            default => $normalized,
        };
    }

    public static function formatHours(float|string|null $hours, string $empty = '-'): string
    {
        if ($hours === null || (float) $hours <= 0) {
            return $empty;
        }

        return number_format((float) $hours, 2, '.', '');
    }

    public static function toEditorRow(User $user, ?Roster $roster, string $rosterDate): array
    {
        return [
            'id' => $roster?->id,
            'userId' => $user->id,
            'name' => $user->name,
            'team' => self::normalizeTeam($user->team),
            'rosterDate' => $rosterDate,
            'holiday' => (bool) ($roster?->is_holiday),
            'totalHours' => self::formatHours($roster?->total_hours),
            'shift1StartDate' => self::dateValue($roster?->shift1_start),
            'shift1StartTime' => self::timeValue($roster?->shift1_start),
            'shift1EndDate' => self::dateValue($roster?->shift1_end),
            'shift1EndTime' => self::timeValue($roster?->shift1_end),
            'shift2StartDate' => self::dateValue($roster?->shift2_start),
            'shift2StartTime' => self::timeValue($roster?->shift2_start),
            'shift2EndDate' => self::dateValue($roster?->shift2_end),
            'shift2EndTime' => self::timeValue($roster?->shift2_end),
            'shift3StartDate' => self::dateValue($roster?->shift3_start),
            'shift3StartTime' => self::timeValue($roster?->shift3_start),
            'shift3EndDate' => self::dateValue($roster?->shift3_end),
            'shift3EndTime' => self::timeValue($roster?->shift3_end),
        ];
    }

    public static function buildRosterAttributes(array $row, string $rosterDate, string $team): array
    {
        $ranges = [
            self::buildShiftRange($row, $rosterDate, 'shift1StartDate', 'shift1StartTime', 'shift1EndDate', 'shift1EndTime'),
            self::buildShiftRange($row, $rosterDate, 'shift2StartDate', 'shift2StartTime', 'shift2EndDate', 'shift2EndTime'),
            self::buildShiftRange($row, $rosterDate, 'shift3StartDate', 'shift3StartTime', 'shift3EndDate', 'shift3EndTime'),
        ];

        return [
            'team' => self::normalizeTeam($team),
            'shift1_start' => $ranges[0][0],
            'shift1_end' => $ranges[0][1],
            'shift2_start' => $ranges[1][0],
            'shift2_end' => $ranges[1][1],
            'shift3_start' => $ranges[2][0],
            'shift3_end' => $ranges[2][1],
            'is_holiday' => (bool) ($row['holiday'] ?? false),
            'total_hours' => self::calculateTotalHours($ranges),
        ];
    }

    public static function combineDateAndTime(?string $date, ?string $time): ?CarbonImmutable
    {
        if (blank($date) || blank($time)) {
            return null;
        }

        try {
            $normalizedDate = CarbonImmutable::parse($date)->toDateString();
            $normalizedTime = self::normalizeTime($time);

            if ($normalizedTime === null) {
                return null;
            }

            return CarbonImmutable::createFromFormat('Y-m-d g:i A', "{$normalizedDate} {$normalizedTime}");
        } catch (\Throwable) {
            return null;
        }
    }

    public static function normalizeDate(?string $value, ?string $fallback = null): ?string
    {
        if (blank($value)) {
            return $fallback;
        }

        try {
            return CarbonImmutable::parse($value)->toDateString();
        } catch (\Throwable) {
            return $fallback;
        }
    }

    public static function normalizeTime(?string $value): ?string
    {
        if (blank($value)) {
            return null;
        }

        $trimmed = trim((string) $value);

        if (preg_match('/^\d{1,2}:\d{2}\s*(AM|PM)$/i', $trimmed) === 1) {
            return strtoupper($trimmed);
        }

        try {
            return CarbonImmutable::parse($trimmed)->format('g:i A');
        } catch (\Throwable) {
            return null;
        }
    }

    public static function calculateTotalHours(array $ranges): float
    {
        $totalMinutes = 0;

        foreach ($ranges as [$start, $end]) {
            if (! $start instanceof CarbonInterface || ! $end instanceof CarbonInterface) {
                continue;
            }

            if ($end->lessThan($start)) {
                $end = $end->addDay();
            }

            $totalMinutes += $start->diffInMinutes($end);
        }

        return round($totalMinutes / 60, 2);
    }

    public static function formatDateTime(?CarbonInterface $value): string
    {
        return $value ? $value->format('Y-m-d h:i A') : '-';
    }

    private static function buildShiftRange(
        array $row,
        string $rosterDate,
        string $startDateKey,
        string $startTimeKey,
        string $endDateKey,
        string $endTimeKey,
    ): array {
        $hasAnyTime = filled($row[$startTimeKey] ?? null) || filled($row[$endTimeKey] ?? null);

        if (! $hasAnyTime) {
            return [null, null];
        }

        $startDate = blank($row[$startDateKey] ?? null) ? $rosterDate : (string) $row[$startDateKey];
        $endDate = blank($row[$endDateKey] ?? null) ? $rosterDate : (string) $row[$endDateKey];
        $startTime = blank($row[$startTimeKey] ?? null) ? '09:00 AM' : (string) $row[$startTimeKey];
        $endTime = blank($row[$endTimeKey] ?? null) ? '09:00 AM' : (string) $row[$endTimeKey];

        return [
            self::combineDateAndTime($startDate, $startTime),
            self::combineDateAndTime($endDate, $endTime),
        ];
    }

    private static function dateValue(?CarbonInterface $value): string
    {
        return $value ? $value->toDateString() : '';
    }

    private static function timeValue(?CarbonInterface $value): string
    {
        return $value ? $value->format('h:i A') : '';
    }
}
