<?php

namespace GriffinForms\Includes\Onboarding;

use GriffinForms\Traits\Singleton;

class PlacementService extends \GriffinForms\Format
{
    use Singleton;

    protected $admin_sql;
    protected $content_classifier;
    protected $frontend_url_logger;

    protected function setAdminSql()
    {
        $this->admin_sql = \GriffinForms\Admin\Sql\Format::getInstance();
    }

    protected function setContentClassifier()
    {
        $this->content_classifier = ContentClassifier::getInstance();
    }

    protected function setFrontendUrlLogger()
    {
        $this->frontend_url_logger = new \GriffinForms\Includes\Publication\FrontendUrlLogger();
    }

    public function getShortcode(int $form_id): string
    {
        return '[griffinforms_form id=' . absint($form_id) . ']';
    }

    public function getSubmissionEditUrl(int $submission_id, string $submission_token): string
    {
        $submission_id = absint($submission_id);
        $submission_token = sanitize_text_field($submission_token);

        if ($submission_id <= 0 || $submission_token === '') {
            return '';
        }

        $pro_page_class = '\\GriffinFormsPro\\Includes\\Onboarding\\SubmissionEditPage';
        if (class_exists($pro_page_class) && method_exists($pro_page_class, 'getInstance')) {
            try {
                $edit_page = $pro_page_class::getInstance();
                if (is_object($edit_page) && method_exists($edit_page, 'getEditUrl')) {
                    $url = (string) $edit_page->getEditUrl($submission_id);
                    if ($url !== '') {
                        return (string) apply_filters('griffinforms_submission_edit_url', $url, $submission_id, $submission_token);
                    }
                }
            } catch (\Throwable $throwable) {
            }
        }

        $base_url = apply_filters(
            'griffinforms_submission_edit_base_url',
            '',
            $submission_id,
            $submission_token
        );

        if (!is_string($base_url) || $base_url === '') {
            return '';
        }

        $url = add_query_arg(
            array(
                'gf_sub_id' => $submission_id,
                'gf_sub_token' => $submission_token,
            ),
            esc_url_raw($base_url)
        );

        return (string) apply_filters('griffinforms_submission_edit_url', $url, $submission_id, $submission_token);
    }

    public function getNativeBlockMarkup(int $form_id): string
    {
        $form_id = absint($form_id);
        if ($form_id < 1) {
            return '';
        }

        return '<!-- wp:griffinforms/form ' . wp_json_encode([
            'formId' => $form_id,
        ], JSON_UNESCAPED_SLASHES) . ' /-->';
    }

    public function placeForm(
        int $form_id,
        string $placement_action = 'create_form_only',
        string $placement_target_type = 'page',
        int $placement_target_id = 0,
        string $page_title = ''
    ) {
        $form_id = absint($form_id);
        $placement_action = sanitize_key($placement_action);
        $placement_target_type = sanitize_key($placement_target_type);
        $placement_target_id = absint($placement_target_id);
        $page_title = sanitize_text_field($page_title);

        if (!$this->isValidForm($form_id)) {
            return new \WP_Error('griffinforms_invalid_form', __('Form not found.', 'griffinforms-form-builder'));
        }

        $allowed_actions = ['create_form_only', 'create_and_add_to_new_page', 'create_and_add_to_new_post', 'add_to_existing_content'];
        if (!in_array($placement_action, $allowed_actions, true)) {
            $placement_action = 'create_form_only';
        }

        if ($placement_action === 'create_form_only') {
            return $this->buildStandalonePlacementResult($form_id);
        }

        if ($placement_action === 'create_and_add_to_new_page' || $placement_action === 'create_and_add_to_new_post') {
            $post_type = $placement_action === 'create_and_add_to_new_post' ? 'post' : 'page';
            return $this->createNewContent($form_id, $post_type, $page_title);
        }

        if ($placement_action === 'add_to_existing_content') {
            if ($placement_target_id <= 0) {
                return new \WP_Error('griffinforms_content_target_required', __('The selected content target is required.', 'griffinforms-form-builder'));
            }

            return $this->appendToExistingContent($form_id, $placement_target_id);
        }

        return $this->buildStandalonePlacementResult($form_id);
    }

    public function createNewPage(int $form_id, string $page_title)
    {
        return $this->createNewContent($form_id, 'page', $page_title);
    }

