<?php
/**
 * WooCommerce shop-surface abilities inside Bricks.
 *
 * @package LolaCoreForBricks
 */

declare(strict_types=1);

namespace LolaCoreForBricks\Handlers;

use LolaCoreForBricks\BricksEnv;
use LolaCoreForBricks\Gateways\WooSurfaceGateway;
use LolaCoreForBricks\Support\BricksCodeGuard;
use LolaCoreForBricks\Support\BricksMetaKeys;
use LolaCoreForBricks\Support\ControlIntrospector;
use LolaCoreForBricks\Support\WooProductCatalog;
use LolaCoreForBricks\Support\WooShopTemplates;

/**
 * Discovery and shortcuts for the Bricks WooCommerce storefront.
 */
class WooSurfaceHandler
{
    public function __construct(
        private readonly ?WooSurfaceGateway $surface = null,
        private readonly ?TemplateWriteHandler $templates = null,
        private readonly ?CompositionHandler $composition = null,
        private readonly ?ControlIntrospector $introspector = null
    ) {
    }

    /**
     * @param array<string, mixed> $params
     * @return array<string, mixed>
     */
    public function getWooBricksSurface(array $params = []): array
    {
        unset($params);

        $guard = $this->environmentGuard([], false);
        if ($guard !== null) {
            return $guard;
        }

        $data    = $this->surface()->getSurface();
        $missing = is_array($data['templates']['missing'] ?? null) ? $data['templates']['missing'] : [];
        $labels  = array_map(
            static fn (array $row): string => (string) ($row['label'] ?? $row['kind'] ?? ''),
            $missing
        );

        $message = $missing === []
            ? 'Your shop templates are in place. I can add product elements next.'
            : sprintf(
                'Your shop is missing the %s template%s.',
                $this->joinLabels($labels),
                count($labels) === 1 ? '' : 's'
            );

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

    /**
     * @param array<string, mixed> $params
     * @return array<string, mixed>
     */
    public function createWooTemplate(array $params, bool $dryRun = false): array
    {
        $guard = $this->environmentGuard($params, true);
        if ($guard !== null) {
            return $guard;
        }

        $kind = WooShopTemplates::resolve((string) ($params['kind'] ?? $params['type'] ?? ''));
        if ($kind === null) {
            return $this->failure(
                'I need the shop template kind. Product, archive, cart, checkout, or account?',
                'missing_kind'
            );
        }

        $title = trim((string) ($params['title'] ?? $params['name'] ?? ''));
        if ($title === '') {
            $title = $kind['label'];
        }

        $conditions = $this->requestedConditions($params);
        if ($conditions === null) {
            $conditions = $kind['conditions'];
        }

        $created = $this->templates()->createTemplate(
            [
                'title'      => $title,
                'type'       => $kind['bricks_type'],
                'status'     => (string) ($params['status'] ?? 'publish'),
                'conditions' => $conditions,
            ],
            $dryRun
        );

        if (($created['success'] ?? false) !== true) {
            return $created;
        }

        return array_merge($created, [
            'kind'        => $kind['kind'],
            'bricks_type' => $kind['bricks_type'],
            'message'     => $dryRun
                ? 'Create preview is ready.'
                : sprintf(
                    'I created the %s template. Refresh the front to see it where it applies.',
                    strtolower($kind['label'])
                ),
        ]);
    }

    /**
     * @param array<string, mixed> $params
     * @return array<string, mixed>
     */
    public function addProductElement(array $params, bool $dryRun = false): array
    {
        $guard = $this->environmentGuard($params, true);
        if ($guard !== null) {
            return $guard;
        }

        $name = WooProductCatalog::resolveElement((string) ($params['name'] ?? $params['element'] ?? ''));
        if ($name === '') {
            return $this->failure(
                'I did not catch that shop element. Did you mean product title, price, or gallery?',
                'unknown_element'
            );
        }

        $settings = is_array($params['settings'] ?? null) ? $params['settings'] : [];
        if ($settings !== []) {
            $invalid = $this->introspector()->validateSettings($name, $settings);
            if ($invalid !== null) {
                return $invalid;
            }
        }

        $inserted = $this->composition()->insertElement(
            [
                'post_id'      => $params['post_id'] ?? $params['postId'] ?? 0,
                'area'         => $params['area'] ?? BricksMetaKeys::AREA_CONTENT,
                'parent_id'    => $params['parent_id'] ?? '',
                'position'     => $params['position'] ?? 'child',
                'reference_id' => $params['reference_id'] ?? '',
                'name'         => $name,
                'settings'     => $settings,
            ],
            $dryRun
        );

        if (($inserted['success'] ?? false) !== true) {
            return $inserted;
        }

        $label = WooProductCatalog::names()[$name] ?? $name;

        return array_merge($inserted, [
            'name'    => $name,
            'group'   => WooProductCatalog::groupFor($name),
            'message' => $dryRun
                ? 'Add preview is ready.'
                : sprintf('I added the %s. Refresh the front to see it.', strtolower($label)),
        ]);
    }

    /**
     * @param array<string, mixed> $params
     * @return array<string, mixed>
     */
    public function getProductElementsCatalog(array $params = []): array
    {
        unset($params);

        $guard = $this->environmentGuard([], false);
        if ($guard !== null) {
            return $guard;
        }

        $groups = [];
        foreach (WooProductCatalog::groups() as $id => $group) {
            $elements = [];
            foreach ($group['elements'] as $name => $label) {
                $elements[] = [
                    'name'  => $name,
                    'label' => $label,
                ];
            }

            $groups[] = [
                'id'       => $id,
                'label'    => $group['label'],
                'elements' => $elements,
                'total'    => count($elements),
            ];
        }

        return [
            'success' => true,
            'message' => 'Here are the WooCommerce elements grouped by shop function.',
            'groups'  => $groups,
            'total'   => WooProductCatalog::count(),
        ];
    }

    /**
     * @param array<string, mixed> $params
     * @return array<int, mixed>|null
     */
    private function requestedConditions(array $params): ?array
    {
        if (!isset($params['conditions'])) {
            return null;
        }

        if (!is_array($params['conditions'])) {
            return [];
        }

        return $params['conditions'];
    }

    /**
     * @param string[] $labels
     */
    private function joinLabels(array $labels): string
    {
        $labels = array_values(array_filter($labels, static fn (string $label): bool => $label !== ''));
        if ($labels === []) {
            return 'shop';
        }

        if (count($labels) === 1) {
            return strtolower($labels[0]);
        }

        $last = array_pop($labels);

        return strtolower(implode(', ', $labels) . ' and ' . $last);
    }

    /**
     * @param array<string, mixed> $params
     * @return array<string, mixed>|null
     */
    private function environmentGuard(array $params, bool $write): ?array
    {
        if ($write && ($rejection = BricksCodeGuard::rejectionResponse($params))) {
            return $rejection;
        }

        if (!BricksEnv::isWooCommerceActive()) {
            return $this->failure(
                'WooCommerce is not active on this site. Activate it and try again.',
                'woocommerce_inactive'
            );
        }

        if (!BricksEnv::userCanReadDesignSystem()) {
            return $this->failure('I need administrator access to work with the Bricks shop.', 'forbidden');
        }

        if (!BricksEnv::isAvailable() || !BricksEnv::meetsFloor()) {
            return $this->failure(
                'Bricks is not available on this site yet. Activate Bricks '
                . BricksEnv::FLOOR_VERSION . ' or newer, then try again.',
                'bricks_unavailable'
            );
        }

        return null;
    }

    /**
     * @return array{success: false, message: string, error: string}
     */
    private function failure(string $message, string $error): array
    {
        return [
            'success' => false,
            'message' => $message,
            'error'   => $error,
        ];
    }

    private function surface(): WooSurfaceGateway
    {
        return $this->surface ?? new WooSurfaceGateway();
    }

    private function templates(): TemplateWriteHandler
    {
        return $this->templates ?? new TemplateWriteHandler();
    }

    private function composition(): CompositionHandler
    {
        return $this->composition ?? new CompositionHandler();
    }

    private function introspector(): ControlIntrospector
    {
        return $this->introspector ?? new ControlIntrospector();
    }
}
