<?php

if (!defined('ABSPATH')) {
    exit;
}

final class BBC_Placements
{
    public static function all()
    {
        $placements = get_option(BBC_OPTION_PLACEMENTS, array());
        return is_array($placements) ? $placements : array();
    }

    public static function upsert($payload)
    {
        $id = isset($payload['placement_id']) ? strtolower((string) $payload['placement_id']) : '';
        $html = isset($payload['html']) ? (string) $payload['html'] : '';
        $placements = self::all();
        $existing = isset($placements[$id]) && is_array($placements[$id]) ? $placements[$id] : null;
        if ($html === '' && $existing && isset($existing['html'])) $html = (string) $existing['html'];
        if (!BBC_Auth::is_uuid($id) || $html === '') {
            return new WP_Error('bbc_invalid_placement', 'BBC placement is invalid.', array('status' => 400));
        }
        $sanitized = self::sanitize_html($html);
        if (is_wp_error($sanitized)) {
            return $sanitized;
        }
        $skip = isset($payload['skip_strategies']) && is_array($payload['skip_strategies']) ? $payload['skip_strategies'] : array();
        $requested = isset($payload['strategy']) ? sanitize_key($payload['strategy']) : 'auto';
        $strategies = $requested === 'auto' ? self::eligible_strategies($skip) : array($requested);
        $last_error = '';
        $attempted = $existing && isset($existing['attempted_strategies']) && is_array($existing['attempted_strategies'])
            ? $existing['attempted_strategies'] : array();
        if ($existing) {
            $removed = self::remove_from_source($existing);
            if (is_wp_error($removed)) return $removed;
        }
        foreach ($strategies as $strategy) {
            $attempted[] = $strategy;
            $result = self::apply($strategy, $id, $sanitized);
            if (is_wp_error($result)) {
                $last_error = $result->get_error_message();
                continue;
            }
            $placements = self::all();
            $placements[$id] = array(
                'placement_id' => $id,
                'html' => $sanitized,
                'html_hash' => hash('sha256', $sanitized),
                'strategy' => $strategy,
                'target_id' => isset($result['target_id']) ? (string) $result['target_id'] : '',
                'node_id' => isset($result['node_id']) ? (string) $result['node_id'] : '',
                'source_hash_before' => isset($result['source_hash_before']) ? (string) $result['source_hash_before'] : '',
                'backup_key' => isset($result['backup_key']) ? (string) $result['backup_key'] : '',
                'attempted_strategies' => $attempted,
                'updated_at' => time(),
            );
            update_option(BBC_OPTION_PLACEMENTS, $placements, false);
            return array('placement_id' => $id, 'strategy' => $strategy, 'target_id' => $placements[$id]['target_id']);
        }
        return new WP_Error('bbc_no_strategy', $last_error ?: 'No BBC placement strategy was available.', array('status' => 409));
    }

    public static function inspect($placement_id)
    {
        $placements = self::all();
        if (!isset($placements[$placement_id])) {
            return new WP_Error('bbc_not_found', 'BBC placement was not found.', array('status' => 404));
        }
        $entry = $placements[$placement_id];
        return array(
            'placement_id' => $placement_id,
            'strategy' => $entry['strategy'],
            'target_id' => $entry['target_id'],
            'durable_present' => self::source_has_marker($entry),
            'runtime_available' => true,
            'html_hash' => $entry['html_hash'],
        );
    }

    public static function summaries()
    {
        $summaries = array();
        foreach (self::all() as $id => $entry) {
            $summaries[] = array(
                'placement_id' => $id,
                'strategy' => isset($entry['strategy']) ? $entry['strategy'] : '',
                'html_hash' => isset($entry['html_hash']) ? $entry['html_hash'] : '',
            );
        }
        return $summaries;
    }

    public static function remove($placement_id)
    {
        $placements = self::all();
        if (!isset($placements[$placement_id])) {
            return array('placement_id' => $placement_id, 'removed' => true);
        }
        $entry = $placements[$placement_id];
        $result = self::remove_from_source($entry);
        if (is_wp_error($result)) {
            return $result;
        }
        unset($placements[$placement_id]);
        update_option(BBC_OPTION_PLACEMENTS, $placements, false);
        return array('placement_id' => $placement_id, 'removed' => true);
    }

    public static function marked_html($id, $html)
    {
        return '<!-- bbc:' . $id . ':start -->' . $html . '<!-- bbc:' . $id . ':end -->';
    }

