<?php

declare(strict_types=1);

namespace Maestro\Core\DataTables;

use Maestro\Core\Models\DataTableColumnDefault;

/**
 * Applies optional instance-level column defaults (visible keys + order) on top of PHP column definitions.
 */
final class DataTableColumnDefaultsMerger
{
    /**
     * @param  array<int, array<string, mixed>>  $defsFromResource
     * @return array<int, array{key: string, header: string, type: string, searchable: bool, filterable: bool, defaultVisible: bool}>
     */
    public function buildApiColumns(string $resourceKey, array $defsFromResource): array
    {
        $row = DataTableColumnDefault::query()->where('resource_key', $resourceKey)->first();

        /** @var list<string>|null $overrideOrder */
        $overrideOrder = $row ? ($row->visible_keys ?? null) : null;

        if ($overrideOrder === null || $overrideOrder === []) {
            return $this->mapDefsPlain($defsFromResource);
        }

        $keyToDef = [];
        foreach ($defsFromResource as $def) {
            $k = $def['key'] ?? null;
            if ($k !== null) {
                $keyToDef[$k] = $def;
            }
        }

        $allowed = array_keys($keyToDef);
        $visibleSet = [];
        $orderedVisible = [];
        foreach ($overrideOrder as $k) {
            if (! isset($keyToDef[$k])) {
                continue;
            }
            $visibleSet[$k] = true;
            $orderedVisible[] = $k;
        }

        if ($orderedVisible === []) {
            return $this->mapDefsPlain($defsFromResource);
        }

        $out = [];
        foreach ($orderedVisible as $k) {
            $out[] = $this->mapOne($keyToDef[$k], true);
        }

        foreach ($defsFromResource as $def) {
            $k = $def['key'] ?? null;
            if ($k === null || isset($visibleSet[$k])) {
                continue;
            }
            $out[] = $this->mapOne($def, false);
        }

        return $out;
    }

    /**
     * @param  array<int, array<string, mixed>>  $defsFromResource
     * @return array<int, array{key: string, header: string, type: string, searchable: bool, filterable: bool, defaultVisible: bool}>
     */
    private function mapDefsPlain(array $defsFromResource): array
    {
        $out = [];
        foreach ($defsFromResource as $def) {
            $k = $def['key'] ?? null;
            if ($k === null) {
                continue;
            }
            $out[] = $this->mapOne($def, (bool) ($def['defaultVisible'] ?? true));
        }

        return $out;
    }

    /**
     * @param  array<string, mixed>  $def
     * @return array{key: string, header: string, type: string, searchable: bool, filterable: bool, defaultVisible: bool}
     */
    private function mapOne(array $def, bool $defaultVisible): array
    {
        $key = $def['key'];

        return [
            'key' => $key,
            'header' => $def['label'] ?? $key,
            'type' => $def['type'] ?? 'text',
            'searchable' => (bool) ($def['searchable'] ?? false),
            'filterable' => (bool) ($def['filterable'] ?? false),
            'defaultVisible' => $defaultVisible,
        ];
    }
}