    public function createNewPost(int $form_id, string $post_title)
    {
        return $this->createNewContent($form_id, 'post', $post_title);
    }

    public function appendToExistingContent(int $form_id, int $post_id)
    {
        $form_id = absint($form_id);
        $post_id = absint($post_id);

        if (!$this->isValidForm($form_id)) {
            return new \WP_Error('griffinforms_invalid_form', __('Form not found.', 'griffinforms-form-builder'));
        }

        $post = get_post($post_id);
        if (!$post instanceof \WP_Post) {
            return new \WP_Error('griffinforms_invalid_content', __('The selected content no longer exists.', 'griffinforms-form-builder'));
        }

        if (!in_array($post->post_type, ['page', 'post'], true)) {
            return new \WP_Error('griffinforms_invalid_content_type', __('Only pages and posts are supported in onboarding placement.', 'griffinforms-form-builder'));
        }

        if (!current_user_can('edit_post', $post_id)) {
            return new \WP_Error('griffinforms_cannot_edit_content', __('You do not have permission to edit the selected content.', 'griffinforms-form-builder'));
        }

        $classification = $this->classifyExistingContent($post);
        if (($classification['classification'] ?? 'safe') === 'unsupported') {
            return new \WP_Error(
                'griffinforms_builder_manual_fallback',
                $this->getManualFallbackMessageForClassification($classification),
                [
                    'classification' => $classification,
                ]
            );
        }

        if ($this->contentHasAnyPlacement((string) $post->post_content)) {
            return new \WP_Error(
                'griffinforms_duplicate_shortcode',
                __('This content already contains a GriffinForms form. GriffinForms currently supports only one form per page or post.', 'griffinforms-form-builder')
            );
        }

        $updated_content = (($classification['builder_key'] ?? 'none') === 'gutenberg' && $this->supportsNativeBlockPlacement($post->post_type))
            ? $this->appendNativeBlockToContent((string) $post->post_content, $this->getNativeBlockMarkup($form_id))
            : $this->appendShortcodeToContent((string) $post->post_content, $this->getShortcode($form_id));
        $update_result = wp_update_post([
            'ID' => $post_id,
            'post_content' => $updated_content,
        ], true);

        if (is_wp_error($update_result)) {
            return $update_result;
        }

        return $this->buildPlacementResult($post_id, $form_id, 'add_to_existing_content');
    }

    protected function createNewContent(int $form_id, string $post_type, string $title)
    {
        $form_id = absint($form_id);
        $post_type = sanitize_key($post_type);
        $title = sanitize_text_field($title);

        if (!$this->isValidForm($form_id)) {
            return new \WP_Error('griffinforms_invalid_form', __('Form not found.', 'griffinforms-form-builder'));
        }

        if (!in_array($post_type, ['page', 'post'], true)) {
            return new \WP_Error('griffinforms_invalid_content_type', __('Only pages and posts are supported.', 'griffinforms-form-builder'));
        }

        if ($title === '') {
            return new \WP_Error(
                $post_type === 'post' ? 'griffinforms_post_title_required' : 'griffinforms_page_title_required',
                $post_type === 'post'
                    ? __('Post title is required.', 'griffinforms-form-builder')
                    : __('Page title is required.', 'griffinforms-form-builder')
            );
        }

        $create_cap = $post_type === 'post' ? 'edit_posts' : 'edit_pages';
        $publish_cap = $post_type === 'post' ? 'publish_posts' : 'publish_pages';

        if (!current_user_can($create_cap)) {
            return new \WP_Error(
                $post_type === 'post' ? 'griffinforms_cannot_create_posts' : 'griffinforms_cannot_create_pages',
                $post_type === 'post'
                    ? __('You do not have permission to create posts.', 'griffinforms-form-builder')
                    : __('You do not have permission to create pages.', 'griffinforms-form-builder')
            );
        }

        $post_status = current_user_can($publish_cap) ? 'publish' : 'draft';
        $post_content = $this->supportsNativeBlockPlacement($post_type)
            ? $this->getNativeBlockMarkup($form_id)
            : $this->getShortcode($form_id);

        $post_id = wp_insert_post([
            'post_type' => $post_type,
            'post_status' => $post_status,
            'post_title' => $title,
            'post_content' => $post_content,
        ], true);

        if (is_wp_error($post_id)) {
            return $post_id;
        }

        $action_key = $post_type === 'post' ? 'create_and_add_to_new_post' : 'create_and_add_to_new_page';
        return $this->buildPlacementResult((int) $post_id, $form_id, $action_key);
    }