    private static function sanitize_html($html)
    {
        if (preg_match('/<\?(?:php|=)|\bon[a-z]+\s*=|javascript\s*:/i', $html)) {
            return new WP_Error('bbc_html_rejected', 'BBC placement contains executable content.', array('status' => 400));
        }
        $allowed = array(
            'a' => array('href' => true, 'title' => true, 'rel' => true, 'target' => true, 'class' => true),
            'p' => array('class' => true), 'div' => array('class' => true), 'span' => array('class' => true),
            'h1' => array('class' => true), 'h2' => array('class' => true), 'h3' => array('class' => true),
            'h4' => array('class' => true), 'h5' => array('class' => true), 'h6' => array('class' => true),
            'strong' => array(), 'em' => array(), 'b' => array(), 'i' => array(), 'br' => array(),
            'ul' => array('class' => true), 'ol' => array('class' => true), 'li' => array('class' => true),
            'blockquote' => array('class' => true),
        );
        $sanitized = wp_kses($html, $allowed, array('http', 'https'));
        if (trim($sanitized) !== trim($html)) {
            return new WP_Error('bbc_html_rejected', 'BBC placement contains unsupported HTML.', array('status' => 400));
        }
        return $sanitized;
    }

    private static function eligible_strategies($skip)
    {
        $strategies = array();
        $page_id = intval(get_option('page_on_front'));
        if (get_option('show_on_front') === 'page' && $page_id > 0) {
            $is_elementor = get_post_meta($page_id, '_elementor_edit_mode', true) === 'builder' || get_post_meta($page_id, '_elementor_data', true);
            $is_opaque_builder = get_post_meta($page_id, '_et_pb_use_builder', true) ||
                get_post_meta($page_id, '_et_pb_old_content', true) ||
                get_post_meta($page_id, '_wpb_vc_js_status', true) ||
                get_post_meta($page_id, '_fl_builder_data', true) ||
                get_post_meta($page_id, '_bricks_page_content_2', true) ||
                get_post_meta($page_id, 'ct_builder_shortcodes', true);
            if ($is_opaque_builder) {
                $strategies[] = 'bbc_runtime';
            } else {
                $strategies[] = $is_elementor ? 'bbc_elementor' : 'bbc_native_page';
            }
        } else {
            $strategies[] = 'bbc_block_template';
        }
        if (!in_array('bbc_runtime', $strategies, true)) $strategies[] = 'bbc_runtime';
        return array_values(array_diff($strategies, array_map('sanitize_key', $skip)));
    }

    private static function apply($strategy, $id, $html)
    {
        if ($strategy === 'bbc_native_page') return self::apply_native($id, $html);
        if ($strategy === 'bbc_elementor') return self::apply_elementor($id, $html);
        if ($strategy === 'bbc_block_template') return self::apply_template($id, $html);
        if ($strategy === 'bbc_runtime') return array('target_id' => 'homepage');
        return new WP_Error('bbc_unknown_strategy', 'BBC strategy is not supported.', array('status' => 400));
    }

    private static function apply_native($id, $html)
    {
        $page_id = intval(get_option('page_on_front'));
        $post = get_post($page_id);
        if (!$post || $post->post_type !== 'page') return new WP_Error('bbc_no_page', 'Static homepage was not found.');
        $marked = self::marked_html($id, $html);
        $content = self::remove_marker((string) $post->post_content, $id) . "\n" . $marked;
        $updated = BBC_Db::update_post_content($page_id, $content);
        return is_wp_error($updated) ? $updated : array('target_id' => $page_id, 'source_hash_before' => hash('sha256', (string) $post->post_content));
    }

    private static function apply_template($id, $html)
    {
        $posts = get_posts(array('post_type' => 'wp_template', 'post_status' => array('publish', 'auto-draft'), 'numberposts' => -1));
        foreach (array('front-page', 'home', 'index') as $slug) {
            foreach ($posts as $post) {
                if ($post->post_name !== $slug && substr($post->post_name, -strlen('//' . $slug)) !== '//' . $slug) continue;
                $marked = "<!-- wp:html -->\n" . self::marked_html($id, $html) . "\n<!-- /wp:html -->";
                $content = self::remove_template_marker((string) $post->post_content, $id) . "\n" . $marked;
                $updated = BBC_Db::update_post_content($post->ID, $content);
                return is_wp_error($updated) ? $updated : array('target_id' => $post->ID, 'source_hash_before' => hash('sha256', (string) $post->post_content));
            }
        }
        return new WP_Error('bbc_no_template', 'Editable homepage template was not found.');
    }

