<?php

namespace App\Http\Controllers\Owner;

use App\Http\Controllers\Controller;
use App\Models\ExternalPartner;
use App\Models\MariachiGroup;
use App\Models\Serenata;
use App\Models\SerenataRoutePlan;
use App\Support\OwnerWorkspace;
use Illuminate\Http\Request;
use Illuminate\Support\Str;

class SerenataPlannerController extends Controller
{
    private function ensureOwnerOrgId(Request $request): int
    {
        return (int) ($request->user()->organization_id ?? 0);
    }

    private function defaultCity(Request $request, int $organizationId): string
    {
        $cityQuery = Serenata::query()
            ->where('organization_id', $organizationId)
            ->whereNotNull('locality')
            ->where('locality', '<>', '')
            ->selectRaw('LOWER(TRIM(locality)) as city_key, MAX(locality) as city_label, COUNT(*) as total')
            ->groupBy('city_key')
            ->orderByDesc('total');
        OwnerWorkspace::applyScope($cityQuery, $request);

        $city = $cityQuery->value('city_label');

        return (string) ($city ?: 'Bogotá');
    }

    private function availableCities(Request $request, int $organizationId): array
    {
        $cityQuery = Serenata::query()
            ->where('organization_id', $organizationId)
            ->whereNotNull('locality')
            ->where('locality', '<>', '')
            ->selectRaw('LOWER(TRIM(locality)) as city_key, MAX(locality) as city_label')
            ->groupBy('city_key')
            ->orderBy('city_label');
        OwnerWorkspace::applyScope($cityQuery, $request);

        return $cityQuery
            ->pluck('city_label')
            ->map(fn ($city) => (string) $city)
            ->values()
            ->all();
    }

    private function internalGroupOptions(Request $request, int $organizationId): array
    {
        return OwnerWorkspace::groupsForUser($request->user())
            ->map(fn (MariachiGroup $group) => [
                'id' => (int) $group->id,
                'name' => (string) $group->name,
            ])
            ->values()
            ->all();
    }

    private function mariachiOptions(Request $request, int $organizationId): array
    {
        $options = [
            [
                'key' => 'interno:todos',
                'label' => 'Todos los grupos (interno)',
            ],
        ];

        foreach ($this->internalGroupOptions($request, $organizationId) as $group) {
            $options[] = [
                'key' => 'interno:grupo:' . $group['id'],
                'label' => $group['name'] . ' (Interno)',
            ];
        }

        $externals = ExternalPartner::query()
            ->where('organization_id', $organizationId)
            ->where('is_active', true)
            ->orderBy('sort_order')
            ->orderBy('name')
            ->get(['id', 'name'])
            ->map(fn (ExternalPartner $partner) => [
                'key' => 'externo:' . $partner->id,
                'label' => (string) $partner->name,
            ])
            ->all();

        return array_merge($options, $externals);
    }

    private function selectedCities(array $validated): array
    {
        $cities = collect($validated['cities'] ?? []);

        if ($cities->isEmpty() && !empty($validated['city'])) {
            $cities = collect([(string) $validated['city']]);
        }

        return $cities
            ->map(fn ($city) => trim((string) $city))
            ->filter(fn ($city) => $city !== '' && $city !== '__all__')
            ->unique(fn ($city) => mb_strtolower($city, 'UTF-8'))
            ->values()
            ->all();
    }

    private function plannerQuery(int $organizationId, string $date, array $cities, ?int $mariachiGroupId = null)
    {
        $query = Serenata::query()
            ->where('organization_id', $organizationId)
            ->whereDate('event_date', $date)
            ->whereIn('service_status', ['Pendiente', 'Confirmado', 'Cedida', 'Completada'])
            ->orderBy('event_time')
            ->orderBy('id');

        if (!empty($cities)) {
            $cityKeys = collect($cities)
                ->map(fn ($city) => mb_strtolower(trim((string) $city), 'UTF-8'))
                ->filter()
                ->values()
                ->all();

            if (!empty($cityKeys)) {
                $query->where(function ($q) use ($cityKeys) {
                    foreach ($cityKeys as $index => $cityKey) {
                        if ($index === 0) {
                            $q->whereRaw('LOWER(TRIM(locality)) = ?', [$cityKey]);
                        } else {
                            $q->orWhereRaw('LOWER(TRIM(locality)) = ?', [$cityKey]);
                        }
                    }
                });
            }
        }

        if (!empty($mariachiGroupId)) {
            $query->where('mariachi_group_id', $mariachiGroupId);
        }

        return $query;
    }

