<?php

namespace App\Services;

use App\Models\Macro;
use App\Models\MacroAssignment;
use App\Models\Service;
use App\Models\ServiceMacroAssignment;
use App\Models\ServiceMacroOverride;
use Illuminate\Support\Collection;

class MacroResolutionService
{
    /**
     * Returns active (non-hidden) assignments for a given service + part type.
     * Uses service-specific assignments if an override exists, otherwise global defaults.
     */
    public function resolveAssignmentsForPart(Service $service, string $partType): Collection
    {
        $hasOverride = ServiceMacroOverride::where('service_id', $service->id)
            ->where('part_type', $partType)
            ->exists();

        if ($hasOverride) {
            $rows = ServiceMacroAssignment::with(['macro', 'label'])
                ->where('service_id', $service->id)
                ->where('part_type', $partType)
                ->orderBy('order')
                ->get();
        } else {
            $rows = MacroAssignment::with(['macro', 'label'])
                ->where('part_type', $partType)
                ->orderBy('order')
                ->get();
        }

        return $rows
            ->reject(fn ($r) => $r->macro === null || $r->macro->isHidden())
            ->reject(fn ($r) => $r->position === 'by_label' && ($r->label === null || $r->label->isHidden()));
    }

    /**
     * Returns the macro export data for macros that apply to a specific slide.
     *
     * @param  array  $slideContext  ['index' => int, 'total' => int, 'label_id' => int|null]
     * @return array<int, array{name: string, uuid: string, collectionName: string, collectionUuid: string}>
     */
    public function macrosForSlide(Service $service, string $partType, array $slideContext): array
    {
        $assignments = $this->resolveAssignmentsForPart($service, $partType);

        $matched = $assignments->filter(function ($a) use ($slideContext) {
            return match ($a->position) {
                'all_slides' => true,
                'first_slide' => $slideContext['index'] === 0,
                'last_slide' => $slideContext['index'] === $slideContext['total'] - 1,
                'by_label' => isset($slideContext['label_id'])
                    && (int) $a->label_id === (int) $slideContext['label_id'],
                default => false,
            };
        });

        // The ProPresenter parser stores only ONE macro per slide (consumers keep
        // $macros[0]). Order the matches so the MOST SPECIFIC position wins,
        // independent of the configured `order`: by_label > first_slide/last_slide
        // > all_slides. Ties break by `order`. This stops a broad all_slides
        // assignment from masking a first_slide/last_slide macro on the very
        // first/last slide of a part.
        $sorted = $matched
            ->sortBy(fn ($a) => $this->positionSpecificity($a->position) * 100000 + (int) $a->order)
            ->values();

        return $sorted->map(fn ($a) => $this->toExportArray($a->macro))->values()->all();
    }

    /**
     * Precedence rank for a macro position when several assignments match a single
     * slide and only one macro can be emitted. Lower rank = more specific = wins.
     */
    private function positionSpecificity(string $position): int
    {
        return match ($position) {
            'by_label' => 0,
            'first_slide', 'last_slide' => 1,
            'all_slides' => 2,
            default => 3,
        };
    }

    /**
     * Returns the count of active assignments for a service + part (for UI badges).
     */
    public function countAssignmentsForPart(Service $service, string $partType): int
    {
        return $this->resolveAssignmentsForPart($service, $partType)->count();
    }

    private function toExportArray(Macro $macro): array
    {
        $collection = $macro->collections()->first();

        return [
            'name' => $macro->name,
            'uuid' => $macro->uuid,
            'collectionName' => $collection?->name ?? '--MAIN--',
            'collectionUuid' => $collection?->uuid ?? '8D02FC57-83F8-4042-9B90-81C229728426',
        ];
    }
}
