<?php
/**
 * Read-side queries for the design-system index.
 *
 * @package LolaCoreForBricks
 */

declare(strict_types=1);

namespace LolaCoreForBricks\Index;

use LolaCoreForBricks\Support\BricksMetaKeys;

/**
 * Answers usage questions from the persisted edge table.
 */
class IndexReader
{
    public function __construct(
        private readonly ?IndexState $state = null,
        private readonly ?IndexRepository $repository = null
    ) {
    }

    /**
     * @return array{indexed: int, total: int, complete: bool}
     */
    public function coverage(): array
    {
        $state = ($this->state ?? new IndexState())->get();
        $total = 1 + (int) ($state['total_pages'] ?? 0);
        $done  = (int) ($state['globals_indexed'] ?? false) + (int) ($state['indexed_pages'] ?? 0);

        if ($total <= 0) {
            $total = 1;
        }

        $indexed = min($done, $total);
        $complete = (bool) ($state['finished'] ?? false) && $indexed >= $total;

        return [
            'indexed'  => $indexed,
            'total'    => $total,
            'complete' => $complete,
        ];
    }

    public function isBuilt(): bool
    {
        return $this->coverage()['complete'];
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function usageOf(string $type, string $id): array
    {
        if ($type === '' || $id === '') {
            return [];
        }

        return ($this->repository ?? new IndexRepository())->findByRef($type, $id);
    }

    /**
     * @return string[]
     */
    public function unusedOf(string $type): array
    {
        if ($type !== BricksMetaKeys::REF_CLASS) {
            return [];
        }

        $defined = [];
        $classes = get_option(BricksMetaKeys::OPTION_GLOBAL_CLASSES, []);
        if (is_array($classes)) {
            foreach ($classes as $class) {
                if (is_array($class) && !empty($class['id'])) {
                    $defined[] = (string) $class['id'];
                }
            }
        }

        $used = ($this->repository ?? new IndexRepository())->distinctRefIds(BricksMetaKeys::REF_CLASS);

        return array_values(array_diff($defined, $used));
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function refsInPost(int $postId): array
    {
        if ($postId <= 0) {
            return [];
        }

        return ($this->repository ?? new IndexRepository())->findByPost($postId);
    }

    /**
     * Groups index edges by post for human-readable usage answers.
     *
     * @return array<int, array<string, mixed>>
     */
    public function usageGroupedByPost(string $type, string $id): array
    {
        $edges = $this->usageOf($type, $id);
        if ($edges === []) {
            return [];
        }

        $grouped = [];
        foreach ($edges as $edge) {
            $postId = (int) ($edge['post_id'] ?? 0);
            $key    = (string) $postId;

            if (!isset($grouped[$key])) {
                $grouped[$key] = [
                    'post_id'       => $postId,
                    'post_title'    => $postId > 0 ? (string) get_the_title($postId) : 'globals',
                    'element_count' => 0,
                    'areas'         => [],
                    'components'    => [],
                ];
            }

            ++$grouped[$key]['element_count'];
            $area = (string) ($edge['area'] ?? '');
            if ($area !== '' && !in_array($area, $grouped[$key]['areas'], true)) {
                $grouped[$key]['areas'][] = $area;
            }

            if (($edge['element_name'] ?? '') === 'component' || $area === BricksMetaKeys::AREA_CONTENT) {
                $elementId = (string) ($edge['element_id'] ?? '');
                if ($elementId !== '' && !in_array($elementId, $grouped[$key]['components'], true)) {
                    $grouped[$key]['components'][] = $elementId;
                }
            }
        }

        return array_values($grouped);
    }
}