    private static function apply_elementor($id, $html)
    {
        $page_id = intval(get_option('page_on_front'));
        $raw = get_post_meta($page_id, '_elementor_data', true);
        if (strlen((string) $raw) > 2097152) {
            return new WP_Error('bbc_elementor_oversized', 'Elementor data is too large for a bounded backup.');
        }
        $version = (string) get_post_meta($page_id, '_elementor_version', true);
        if ($version === '' || version_compare($version, '2.0.0', '<') || version_compare($version, '4.0.0', '>=')) {
            return new WP_Error('bbc_elementor_schema', 'Elementor schema version is not recognized.');
        }
        $elements = json_decode((string) $raw, true);
        if (!is_array($elements)) return new WP_Error('bbc_elementor_schema', 'Elementor data is not readable.');
        $widget_id = substr(hash('sha256', $id), 0, 7);
        $root_id = substr(hash('sha256', $id . 'r'), 0, 7);
        $elements = self::remove_elementor_node($elements, $root_id);
        $widget = array('id' => $widget_id, 'elType' => 'widget', 'widgetType' => 'html', 'settings' => array('html' => self::marked_html($id, $html)), 'elements' => array());
        if (isset($elements[0]['elType']) && $elements[0]['elType'] === 'container') {
            $elements[] = array('id' => $root_id, 'elType' => 'container', 'isInner' => false, 'settings' => array(), 'elements' => array($widget));
        } else {
            $column = array('id' => substr(hash('sha256', $id . 'o'), 0, 7), 'elType' => 'column', 'isInner' => false, 'settings' => array('_column_size' => 100), 'elements' => array($widget));
            $elements[] = array('id' => $root_id, 'elType' => 'section', 'isInner' => false, 'settings' => array(), 'elements' => array($column));
        }
        $encoded = wp_json_encode($elements);
        $backup_key = 'bbc_elementor_backup_' . str_replace('-', '', $id);
        update_option($backup_key, array(
            'target_id' => $page_id,
            'data' => (string) $raw,
            'hash' => hash('sha256', (string) $raw),
            'created_at' => time(),
        ), false);
        update_post_meta($page_id, '_elementor_data', wp_slash($encoded));
        if ((string) get_post_meta($page_id, '_elementor_data', true) !== $encoded) {
            update_post_meta($page_id, '_elementor_data', wp_slash((string) $raw));
            delete_option($backup_key);
            return new WP_Error('bbc_elementor_save', 'Elementor data could not be updated.');
        }
        clean_post_cache($page_id);
        return array('target_id' => $page_id, 'node_id' => $root_id, 'backup_key' => $backup_key, 'source_hash_before' => hash('sha256', (string) $raw));
    }

    private static function remove_elementor_node($elements, $node_id)
    {
        $result = array();
        foreach ($elements as $element) {
            if (!is_array($element) || (isset($element['id']) && $element['id'] === $node_id)) continue;
            if (isset($element['elements']) && is_array($element['elements'])) {
                $element['elements'] = self::remove_elementor_node($element['elements'], $node_id);
            }
            $result[] = $element;
        }
        return $result;
    }

    private static function source_has_marker($entry)
    {
        if ($entry['strategy'] === 'bbc_runtime') return false;
        if ($entry['strategy'] === 'bbc_elementor') {
            return strpos((string) get_post_meta(intval($entry['target_id']), '_elementor_data', true), 'bbc:' . $entry['placement_id'] . ':start') !== false;
        }
        $post = get_post(intval($entry['target_id']));
        return $post && strpos((string) $post->post_content, 'bbc:' . $entry['placement_id'] . ':start') !== false;
    }

    private static function remove_from_source($entry)
    {
        if ($entry['strategy'] === 'bbc_runtime') return true;
        if ($entry['strategy'] === 'bbc_elementor') {
            $raw = get_post_meta(intval($entry['target_id']), '_elementor_data', true);
            $elements = json_decode((string) $raw, true);
            if (!is_array($elements)) return new WP_Error('bbc_elementor_schema', 'Elementor cleanup data is not readable.');
            update_post_meta(intval($entry['target_id']), '_elementor_data', wp_slash(wp_json_encode(self::remove_elementor_node($elements, $entry['node_id']))));
            clean_post_cache(intval($entry['target_id']));
            if (!empty($entry['backup_key'])) delete_option($entry['backup_key']);
            return true;
        }
        $post = get_post(intval($entry['target_id']));
        if (!$post) return new WP_Error('bbc_target_missing', 'BBC cleanup target was not found.');
        $content = $entry['strategy'] === 'bbc_block_template'
            ? self::remove_template_marker((string) $post->post_content, $entry['placement_id'])
            : self::remove_marker((string) $post->post_content, $entry['placement_id']);
        $updated = BBC_Db::update_post_content($post->ID, $content);
        return is_wp_error($updated) ? $updated : true;
    }

    private static function remove_marker($content, $id)
    {
        $start = '<!-- bbc:' . $id . ':start -->';
        $end = '<!-- bbc:' . $id . ':end -->';
        $start_at = strpos($content, $start);
        if ($start_at === false) return $content;
        $end_at = strpos($content, $end, $start_at);
        if ($end_at === false) return $content;
        return substr($content, 0, $start_at) . substr($content, $end_at + strlen($end));
    }

    private static function remove_template_marker($content, $id)
    {
        $start = '<!-- wp:html -->' . "\n" . '<!-- bbc:' . $id . ':start -->';
        $end = '<!-- bbc:' . $id . ':end -->' . "\n" . '<!-- /wp:html -->';
        $start_at = strpos($content, $start);
        if ($start_at === false) return self::remove_marker($content, $id);
        $end_at = strpos($content, $end, $start_at);
        if ($end_at === false) return self::remove_marker($content, $id);
        return substr($content, 0, $start_at) . substr($content, $end_at + strlen($end));
    }
}