    public function contentHasShortcodeForForm(string $content, int $form_id): bool
    {
        $form_id = absint($form_id);
        return $form_id > 0 && in_array($form_id, $this->extractShortcodeFormIdsFromContent($content), true);
    }

    public function contentHasNativeBlockForForm(string $content, int $form_id): bool
    {
        $form_id = absint($form_id);
        return $form_id > 0 && in_array($form_id, $this->extractNativeBlockFormIdsFromContent($content), true);
    }

    public function extractPlacedFormIdsFromContent(string $content): array
    {
        if ($content === '') {
            return [];
        }

        $form_ids = array_merge(
            $this->extractShortcodeFormIdsFromContent($content),
            $this->extractNativeBlockFormIdsFromContent($content)
        );

        if (empty($form_ids)) {
            return [];
        }

        return array_values(array_unique(array_filter(array_map('absint', $form_ids))));
    }

    protected function extractShortcodeFormIdsFromContent(string $content): array
    {
        if ($content === '') {
            return [];
        }

        $pattern = get_shortcode_regex(['griffinforms_form']);
        if (!preg_match_all('/' . $pattern . '/s', $content, $matches, PREG_SET_ORDER)) {
            return [];
        }

        $form_ids = [];
        foreach ($matches as $match) {
            if (($match[2] ?? '') !== 'griffinforms_form') {
                continue;
            }

            $attrs = shortcode_parse_atts($match[3] ?? '');
            $matched_form_id = isset($attrs['id']) ? absint($attrs['id']) : 0;
            if ($matched_form_id > 0) {
                $form_ids[] = $matched_form_id;
            }
        }

        return array_values(array_unique($form_ids));
    }

    protected function extractNativeBlockFormIdsFromContent(string $content): array
    {
        if ($content === '' || !function_exists('parse_blocks')) {
            return [];
        }

        return $this->extractNativeBlockFormIds(parse_blocks($content));
    }

    protected function extractNativeBlockFormIds(array $blocks): array
    {
        $form_ids = [];

        foreach ($blocks as $block) {
            if (!is_array($block)) {
                continue;
            }

            if (($block['blockName'] ?? '') === 'griffinforms/form') {
                $attrs = is_array($block['attrs'] ?? null) ? $block['attrs'] : [];
                $form_id = absint($attrs['formId'] ?? 0);
                if ($form_id > 0) {
                    $form_ids[] = $form_id;
                }
            }

            if (!empty($block['innerBlocks'])) {
                $form_ids = array_merge($form_ids, $this->extractNativeBlockFormIds((array) $block['innerBlocks']));
            }
        }

        return array_values(array_unique($form_ids));
    }

    protected function containsNativeBlockForForm(array $blocks, int $form_id): bool
    {
        foreach ($blocks as $block) {
            if (!is_array($block)) {
                continue;
            }

            if (($block['blockName'] ?? '') === 'griffinforms/form') {
                $attrs = is_array($block['attrs'] ?? null) ? $block['attrs'] : [];
                if (absint($attrs['formId'] ?? 0) === $form_id) {
                    return true;
                }
            }

            if (!empty($block['innerBlocks']) && $this->containsNativeBlockForForm((array) $block['innerBlocks'], $form_id)) {
                return true;
            }
        }

        return false;
    }

    public function contentHasAnyPlacement(string $content): bool
    {
        return $this->contentHasAnyShortcodePlacement($content) || $this->contentHasAnyNativeBlockPlacement($content);
    }

    protected function contentHasAnyShortcodePlacement(string $content): bool
    {
        return !empty($this->extractShortcodeFormIdsFromContent($content));
    }

    protected function contentHasAnyNativeBlockPlacement(string $content): bool
    {
        return !empty($this->extractNativeBlockFormIdsFromContent($content));
    }

    protected function containsAnyNativeBlock(array $blocks): bool
    {
        foreach ($blocks as $block) {
            if (!is_array($block)) {
                continue;
            }

            if (($block['blockName'] ?? '') === 'griffinforms/form') {
                return true;
            }

            if (!empty($block['innerBlocks']) && $this->containsAnyNativeBlock((array) $block['innerBlocks'])) {
                return true;
            }
        }

        return false;
    }

