<?php

namespace App\Support\Tasks;

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;

class TaskKanbanStageManager
{
    /**
     * @var list<string>
     */
    private const CORE_KANBAN_STATUSES = ['todo', 'in_progress', 'review', 'done'];

    /**
     * @var array<string, string>
     */
    private const DEFAULT_STAGE_COLORS = [
        'todo' => '#94A3B8',
        'in_progress' => '#3B82F6',
        'review' => '#F59E0B',
        'done' => '#10B981',
    ];

    /**
     * @return array<string, string>
     */
    public function defaultTaskStatusOptions(): array
    {
        return [
            'todo' => __('To do'),
            'in_progress' => __('In progress'),
            'review' => __('Review'),
            'done' => __('Done'),
        ];
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function resolveKanbanStages(User $user): array
    {
        $defaultStatusOptions = $this->defaultTaskStatusOptions();
        $defaultStages = collect(self::CORE_KANBAN_STATUSES)
            ->map(function (string $status) use ($defaultStatusOptions): array {
                return [
                    'status' => $status,
                    'label' => (string) ($defaultStatusOptions[$status] ?? $status),
                    'color' => (string) (self::DEFAULT_STAGE_COLORS[$status] ?? '#94A3B8'),
                ];
            })
            ->all();

        $preset = $user->task_kanban_preset;

        if (! is_array($preset) || $preset === []) {
            return $defaultStages;
        }

        $resolved = [];
        foreach ($preset as $item) {
            if (! is_array($item)) {
                continue;
            }

            $status = $this->normalizeKanbanStatus((string) ($item['status'] ?? ''));

            if ($status === '' || array_key_exists($status, $resolved)) {
                continue;
            }

            $label = trim((string) ($item['label'] ?? ''));
            $color = $this->normalizeKanbanColor((string) ($item['color'] ?? ''));

            $resolved[$status] = [
                'status' => $status,
                'label' => $label !== '' ? $label : (string) ($defaultStatusOptions[$status] ?? Str::headline(str_replace('_', ' ', $status))),
                'color' => $color ?? (string) (self::DEFAULT_STAGE_COLORS[$status] ?? '#94A3B8'),
            ];
        }

        foreach (self::CORE_KANBAN_STATUSES as $status) {
            if (! array_key_exists($status, $resolved)) {
                $resolved[$status] = [
                    'status' => $status,
                    'label' => (string) ($defaultStatusOptions[$status] ?? $status),
                    'color' => (string) (self::DEFAULT_STAGE_COLORS[$status] ?? '#94A3B8'),
                ];
            }
        }

        return array_values($resolved);
    }

    /**
     * @return list<string>
     */
    public function allowedTaskStatusesForUser(User $user, ?string $currentStatus = null): array
    {
        $statuses = collect($this->resolveKanbanStages($user))
            ->pluck('status')
            ->merge(self::CORE_KANBAN_STATUSES);

        if ($currentStatus && $currentStatus !== '') {
            $statuses->push($currentStatus);
        }

        return $statuses
            ->filter(fn ($status) => is_string($status) && $status !== '')
            ->unique()
            ->values()
            ->all();
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function buildPresetFromLegacyPayload(Request $request): array
    {
        $defaultStatusOptions = $this->defaultTaskStatusOptions();
        $allowedStatuses = array_keys($defaultStatusOptions);

        $validated = $request->validate([
            'labels' => ['required', 'array'],
            'labels.*' => ['nullable', 'string', 'max:60'],
            'sort' => ['required', 'array'],
            'sort.*' => ['required', 'integer', 'min:1', 'max:50'],
        ]);

        foreach ($allowedStatuses as $status) {
            if (! array_key_exists($status, $validated['labels'])) {
                throw ValidationException::withMessages([
                    "labels.{$status}" => 'Fill in the names of all kanban stages.',
                ]);
            }

            if (! array_key_exists($status, $validated['sort'])) {
                throw ValidationException::withMessages([
                    "sort.{$status}" => 'Fill in the order of all kanban stages.',
                ]);
            }
        }

        $orderedStatuses = collect($allowedStatuses)
            ->sortBy(function (string $status) use ($validated, $allowedStatuses): int {
                $position = (int) ($validated['sort'][$status] ?? 99);
                $fallbackOrder = array_search($status, $allowedStatuses, true);
                $fallbackOrder = $fallbackOrder === false ? 99 : $fallbackOrder;

                return ($position * 100) + $fallbackOrder;
            })
            ->values();

        return $orderedStatuses->map(function (string $status) use ($validated, $defaultStatusOptions): array {
            $label = trim((string) ($validated['labels'][$status] ?? ''));

            return [
                'status' => $status,
                'label' => $label !== '' ? $label : (string) ($defaultStatusOptions[$status] ?? $status),
                'color' => (string) (self::DEFAULT_STAGE_COLORS[$status] ?? '#94A3B8'),
            ];
        })->all();
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function buildPresetFromStagePayload(Request $request): array
    {
        $validated = $request->validate([
            'stages' => ['required', 'array', 'min:4', 'max:50'],
            'stages.*.status' => ['required', 'string', 'max:80'],
            'stages.*.label' => ['required', 'string', 'max:60'],
            'stages.*.color' => ['nullable', 'string', 'regex:/^#[0-9A-Fa-f]{6}$/'],
            'stages.*.is_core' => ['nullable', 'boolean'],
        ]);

        $defaultStatusOptions = $this->defaultTaskStatusOptions();
        $preset = [];
        $seenStatuses = [];

        foreach ($validated['stages'] as $index => $stage) {
            $rawStatus = (string) ($stage['status'] ?? '');
            $status = $this->normalizeKanbanStatus($rawStatus);
            $label = trim((string) ($stage['label'] ?? ''));
            $color = $this->normalizeKanbanColor((string) ($stage['color'] ?? ''))
                ?? '#94A3B8';

            if ($status === '') {
                throw ValidationException::withMessages([
                    "stages.{$index}.status" => 'The stage must have valid code.',
                ]);
            }

            if (in_array($status, $seenStatuses, true)) {
                throw ValidationException::withMessages([
                    "stages.{$index}.status" => 'Stage codes must be unique.',
                ]);
            }

            if ($label === '') {
                throw ValidationException::withMessages([
                    "stages.{$index}.label" => 'Indicate the name of the stage.',
                ]);
            }

            $seenStatuses[] = $status;
            $preset[] = [
                'status' => $status,
                'label' => $label,
                'color' => $color,
            ];
        }

        foreach (self::CORE_KANBAN_STATUSES as $coreStatus) {
            if (! in_array($coreStatus, $seenStatuses, true)) {
                $preset[] = [
                    'status' => $coreStatus,
                    'label' => (string) ($defaultStatusOptions[$coreStatus] ?? $coreStatus),
                    'color' => (string) (self::DEFAULT_STAGE_COLORS[$coreStatus] ?? '#94A3B8'),
                ];
            }
        }

        return array_values($preset);
    }

    private function normalizeKanbanStatus(string $value): string
    {
        $value = trim(Str::lower($value));
        if ($value === '') {
            return '';
        }

        if (in_array($value, self::CORE_KANBAN_STATUSES, true)) {
            return $value;
        }

        $normalized = preg_replace('/[^a-z0-9_]+/', '_', $value) ?? '';
        $normalized = trim((string) $normalized, '_');

        if ($normalized === '') {
            return '';
        }

        if (! Str::startsWith($normalized, 'custom_')) {
            $normalized = 'custom_'.$normalized;
        }

        return Str::limit($normalized, 60, '');
    }

    private function normalizeKanbanColor(string $value): ?string
    {
        $value = trim($value);

        if (! preg_match('/^#[0-9A-Fa-f]{6}$/', $value)) {
            return null;
        }

        return strtoupper($value);
    }
}