    private function resolveInternalGroupIdFromKey(string $mariachiKey): ?int
    {
        if (!str_starts_with($mariachiKey, 'interno:grupo:')) {
            return null;
        }

        $parts = explode(':', $mariachiKey);
        $id = (int) ($parts[2] ?? 0);
        return $id > 0 ? $id : null;
    }

    public function index(Request $request)
    {
        $organizationId = $this->ensureOwnerOrgId($request);
        abort_if($organizationId <= 0, 403);

        $defaultDate = now()->toDateString();
        $defaultCity = $this->defaultCity($request, $organizationId);
        $workspaceGroupId = OwnerWorkspace::activeGroupId($request, $organizationId);
        $defaultMariachiKey = !empty($workspaceGroupId)
            ? 'interno:grupo:' . $workspaceGroupId
            : 'interno:todos';

        return view('content.owner.serenatas.planner', [
            'defaultDate' => $defaultDate,
            'defaultCity' => $defaultCity,
            'cityOptions' => $this->availableCities($request, $organizationId),
            'mariachiOptions' => $this->mariachiOptions($request, $organizationId),
            'defaultMariachiKey' => $defaultMariachiKey,
        ]);
    }

    public function data(Request $request)
    {
        $organizationId = $this->ensureOwnerOrgId($request);
        abort_if($organizationId <= 0, 403);

        $validated = $request->validate([
            'date' => ['required', 'date'],
            'city' => ['nullable', 'string', 'max:120'],
            'cities' => ['nullable', 'array'],
            'cities.*' => ['string', 'max:120'],
            'mariachi_key' => ['nullable', 'string', 'max:120'],
        ]);

        $planDate = (string) $validated['date'];
        $cities = $this->selectedCities($validated);
        $workspaceGroupId = OwnerWorkspace::activeGroupId($request, $organizationId);
        $defaultMariachiKey = !empty($workspaceGroupId)
            ? 'interno:grupo:' . $workspaceGroupId
            : 'interno:todos';
        $mariachiKey = trim((string) ($validated['mariachi_key'] ?? $defaultMariachiKey));
        if ($mariachiKey === '') {
            $mariachiKey = $defaultMariachiKey;
        }
        $mariachiLabel = collect($this->mariachiOptions($request, $organizationId))
            ->firstWhere('key', $mariachiKey)['label'] ?? 'Todos los grupos (interno)';
        $mariachiGroupId = $this->resolveInternalGroupIdFromKey($mariachiKey);

        $serenatas = OwnerWorkspace::applyScope(
            $this->plannerQuery($organizationId, $planDate, $cities, $mariachiGroupId),
            $request
        )->get();
        $serenataIds = $serenatas->pluck('id')->all();

        $plans = SerenataRoutePlan::query()
            ->where('organization_id', $organizationId)
            ->whereDate('plan_date', $planDate)
            ->where('mariachi_key', $mariachiKey)
            ->when(!empty($serenataIds), fn ($q) => $q->whereIn('serenata_id', $serenataIds))
            ->get()
            ->keyBy('serenata_id');

        $columnBuckets = [];
        foreach ($serenatas as $serenata) {
            $plan = $plans->get($serenata->id);
            $columnLabel = trim((string) ($plan?->column_label ?: $serenata->neighborhood ?: 'Sin barrio'));
            $columnKey = trim((string) ($plan?->column_key ?: Str::slug($columnLabel)));
            if ($columnKey === '') {
                $columnKey = 'sin-barrio';
            }
            $columnLabel = $columnLabel !== '' ? $columnLabel : 'Sin barrio';

            $columnBuckets[$columnKey] ??= [
                'id' => $columnKey,
                'title' => $columnLabel,
                'item' => [],
            ];

            $columnBuckets[$columnKey]['item'][] = [
                'id' => (string) $serenata->id,
                'title' => $this->cardHtml($serenata),
                'drag' => true,
                '_sort' => [
                    'plan' => (int) ($plan?->route_order ?? 9999),
                    'time' => (string) ($serenata->event_time ?? '99:99'),
                ],
            ];
        }

        $boards = collect($columnBuckets)->map(function ($board) {
            $board['item'] = collect($board['item'])
                ->sortBy([['_sort.plan', 'asc'], ['_sort.time', 'asc']])
                ->map(function ($item) {
                    unset($item['_sort']);
                    return $item;
                })
                ->values()
                ->all();
            return $board;
        })->sortBy('title')->values()->all();

        return response()->json([
            'success' => true,
            'boards' => $boards,
            'meta' => [
                'count' => count($serenataIds),
                'mariachi_key' => $mariachiKey,
                'mariachi_label' => $mariachiLabel,
                'date' => $planDate,
                'cities' => $cities,
                'city' => empty($cities) ? 'Todas' : implode(', ', $cities),
            ],
        ]);
    }