    public function appendShortcodeToContent(string $content, string $shortcode): string
    {
        $content = trim($content);
        $shortcode = trim($shortcode);

        if ($content === '') {
            return $shortcode;
        }

        return $content . "\n\n" . $shortcode;
    }

    public function appendNativeBlockToContent(string $content, string $block_markup): string
    {
        $content = trim($content);
        $block_markup = trim($block_markup);

        if ($content === '') {
            return $block_markup;
        }

        return $content . "\n\n" . $block_markup;
    }

    protected function supportsNativeBlockPlacement(string $post_type): bool
    {
        $post_type = sanitize_key($post_type);

        if (!in_array($post_type, ['page', 'post'], true)) {
            return false;
        }

        if (!function_exists('use_block_editor_for_post_type')) {
            return false;
        }

        return use_block_editor_for_post_type($post_type);
    }

    public function getContentOptions(string $post_type): array
    {
        $post_type = sanitize_key($post_type);
        if (!in_array($post_type, ['page', 'post'], true)) {
            return [];
        }

        $post_type_object = get_post_type_object($post_type);
        if (!$post_type_object) {
            return [];
        }

        $edit_cap = (string) ($post_type_object->cap->edit_posts ?? '');
        if ($edit_cap === '' || !current_user_can($edit_cap)) {
            return [];
        }

        $items = get_posts([
            'post_type' => $post_type,
            'post_status' => ['publish', 'draft', 'pending', 'future', 'private'],
            'orderby' => 'title',
            'order' => 'ASC',
            'posts_per_page' => 100,
        ]);

        $options = [];
        foreach ((array) $items as $item) {
            if (!$item instanceof \WP_Post) {
                continue;
            }

            if (!current_user_can('edit_post', $item->ID)) {
                continue;
            }

            $options[] = [
                'id' => (int) $item->ID,
                'title' => $item->post_title !== '' ? $item->post_title : __('(no title)', 'griffinforms-form-builder'),
                'status' => (string) $item->post_status,
                'type' => (string) $item->post_type,
            ];
        }

        return $options;
    }

    public function classifyExistingContent($post): array
    {
        return $this->content_classifier->classifyPost($post);
    }

    protected function isValidForm(int $form_id): bool
    {
        return $form_id > 0 && $this->admin_sql->itemExists('form', $form_id);
    }

    protected function buildPlacementResult(int $post_id, int $form_id, string $action_key): array
    {
        $post = get_post($post_id);
        $view_url = '';
        if ($post instanceof \WP_Post && in_array($post->post_status, ['publish', 'future', 'private'], true)) {
            $view_url = (string) get_permalink($post_id);
            if ($view_url !== '' && isset($this->frontend_url_logger) && $this->frontend_url_logger instanceof \GriffinForms\Includes\Publication\FrontendUrlLogger) {
                $this->frontend_url_logger->append($form_id, $view_url);
            }
        }

        return [
            'action' => sanitize_key($action_key),
            'form_id' => absint($form_id),
            'post_id' => absint($post_id),
            'post_type' => $post instanceof \WP_Post ? (string) $post->post_type : '',
            'post_status' => $post instanceof \WP_Post ? (string) $post->post_status : '',
            'title' => $post instanceof \WP_Post ? (string) $post->post_title : '',
            'shortcode' => $this->getShortcode($form_id),
            'edit_url' => (string) get_edit_post_link($post_id, 'raw'),
            'view_url' => $view_url,
        ];
    }

    protected function buildStandalonePlacementResult(int $form_id): array
    {
        return [
            'action' => 'create_form_only',
            'form_id' => absint($form_id),
            'post_id' => 0,
            'post_type' => '',
            'post_status' => '',
            'title' => '',
            'shortcode' => $this->getShortcode($form_id),
            'edit_url' => '',
            'view_url' => '',
        ];
    }

    protected function getManualFallbackMessageForClassification(array $classification): string
    {
        $builder_key = sanitize_key((string) ($classification['builder_key'] ?? 'unknown_builder'));

        if ($builder_key === 'elementor') {
            return __('This page uses an Elementor-managed layout, so GriffinForms did not insert the form automatically. Your form is ready. Use the shortcode below to place it safely.', 'griffinforms-form-builder');
        }

        return __('This content uses a builder-managed layout, so GriffinForms did not insert the form automatically. Your form is ready. Use the shortcode below to place it safely.', 'griffinforms-form-builder');
    }
}
