<?php
/**
 * Template create, content, conditions, duplicate, and trash.
 *
 * @package LolaCoreForBricks
 */

declare(strict_types=1);

namespace LolaCoreForBricks\Handlers;

use LolaCoreForBricks\BricksEnv;
use LolaCoreForBricks\Elements\ElementTree;
use LolaCoreForBricks\Elements\IdGenerator;
use LolaCoreForBricks\Gateways\ElementGateway;
use LolaCoreForBricks\Gateways\TemplateGateway;
use LolaCoreForBricks\Support\BricksCodeGuard;
use LolaCoreForBricks\Support\TemplateConditionPreview;

/**
 * Executes B11 template write abilities.
 */
class TemplateWriteHandler
{
    public function __construct(
        private readonly ?TemplateGateway $templates = null,
        private readonly ?ElementGateway $elements = null,
        private readonly ?TemplateConditionPreview $preview = null,
        private readonly ?IdGenerator $ids = null
    ) {
    }

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

        $title      = trim((string) ($params['title'] ?? $params['name'] ?? ''));
        $type       = $this->templates()->normalizeType((string) ($params['type'] ?? ''));
        $conditions = $this->normalizeConditions($params);
        $preview    = $this->preview()->preview($conditions);

        if ($title === '') {
            return $this->failure('I need a name for the template.', 'missing_title');
        }

        if ($type === '') {
            return $this->failure('I need the template type. Header, footer, content, or archive?', 'missing_type');
        }

        if ($dryRun) {
            return [
                'success'    => true,
                'dry_run'    => true,
                'message'    => 'Create preview is ready.',
                'title'      => $title,
                'type'       => $type,
                'conditions' => $conditions,
                'impact'     => $this->impactFromPreview($preview),
                'preview'    => $preview,
            ];
        }

        $created = $this->templates()->createTemplate($title, $type, (string) ($params['status'] ?? 'publish'));
        if (($created['success'] ?? false) !== true) {
            return $created;
        }

        if ($conditions !== []) {
            $written = $this->templates()->writeConditions((int) $created['template_id'], $conditions);
            if (($written['success'] ?? false) !== true) {
                return $written;
            }
        }

