<?php
/**
 * Knowledge read handlers for Bricks design-system layer 1.
 *
 * @package LolaCoreForBricks
 */

declare(strict_types=1);

namespace LolaCoreForBricks\Handlers;

use LolaCoreForBricks\BricksEnv;
use LolaCoreForBricks\Gateways\ClassGateway;
use LolaCoreForBricks\Gateways\ComponentGateway;
use LolaCoreForBricks\Gateways\TemplateGateway;
use LolaCoreForBricks\Gateways\ThemeStyleGateway;
use LolaCoreForBricks\Gateways\VariableGateway;
use LolaCoreForBricks\Support\ControlIntrospector;
use LolaCoreForBricks\Support\OperatingGuide;

/**
 * Executes B2 knowledge read abilities.
 */
class KnowledgeHandler
{
    public function __construct(
        private readonly ?ClassGateway $classGateway = null,
        private readonly ?VariableGateway $variableGateway = null,
        private readonly ?ThemeStyleGateway $themeStyleGateway = null,
        private readonly ?ComponentGateway $componentGateway = null,
        private readonly ?TemplateGateway $templateGateway = null,
        private readonly ?ControlIntrospector $controlIntrospector = null
    ) {
    }

    /**
     * @param array<string, mixed> $params
     * @return array<string, mixed>
     */
    public function getGlobalClasses(array $params = []): array
    {
        return $this->wrapRead(
            ($this->classGateway ?? new ClassGateway())->getGlobalClasses($params),
            'Global classes are ready.'
        );
    }

    /**
     * @param array<string, mixed> $params
     * @return array<string, mixed>
     */
    public function getClassUsage(array $params = []): array
    {
        $data = ($this->classGateway ?? new ClassGateway())->getClassUsage($params);
        if (isset($data['error'])) {
            return $this->errorForUsage($data);
        }

        return $this->wrapRead($data, 'Class usage is ready.');
    }

    /**
     * @param array<string, mixed> $params
     * @return array<string, mixed>
     */
    public function getGlobalVariables(array $params = []): array
    {
        return $this->wrapRead(
            ($this->variableGateway ?? new VariableGateway())->getGlobalVariables($params),
            'Global variables are ready.'
        );
    }

    /**
     * @param array<string, mixed> $params
     * @return array<string, mixed>
     */
    public function getVariableUsage(array $params = []): array
    {
        $data = ($this->variableGateway ?? new VariableGateway())->getVariableUsage($params);
        if (isset($data['error'])) {
            return $this->errorForUsage($data);
        }

        return $this->wrapRead($data, 'Variable usage is ready.');
    }

    /**
     * @param array<string, mixed> $params
     * @return array<string, mixed>
     */
    public function getThemeStyles(array $params = []): array
    {
        return $this->wrapRead(
            ($this->themeStyleGateway ?? new ThemeStyleGateway())->getThemeStyles($params),
            'Theme styles are ready.'
        );
    }

    /**
     * @param array<string, mixed> $params
     * @return array<string, mixed>
     */
    public function getComponents(array $params = []): array
    {
        return $this->wrapRead(
            ($this->componentGateway ?? new ComponentGateway())->getComponents($params),
            'Components are ready.'
        );
    }

    /**
     * @param array<string, mixed> $params
     * @return array<string, mixed>
     */
    public function getComponentUsage(array $params = []): array
    {
        $data = ($this->componentGateway ?? new ComponentGateway())->getComponentUsage($params);
        if (isset($data['error'])) {
            return $this->errorForUsage($data);
        }

        return $this->wrapRead($data, 'Component usage is ready.');
    }

    /**
     * @param array<string, mixed> $params
     * @return array<string, mixed>
     */
    public function getTemplates(array $params = []): array
    {
        return $this->wrapRead(
            ($this->templateGateway ?? new TemplateGateway())->getTemplates($params),
            'Templates are ready.'
        );
    }

    /**
     * @param array<string, mixed> $params
     * @return array<string, mixed>
     */
    public function resolveTemplateForPage(array $params = []): array
    {
        $data = ($this->templateGateway ?? new TemplateGateway())->resolveTemplateForPage($params);
        if (isset($data['error'])) {
            return $this->errorForUsage($data);
        }

        return $this->wrapRead($data, 'Template resolution is ready.');
    }

    /**
     * @param array<string, mixed> $params
     * @return array<string, mixed>
     */
    public function describeElementControls(array $params = []): array
    {
        $elementName = (string) ($params['element_name'] ?? '');
        $group       = isset($params['group']) ? (string) $params['group'] : null;

        $data = ($this->controlIntrospector ?? new ControlIntrospector())->describe($elementName, $group);
        if (!($data['success'] ?? false)) {
            return [
                'success' => false,
                'message' => (string) ($data['message'] ?? 'I could not describe that element.'),
            ];
        }

        unset($data['success']);

        return $this->wrapRead($data, 'Element controls are ready.');
    }

    /**
     * @param array<string, mixed> $params
     * @return array<string, mixed>
     */
    public function getBricksOperatingGuide(array $params = []): array
    {
        $topic = isset($params['topic']) ? (string) $params['topic'] : null;
        $data  = OperatingGuide::build($topic);

        if (isset($data['error'])) {
            return [
                'success'          => false,
                'message'          => 'I do not have a guide topic with that name yet. I listed the ones I do have.',
                'error'            => (string) $data['error'],
                'topic'            => (string) ($data['topic'] ?? ''),
                'available_topics' => is_array($data['available_topics'] ?? null) ? $data['available_topics'] : [],
            ];
        }

        return $this->wrapRead($data, 'Bricks operating guide is ready.');
    }

    /**
     * @param array<string, mixed> $data
     * @return array<string, mixed>
     */
    private function wrapRead(array $data, string $message): array
    {
        $guard = $this->environmentGuard();
        if ($guard !== null) {
            return $guard;
        }

        return array_merge(
            [
                'success' => true,
                'message' => $message,
            ],
            $data
        );
    }

    /**
     * @param array<string, mixed> $data
     * @return array<string, mixed>
     */
    private function errorForUsage(array $data): array
    {
        $guard = $this->environmentGuard();
        if ($guard !== null) {
            return $guard;
        }

        $message = match ($data['error'] ?? '') {
            'missing_class'      => 'Tell me which global class to inspect (id or name).',
            'class_not_found'    => 'I could not find that global class on this site.',
            'missing_variable'   => 'Tell me which variable to inspect.',
            'missing_component'  => 'Tell me which component to inspect.',
            'component_not_found'=> 'I could not find that component on this site.',
            'missing_post_id'    => 'Tell me which page to resolve templates for.',
            'post_not_found'     => 'I could not find that page.',
            'bricks_resolver_unavailable' => 'Bricks template resolver is not available on this site.',
            default              => 'That read could not complete.',
        };

        return [
            'success' => false,
            'message' => $message,
            'data'    => $data,
        ];
    }

    /**
     * @return array<string, mixed>|null
     */
    private function environmentGuard(): ?array
    {
        if (!BricksEnv::userCanReadDesignSystem()) {
            return [
                'success' => false,
                'message' => 'I need administrator access to read the Bricks design system.',
            ];
        }

        if (!BricksEnv::isAvailable() || !BricksEnv::meetsFloor()) {
            return [
                'success' => false,
                'message' => 'Bricks is not available on this site yet. Activate Bricks '
                    . BricksEnv::FLOOR_VERSION . ' or newer, then try again.',
            ];
        }

        return null;
    }
}
