<?php
/**
 * Reads and writes Bricks page element trees (content, header, footer).
 *
 * @package LolaCoreForBricks
 */

declare(strict_types=1);

namespace LolaCoreForBricks\Gateways;

use LolaCoreForBricks\Support\BricksCodeGuard;
use LolaCoreForBricks\Support\BricksMetaKeys;
use LolaCoreForBricks\Support\CssRegenerator;

/**
 * Persists one page area. update_post_meta triggers B1 reindex.
 */
class ElementGateway
{
    public function __construct(
        private readonly ?CssRegenerator $cssRegenerator = null
    ) {
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function read(int $postId, string $area): array
    {
        $metaKey = $this->metaKeyForArea($area);
        if ($metaKey === null || $postId <= 0) {
            return [];
        }

        $elements = get_post_meta($postId, $metaKey, true);

        return is_array($elements) ? array_values($elements) : [];
    }

    /**
     * @param array<int, array<string, mixed>> $elements
     * @return array<string, mixed>
     */
    public function write(int $postId, string $area, array $elements, bool $dryRun = false): array
    {
        $metaKey = $this->metaKeyForArea($area);
        if ($metaKey === null) {
            return $this->failure('I did not catch that page area. Did you mean content, header, or footer?', 'invalid_area');
        }

        if ($postId <= 0 || get_post($postId) === null) {
            return $this->failure('I could not find that page. It may have been deleted.', 'post_not_found');
        }

        $guard = BricksCodeGuard::rejectionResponse($elements);
        if ($guard !== null) {
            return $guard;
        }

        if ($dryRun) {
            return [
                'success'  => true,
                'dry_run'  => true,
                'message'  => 'Write preview is ready.',
                'post_id'  => $postId,
                'area'     => $area,
                'elements' => count($elements),
            ];
        }

        $this->snapshotRevision($postId);
        update_post_meta($postId, $metaKey, array_values($elements));
        ($this->cssRegenerator ?? new CssRegenerator())->afterPageWrite($postId, $area);

        return [
            'success'        => true,
            'message'        => 'The page structure is saved. Refresh the front to see it.',
            'post_id'        => $postId,
            'area'           => $area,
            'elements'       => count($elements),
            'revision_saved' => true,
        ];
    }

    public function resolveArea(string $area): string
    {
        $area = strtolower(trim($area));

        return in_array($area, [
            BricksMetaKeys::AREA_CONTENT,
            BricksMetaKeys::AREA_HEADER,
            BricksMetaKeys::AREA_FOOTER,
        ], true) ? $area : BricksMetaKeys::AREA_CONTENT;
    }

    private function snapshotRevision(int $postId): int
    {
        if (!function_exists('wp_save_post_revision')) {
            return 0;
        }

        if (function_exists('add_filter')) {
            add_filter('wp_save_post_revision_check_for_changes', '__return_false');
        }

        $revisionId = (int) wp_save_post_revision($postId);

        if (function_exists('remove_filter')) {
            remove_filter('wp_save_post_revision_check_for_changes', '__return_false');
        }

        if ($revisionId <= 0 || !function_exists('update_metadata')) {
            return $revisionId;
        }

        foreach (BricksMetaKeys::pageMetaKeyToArea() as $metaKey => $_area) {
            $current = get_post_meta($postId, $metaKey, true);
            if (is_array($current) && $current !== []) {
                update_metadata('post', $revisionId, $metaKey, $current);
            }
        }

        return $revisionId;
    }

    private function metaKeyForArea(string $area): ?string
    {
        $map = array_flip(BricksMetaKeys::pageMetaKeyToArea());

        return $map[$area] ?? null;
    }

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