        return array_merge($created, [
            'message'     => sprintf('I created the %s template. Refresh the front to see it where it applies.', $type),
            'conditions'  => $conditions,
            'impact'      => $this->impactFromPreview($preview),
            'preview'     => $preview,
            'resolution'  => $this->resolutionObjectForPreview($preview),
        ]);
    }

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

        $templateId = absint($params['template_id'] ?? $params['post_id'] ?? 0);
        if (!$this->templates()->isTemplate($templateId)) {
            return $this->failure('That is not a Bricks template.', 'not_a_template');
        }

        $elements = is_array($params['elements'] ?? null) ? $params['elements'] : [];
        $tree     = ElementTree::fromList($elements);
        $error    = $elements === [] ? null : $tree->firstCoherenceError();
        if ($error !== null) {
            return $this->failure(
                'The page tree is inconsistent. I will not write that.',
                $error
            );
        }

        $type = $this->templates()->readType($templateId);
        $area = $this->elements()->resolveArea(
            (string) ($params['area'] ?? $this->templates()->defaultAreaForType($type))
        );

        $written = $this->elements()->write($templateId, $area, $tree->toList(), $dryRun);
        if (($written['success'] ?? false) !== true) {
            return $written;
        }

        return array_merge($written, [
            'message'     => $dryRun
                ? 'Template content preview is ready.'
                : 'I updated the template content. Refresh the front to see it where this template applies.',
            'template_id' => $templateId,
            'type'        => $type,
        ]);
    }

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

        $templateId = absint($params['template_id'] ?? $params['post_id'] ?? 0);
        if (!$this->templates()->isTemplate($templateId)) {
            return $this->failure('That is not a Bricks template.', 'not_a_template');
        }

        $conditions = $this->normalizeConditions($params);
        $preview    = $this->preview()->preview($conditions);

        if ($dryRun) {
            return [
                'success'     => true,
                'dry_run'     => true,
                'message'     => (string) ($preview['summary'] ?? 'Condition preview is ready.'),
                'template_id' => $templateId,
                'conditions'  => $conditions,
                'impact'      => $this->impactFromPreview($preview),
                'preview'     => $preview,
            ];
        }

        $written = $this->templates()->writeConditions($templateId, $conditions);
        if (($written['success'] ?? false) !== true) {
            return $written;
        }

        return array_merge($written, [
            'message'    => (string) ($preview['summary'] ?? 'I updated the template conditions.'),
            'impact'     => $this->impactFromPreview($preview),
            'preview'    => $preview,
            'resolution' => $this->resolutionObjectForPreview($preview),
        ]);
    }

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

        $templateId = absint($params['template_id'] ?? $params['post_id'] ?? 0);
        if (!$this->templates()->isTemplate($templateId)) {
            return $this->failure('That is not a Bricks template.', 'not_a_template');
        }

        if ($dryRun) {
            return [
                'success'     => true,
                'dry_run'     => true,
                'message'     => 'Duplicate preview is ready.',
                'template_id' => $templateId,
                'title'       => 'Copy of ' . (string) get_the_title($templateId),
            ];
        }

        $duplicated = $this->templates()->duplicateTemplate($templateId, $this->elements(), $this->ids());
        if (($duplicated['success'] ?? false) !== true) {
            return $duplicated;
        }

        return array_merge($duplicated, [
            'message' => 'I duplicated the template with new element IDs.',
        ]);
    }

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

        $templateId = absint($params['template_id'] ?? $params['post_id'] ?? 0);
        if (!$this->templates()->isTemplate($templateId)) {
            return $this->failure('That is not a Bricks template.', 'not_a_template');
        }

        if ($dryRun) {
            return [
                'success'     => true,
                'dry_run'     => true,
                'message'     => 'I would move this template to the WordPress trash. You can restore it from there.',
                'template_id' => $templateId,
            ];
        }

        $trashed = $this->templates()->trashTemplate($templateId);
        if (($trashed['success'] ?? false) !== true) {
            return $trashed;
        }

        return array_merge($trashed, [
            'message' => 'I moved the template to the WordPress trash. You can restore it from there.',
        ]);
    }

    /**
     * @param array<string, mixed> $params
     * @return array<int, mixed>
     */
    private function normalizeConditions(array $params): array
    {
        if (!empty($params['entire_site'])) {
            return [['main' => 'any']];
        }

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

        $allowed = ['any', 'frontpage', 'postType', 'archiveType', 'search', 'error', 'terms', 'ids'];
        $normalized = [];
        foreach ($conditions as $condition) {
            if (!is_array($condition)) {
                continue;
            }

            $main = (string) ($condition['main'] ?? '');
            if (!in_array($main, $allowed, true)) {
                continue;
            }

            $row         = $condition;
            $row['main'] = $main;
            $normalized[] = $row;
        }

        return $normalized;
    }

    /**
     * @param array<string, mixed> $preview
     * @return array<string, mixed>
     */
    private function impactFromPreview(array $preview): array
    {
        return [
            'pages'      => (int) ($preview['pages'] ?? 0),
            'page_names' => is_array($preview['page_names'] ?? null) ? $preview['page_names'] : [],
            'scope'      => (string) ($preview['scope'] ?? ''),
        ];
    }

    /**
     * MCP output schema types resolution as object. PHP [] JSON-encodes as array.
     *
     * @param array<string, mixed> $preview
     * @return array<string, mixed>|\stdClass
     */
    private function resolutionObjectForPreview(array $preview): array|\stdClass
    {
        $exampleId = absint($preview['page_ids'][0] ?? 0);
        if ($exampleId <= 0) {
            return new \stdClass();
        }

        $resolved = $this->templates()->resolveTemplateForPage(['post_id' => $exampleId]);
        if (isset($resolved['error']) || $resolved === []) {
            return new \stdClass();
        }

        return $resolved;
    }

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

        if (!BricksEnv::userCanReadDesignSystem()) {
            return $this->failure('I need administrator access to change Bricks templates.', '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 templates(): TemplateGateway
    {
        return $this->templates ?? new TemplateGateway();
    }

    private function elements(): ElementGateway
    {
        return $this->elements ?? new ElementGateway();
    }

    private function preview(): TemplateConditionPreview
    {
        return $this->preview ?? new TemplateConditionPreview();
    }

    private function ids(): IdGenerator
    {
        return $this->ids ?? new IdGenerator();
    }
}
