<?php
/**
 * Door 3 registration helpers for B8 composition abilities.
 *
 * @package LolaCoreForBricks
 */

declare(strict_types=1);

namespace LolaCoreForBricks;

use LolaCoreForBricks\Handlers\CompositionHandler;
use LolaCoreForBricks\Support\AbilityI18n;

/**
 * Registers composition abilities on the WordPress Abilities API.
 */
final class CompositionWpRegistrar
{
    public function registerAbilities(WPAbilitiesRegistrar $registrar): void
    {
        if (!function_exists('wp_register_ability')) {
            return;
        }

        foreach (CompositionAbilityMap::entries() as $actionId => $entry) {
            if ($entry['type'] === 'read') {
                $registrar->registerKnowledgeReadAbility(
                    $entry['slug'],
                    AbilityI18n::label($actionId),
                    AbilityI18n::description($actionId),
                    $this->inputSchema($actionId),
                    $this->outputSchema($actionId),
                    [$this, $this->callbackMethod($actionId)]
                );
                continue;
            }

            $registrar->registerMappedWriteAbility(
                $entry['slug'],
                AbilityI18n::label($actionId),
                AbilityI18n::description($actionId),
                $this->inputSchema($actionId),
                $this->outputSchema($actionId),
                [$this, $this->callbackMethod($actionId)],
                $entry['destructive'],
                $entry['idempotent']
            );
        }
    }

    /**
     * @param array<string, mixed> $input
     * @return array<string, mixed>
     */
    public function executeGetPageStructure(array $input): array
    {
        return (new CompositionHandler())->getPageStructure($input);
    }

    /**
     * @param array<string, mixed> $input
     * @return array<string, mixed>
     */
    public function executeInsertElement(array $input): array
    {
        return $this->dispatch('insertElement', $input);
    }

    /**
     * @param array<string, mixed> $input
     * @return array<string, mixed>
     */
    public function executeMoveElement(array $input): array
    {
        return $this->dispatch('moveElement', $input);
    }

    /**
     * @param array<string, mixed> $input
     * @return array<string, mixed>
     */
    public function executeDuplicateElement(array $input): array
    {
        return $this->dispatch('duplicateElement', $input);
    }

    /**
     * @param array<string, mixed> $input
     * @return array<string, mixed>
     */
    public function executeDeleteElement(array $input): array
    {
        return $this->dispatch('deleteElement', $input);
    }

    /**
     * @param array<string, mixed> $input
     * @return array<string, mixed>
     */
    private function dispatch(string $method, array $input): array
    {
        $dryRun = (bool) ($input['dry_run'] ?? false);

        return (new CompositionHandler())->{$method}($input, $dryRun);
    }

    private function callbackMethod(string $actionId): string
    {
        return match ($actionId) {
            'get_page_structure' => 'executeGetPageStructure',
            'insert_element'     => 'executeInsertElement',
            'move_element'       => 'executeMoveElement',
            'duplicate_element'  => 'executeDuplicateElement',
            'delete_element'     => 'executeDeleteElement',
            default              => 'executeGetPageStructure',
        };
    }

    /**
     * @return array<string, array<string, mixed>>
     */
    private function inputSchema(string $actionId): array
    {
        $page = [
            'post_id' => ['type' => 'integer'],
            'area'    => ['type' => 'string'],
        ];
        $dryRun = ['dry_run' => ['type' => 'boolean']];
        $place  = [
            'parent_id'    => ['type' => 'string'],
            'position'     => ['type' => 'string'],
            'reference_id' => ['type' => 'string'],
        ];

        return match ($actionId) {
            'get_page_structure' => array_merge($page, [
                'depth'  => ['type' => 'integer'],
                'expand' => ['type' => 'string'],
            ]),
            'insert_element' => array_merge($dryRun, $page, $place, [
                'name'     => ['type' => 'string'],
                'settings' => ['type' => 'object'],
            ]),
            'move_element' => array_merge($dryRun, $page, $place, [
                'element_id' => ['type' => 'string'],
            ]),
            'duplicate_element', 'delete_element' => array_merge($dryRun, $page, [
                'element_id' => ['type' => 'string'],
            ]),
            default => $page,
        };
    }

    /**
     * @return array<string, array<string, mixed>>
     */
    private function outputSchema(string $actionId): array
    {
        $base = [
            'success' => ['type' => 'boolean'],
            'message' => ['type' => 'string'],
            'post_id' => ['type' => 'integer'],
            'area'    => ['type' => 'string'],
            'summary' => ['type' => 'array'],
            'totals'  => ['type' => 'object'],
        ];

        return match ($actionId) {
            'get_page_structure' => array_merge($base, [
                'truncated' => ['type' => 'boolean'],
            ]),
            'insert_element' => array_merge($base, [
                'element_id' => ['type' => 'string'],
                'name'       => ['type' => 'string'],
            ]),
            'move_element' => array_merge($base, [
                'element_id' => ['type' => 'string'],
            ]),
            'duplicate_element' => array_merge($base, [
                'element_id'     => ['type' => 'string'],
                'new_element_id' => ['type' => 'string'],
                'id_map'         => ['type' => 'object'],
            ]),
            'delete_element' => array_merge($base, [
                'element_id'    => ['type' => 'string'],
                'removed_count' => ['type' => 'integer'],
            ]),
            default => $base,
        };
    }
}