    public function move(Request $request)
    {
        $organizationId = $this->ensureOwnerOrgId($request);
        abort_if($organizationId <= 0, 403);

        $validated = $request->validate([
            'date' => ['required', 'date'],
            'city' => ['nullable', 'string', 'max:120'],
            'cities' => ['nullable', 'array'],
            'cities.*' => ['string', 'max:120'],
            'mariachi_key' => ['required', 'string', 'max:120'],
            'mariachi_label' => ['nullable', 'string', 'max:190'],
            'columns' => ['required', 'array'],
            'columns.*.column_key' => ['required', 'string', 'max:120'],
            'columns.*.column_label' => ['nullable', 'string', 'max:120'],
            'columns.*.serenata_ids' => ['nullable', 'array'],
            'columns.*.serenata_ids.*' => ['integer'],
        ]);

        $planDate = (string) $validated['date'];
        $mariachiKey = trim((string) $validated['mariachi_key']);
        $mariachiLabel = trim((string) ($validated['mariachi_label'] ?? ''));
        $cities = $this->selectedCities($validated);
        $city = count($cities) === 1 ? $cities[0] : null;

        $allIds = collect($validated['columns'])->flatMap(function ($col) {
            return collect($col['serenata_ids'] ?? [])->map(fn ($id) => (int) $id);
        })->filter()->unique()->values();

        $validSerenataIds = OwnerWorkspace::applyScope(
            Serenata::query()
                ->where('organization_id', $organizationId)
                ->whereDate('event_date', $planDate)
                ->when($this->resolveInternalGroupIdFromKey($mariachiKey), fn ($q, $groupId) => $q->where('mariachi_group_id', $groupId))
                ->whereIn('id', $allIds->all()),
            $request
        )
            ->pluck('id')
            ->map(fn ($id) => (int) $id)
            ->all();

        $validSet = array_flip($validSerenataIds);

        foreach ($validated['columns'] as $column) {
            $columnKey = trim((string) ($column['column_key'] ?? ''));
            $columnLabel = trim((string) ($column['column_label'] ?? ''));
            if ($columnLabel === '') {
                $columnLabel = 'Sin barrio';
            }

            foreach (array_values($column['serenata_ids'] ?? []) as $index => $serenataId) {
                $serenataId = (int) $serenataId;
                if (!isset($validSet[$serenataId])) {
                    continue;
                }

                SerenataRoutePlan::query()->updateOrCreate(
                    [
                        'organization_id' => $organizationId,
                        'serenata_id' => $serenataId,
                        'plan_date' => $planDate,
                        'mariachi_key' => $mariachiKey,
                    ],
                    [
                        'created_by_user_id' => (int) $request->user()->id,
                        'city' => $city !== '' ? $city : null,
                        'mariachi_label' => $mariachiLabel !== '' ? $mariachiLabel : null,
                        'column_key' => $columnKey !== '' ? $columnKey : 'sin-barrio',
                        'column_label' => $columnLabel,
                        'route_order' => $index + 1,
                    ]
                );
            }
        }

        SerenataRoutePlan::query()
            ->where('organization_id', $organizationId)
            ->whereDate('plan_date', $planDate)
            ->where('mariachi_key', $mariachiKey)
            ->when($allIds->isNotEmpty(), fn ($q) => $q->whereNotIn('serenata_id', $allIds->all()))
            ->delete();

        return response()->json([
            'success' => true,
            'message' => 'Ruta actualizada.',
        ]);
    }

    public function optimize(Request $request)
    {
        $organizationId = $this->ensureOwnerOrgId($request);
        abort_if($organizationId <= 0, 403);

        $validated = $request->validate([
            'date' => ['required', 'date'],
            'city' => ['nullable', 'string', 'max:120'],
            'cities' => ['nullable', 'array'],
            'cities.*' => ['string', 'max:120'],
            'mariachi_key' => ['required', 'string', 'max:120'],
            'mariachi_label' => ['nullable', 'string', 'max:190'],
        ]);

        $planDate = (string) $validated['date'];
        $cities = $this->selectedCities($validated);
        $city = count($cities) === 1 ? $cities[0] : null;
        $mariachiKey = trim((string) $validated['mariachi_key']);
        $mariachiLabel = trim((string) ($validated['mariachi_label'] ?? ''));

        $mariachiGroupId = $this->resolveInternalGroupIdFromKey($mariachiKey);

        $serenatas = OwnerWorkspace::applyScope(
            $this->plannerQuery($organizationId, $planDate, $cities, $mariachiGroupId)
                ->orderBy('locality')
                ->orderBy('neighborhood')
                ->orderBy('service_address'),
            $request
        )->get();

        $grouped = $serenatas->groupBy(function (Serenata $serenata) {
            $label = trim((string) ($serenata->neighborhood ?: 'Sin barrio'));
            return $label !== '' ? $label : 'Sin barrio';
        });

        foreach ($grouped as $label => $items) {
            $columnKey = Str::slug($label);
            if ($columnKey === '') {
                $columnKey = 'sin-barrio';
            }

            foreach ($items->values() as $index => $serenata) {
                SerenataRoutePlan::query()->updateOrCreate(
                    [
                        'organization_id' => $organizationId,
                        'serenata_id' => $serenata->id,
                        'plan_date' => $planDate,
                        'mariachi_key' => $mariachiKey,
                    ],
                    [
                        'created_by_user_id' => (int) $request->user()->id,
                        'city' => $city !== '' ? $city : null,
                        'mariachi_label' => $mariachiLabel !== '' ? $mariachiLabel : null,
                        'column_key' => $columnKey,
                        'column_label' => $label,
                        'route_order' => $index + 1,
                    ]
                );
            }
        }

        return response()->json([
            'success' => true,
            'message' => 'Ruta optimizada por hora/barrio/dirección.',
        ]);
    }

    private function cardHtml(Serenata $serenata): string
    {
        $time = trim((string) ($serenata->event_time ?? ''));
        $neighborhood = trim((string) ($serenata->neighborhood ?? 'Sin barrio'));
        $address = trim((string) ($serenata->service_address ?? 'Sin dirección'));
        $client = trim((string) ($serenata->payer_name ?? 'Cliente'));
        $status = trim((string) ($serenata->service_status ?? 'Pendiente'));
        $viewUrl = route('owner.serenatas.show', ['serenata' => $serenata->id, 'embed' => 1]);

        return sprintf(
            '<div class="serenata-kanban-card" data-serenata-id="%d" data-view-url="%s">
                <div class="d-flex justify-content-between align-items-center mb-2">
                    <span class="badge bg-label-primary">%s</span>
                    <button type="button" class="btn btn-sm btn-icon btn-text-secondary btn-open-serenata-view" data-view-url="%s" title="Ver detalle">
                        <i class="icon-base ti tabler-eye icon-18px"></i>
                    </button>
                </div>
                <div class="fw-semibold text-truncate">%s</div>
                <div class="text-body-secondary small text-truncate">%s</div>
                <div class="text-body-secondary small text-truncate">%s</div>
                <div class="mt-2"><span class="badge bg-label-info">%s</span></div>
            </div>',
            $serenata->id,
            e($viewUrl),
            e($time !== '' ? $time : '--:--'),
            e($viewUrl),
            e($client),
            e($neighborhood),
            e($address),
            e($status)
        );
    }
}
