<?php

namespace GriffinForms\Includes\Ai\PromptRegistry;

use GriffinForms\Admin\Sql\App;
use GriffinForms\Admin\Sql\Format;

class ShadowDraftStructureApplier
{
    protected App $app_sql;
    protected Format $format_sql;
    protected \GriffinForms\Config $config;
    protected \GriffinForms\Settings $settings;
    protected \GriffinForms\Log $log;
    protected StructureSnapshotBuilder $snapshot_builder;
    protected SchemaDiffAnalyzer $diff_analyzer;
    protected ShadowUidRegistry $shadow_uid_registry;
    protected ?bool $status_column_exists = null;

    public function __construct()
    {
        $this->app_sql = App::getInstance();
        $this->format_sql = Format::getInstance();
        $this->config = \GriffinForms\Config::getInstance();
        $this->settings = \GriffinForms\Settings::getInstance();
        $this->log = \GriffinForms\Log::getInstance();
        $this->snapshot_builder = new StructureSnapshotBuilder();
        $this->diff_analyzer = new SchemaDiffAnalyzer();
        $this->shadow_uid_registry = new ShadowUidRegistry();
    }

    public function apply(int $form_id, array $schema, string $source_prompt = ''): array
    {
        global $wpdb;

        $form_id = absint($form_id);
        if ($form_id <= 0 || !$this->format_sql->itemExists('form', $form_id)) {
            return [
                'success' => false,
                'message' => __('The target draft form could not be found.', 'griffinforms-form-builder'),
            ];
        }

        try {
            $integration_settings = $this->getFormIntegrationSettings($form_id);
            $stored_shadow_registry = (array) (($integration_settings['ai_draft']['shadow_registry'] ?? []));
            $before_snapshot = $this->snapshot_builder->buildFromForm($form_id);
            $target_snapshot = $this->snapshot_builder->buildFromSchema($schema);
            [$before_snapshot, $target_snapshot] = $this->shadow_uid_registry->hydrateSnapshots(
                $before_snapshot,
                $target_snapshot,
                $stored_shadow_registry
            );
            $lineage_report = $this->diff_analyzer->analyze($before_snapshot, $target_snapshot);
            $shadow_registry = $this->shadow_uid_registry->buildRegistryFromSnapshot($target_snapshot);

            $wpdb->query('START TRANSACTION');

            $this->wipeExistingStructure($form_id);

            $mutated_ids = [];
            $page_ids = [];

            foreach ((array) ($schema['pages'] ?? []) as $page_schema) {
                $page_id = $this->app_sql->createFormPage($form_id);
                if (!is_int($page_id) || $page_id <= 0) {
                    throw new \RuntimeException('Failed to create form page.');
                }

                $page_ids[] = $page_id;
                $mutated_ids[] = $page_id;
                $this->updatePageRecord($page_id, $page_schema);

                $row_ids = [];
            foreach ((array) ($page_schema['rows'] ?? []) as $row_schema) {
                    $row_id = $this->app_sql->createPageRow($form_id);
                    if (!is_int($row_id) || $row_id <= 0) {
                        throw new \RuntimeException('Failed to create page row.');
                    }

                    $row_ids[] = $row_id;
                    $mutated_ids[] = $row_id;
                    $this->updateRowRecord($row_id, $row_schema);
                    $this->removeAutoAttachedColumns($row_id);

                    $column_ids = [];
                    foreach ((array) ($row_schema['columns'] ?? []) as $column_schema) {
                        $column_id = $this->app_sql->createRowColumn(absint($column_schema['width'] ?? 12), $form_id);
                        if (!is_int($column_id) || $column_id <= 0) {
                            throw new \RuntimeException('Failed to create row column.');
                        }

                        $column_ids[] = $column_id;
                        $mutated_ids[] = $column_id;
                        $this->updateColumnRecord($column_id, $column_schema);

                        $field_ids = [];
                        foreach ((array) ($column_schema['fields'] ?? []) as $field_schema) {
                            if (!$this->validateLikertFieldSchema((array) $field_schema, $form_id)) {
                                throw new \RuntimeException('Likert field schema is missing explicit rows or columns.');
                            }
                            $field_id = $this->app_sql->createField($this->mapFieldData($field_schema));
                            if (!is_int($field_id) || $field_id <= 0) {
                                throw new \RuntimeException('Failed to create field.');
                            }

                            $field_ids[] = $field_id;
                            $mutated_ids[] = $field_id;
                        }

                        if ($this->app_sql->updateRowColumnFields($column_id, $field_ids) === false) {
                            throw new \RuntimeException('Failed to attach fields to column.');
                        }
                    }

                    if ($this->app_sql->updatePageRowColumns($row_id, $column_ids) === false) {
                        throw new \RuntimeException('Failed to attach columns to row.');
                    }
                }

                if ($this->app_sql->updateFormPageRows($page_id, $row_ids) === false) {
                    throw new \RuntimeException('Failed to attach rows to page.');
                }
            }

            if ($this->app_sql->updateFormFormPages($form_id, $page_ids) === false) {
                throw new \RuntimeException('Failed to attach pages to form.');
            }

            $this->updateFormRecord($form_id, $schema, $source_prompt, $lineage_report, $shadow_registry, $integration_settings);

            $wpdb->query('COMMIT');

            $this->logLineageReport($form_id, $lineage_report);

            return [
                'success' => true,
                'mutated_ids' => array_values(array_unique(array_map('absint', $mutated_ids))),
                'lineage' => $lineage_report,
            ];
        } catch (\Throwable $throwable) {
            $wpdb->query('ROLLBACK');
            $this->log->add(
                'error',
                'ai_draft',
                $form_id,
                (int) get_current_user_id(),
                'Shadow draft apply failed: ' . sanitize_text_field($throwable->getMessage()),
                'ai_apply'
            );

            return [
                'success' => false,
                'message' => __('Griffin Assist could not safely apply the generated structure. The draft was left unchanged.', 'griffinforms-form-builder'),
            ];
        }
    }

    protected function wipeExistingStructure(int $form_id): void
    {
        $page_ids = $this->decodeValue($this->format_sql->getItemValue($form_id, 'formpages', 'form'));
        if (!is_array($page_ids)) {
            return;
        }

        foreach ($page_ids as $page_id) {
            $page_id = absint($page_id);
            if ($page_id > 0) {
                $this->format_sql->deleteFormPage($page_id, $form_id);
            }
        }
    }

    protected function updateFormRecord(
        int $form_id,
        array $schema,
        string $source_prompt,
        array $lineage_report = [],
        array $shadow_registry = [],
        ?array $integration_settings = null
    ): void
    {
        global $wpdb;

        $table = $this->config->getTable('form');
        $form = (array) ($schema['form'] ?? []);
        $name = trim((string) ($form['name'] ?? ''));
        $heading = trim((string) ($form['heading'] ?? ''));
        $description = trim((string) ($form['description'] ?? ''));

        if (!is_array($integration_settings)) {
            $integration_settings = $this->getFormIntegrationSettings($form_id);
        }

        $integration_settings['ai_draft'] = array_merge(
            (array) ($integration_settings['ai_draft'] ?? []),
            [
                'last_prompt' => sanitize_textarea_field($source_prompt),
                'last_applied_at' => current_time('mysql', true),
                'auto_start_pending' => 0,
                'last_lineage_report' => $lineage_report,
                'shadow_registry' => $shadow_registry,
            ]
        );

        $update_data = [
            'name' => $name !== '' ? sanitize_text_field($name) : __('AI Draft Form', 'griffinforms-form-builder'),
            'heading' => $heading !== '' ? sanitize_text_field($heading) : ($name !== '' ? sanitize_text_field($name) : __('AI Draft Form', 'griffinforms-form-builder')),
            'description' => sanitize_textarea_field($description),
            'submit_label' => sanitize_text_field((string) ($form['submit_label'] ?? '')),
            'integration_settings' => maybe_serialize($integration_settings),
            'origin' => 'ai',
            'last_editor' => get_current_user_id(),
            'last_edited' => current_time('mysql', true),
        ];
        if ($this->formTableHasStatusColumn()) {
            $update_data['status'] = 'draft';
        }

        $wpdb->update(
            $table,
            $update_data,
            ['id' => $form_id],
            array_fill(0, count($update_data), '%s'),
            ['%d']
        );
    }

    protected function updatePageRecord(int $page_id, array $page_schema): void
    {
        global $wpdb;

        $table = $this->config->getTable('formpage');
        $wpdb->update(
            $table,
            [
                'name' => sanitize_text_field((string) ($page_schema['name'] ?? __('Page', 'griffinforms-form-builder'))),
                'heading' => sanitize_text_field((string) ($page_schema['heading'] ?? '')),
                'description' => sanitize_textarea_field((string) ($page_schema['description'] ?? '')),
                'previous_label' => sanitize_text_field((string) ($page_schema['previous_label'] ?? '')),
                'next_label' => sanitize_text_field((string) ($page_schema['next_label'] ?? '')),
                'last_editor' => get_current_user_id(),
                'last_edited' => current_time('mysql', true),
            ],
            ['id' => $page_id],
            ['%s', '%s', '%s', '%s', '%s', '%d', '%s'],
            ['%d']
        );
    }

    protected function updateRowRecord(int $row_id, array $row_schema): void
    {
        global $wpdb;

        $table = $this->config->getTable('pagerow');
        $wpdb->update(
            $table,
            [
                'name' => sanitize_text_field((string) ($row_schema['name'] ?? __('Row', 'griffinforms-form-builder'))),
                'last_editor' => get_current_user_id(),
                'last_edited' => current_time('mysql', true),
            ],
            ['id' => $row_id],
            ['%s', '%d', '%s'],
            ['%d']
        );
    }

    protected function updateColumnRecord(int $column_id, array $column_schema): void
    {
        global $wpdb;

        $table = $this->config->getTable('rowcolumn');
        $wpdb->update(
            $table,
            [
                'name' => sanitize_text_field((string) ($column_schema['name'] ?? __('Column', 'griffinforms-form-builder'))),
                'last_editor' => get_current_user_id(),
                'last_edited' => current_time('mysql', true),
            ],
            ['id' => $column_id],
            ['%s', '%d', '%s'],
            ['%d']
        );
    }

    protected function mapFieldData(array $field_schema): array
    {
        $field_type = sanitize_key((string) ($field_schema['field_type'] ?? 'inputtext'));
        $label = sanitize_text_field((string) ($field_schema['label'] ?? __('Field', 'griffinforms-form-builder')));
        $name = sanitize_text_field((string) ($field_schema['name'] ?? $label));
        $options = $this->mapOptions($field_type, (array) ($field_schema['options'] ?? []));
        $field_settings = $this->mapFieldSettings($field_type, $field_schema);

        return [
            'field_type' => $field_type,
            'name' => $name !== '' ? $name : $label,
            'heading' => $label,
            'description' => sanitize_textarea_field((string) ($field_schema['description'] ?? '')),
            'is_required' => !empty($field_schema['required']) ? 1 : 0,
            'placeholder' => sanitize_text_field((string) ($field_schema['placeholder'] ?? '')),
            'field_options' => !empty($options) ? maybe_serialize($options) : maybe_serialize([]),
            'field_settings' => !empty($field_settings) ? maybe_serialize($field_settings) : maybe_serialize([]),
            'is_reusable' => 0,
        ];
    }

    protected function mapFieldSettings(string $field_type, array $field_schema): array
    {
        if ($field_type !== 'inputlikert') {
            return [];
        }

        $rows = [];
        foreach ((array) ($field_schema['likert_rows'] ?? []) as $row) {
            if (!is_array($row)) {
                continue;
            }

            $rows[] = [
                'id' => sanitize_key((string) ($row['id'] ?? '')),
                'label' => sanitize_text_field((string) ($row['label'] ?? '')),
            ];
        }

        $columns = [];
        foreach ((array) ($field_schema['likert_columns'] ?? []) as $column) {
            if (!is_array($column)) {
                continue;
            }

            $columns[] = [
                'id' => sanitize_key((string) ($column['id'] ?? '')),
                'label' => sanitize_text_field((string) ($column['label'] ?? '')),
            ];
        }

        return [
            'rows' => array_slice($rows, 0, 20),
            'columns' => array_slice($columns, 0, 10),
            'allow_na' => !empty($field_schema['likert_allow_na']),
            'required_mode' => in_array((string) ($field_schema['required_mode'] ?? 'all'), ['all', 'none'], true)
                ? (string) ($field_schema['required_mode'] ?? 'all')
                : 'all',
            'column_width' => in_array((string) ($field_schema['likert_column_width'] ?? 'auto'), ['auto', 'equal'], true)
                ? (string) ($field_schema['likert_column_width'] ?? 'auto')
                : 'auto',
        ];
    }

    protected function validateLikertFieldSchema(array $field_schema, int $form_id): bool
    {
        if (sanitize_key((string) ($field_schema['field_type'] ?? '')) !== 'inputlikert') {
            return true;
        }

        $rows = array_values((array) ($field_schema['likert_rows'] ?? []));
        $columns = array_values((array) ($field_schema['likert_columns'] ?? []));

        $has_rows = false;
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $label = sanitize_text_field((string) ($row['label'] ?? ''));
            if ($label !== '' && !preg_match('/^row\s*\d+$/i', $label)) {
                $has_rows = true;
                break;
            }
        }

        $has_columns = count($columns) >= 2;

        if (!$has_rows || !$has_columns) {
            $this->log->add(
                'warning',
                'ai_draft',
                $form_id,
                (int) get_current_user_id(),
                'Blocked empty Likert field schema before apply.',
                'ai_apply'
            );
            return false;
        }

        return true;
    }

    protected function mapOptions(string $field_type, array $options): array
    {
        if (!in_array($field_type, ['selectfield', 'inputcheckbox', 'inputradio'], true)) {
            return [];
        }

        if (in_array($field_type, ['inputcheckbox', 'inputradio'], true)) {
            $mapped_groups = [];

            foreach ($options as $group) {
                $group_map = [];

                if (is_array($group) && isset($group['options']) && is_array($group['options'])) {
                    foreach ($group['options'] as $option) {
                        $label = sanitize_textarea_field((string) ($option['label'] ?? ''));
                        $value = sanitize_key((string) ($option['value'] ?? ''));

                        if ($label === '' || $value === '') {
                            continue;
                        }

                        $group_map[$label] = $value;
                    }
                } elseif (is_array($group)) {
                    foreach ($group as $option) {
                        if (!is_array($option)) {
                            continue;
                        }

                        $label = sanitize_textarea_field((string) ($option['label'] ?? ''));
                        $value = sanitize_key((string) ($option['value'] ?? ''));

                        if ($label === '' || $value === '') {
                            continue;
                        }

                        $group_map[$label] = $value;
                    }
                }

                if (!empty($group_map)) {
                    $mapped_groups[] = $group_map;
                }
            }

            if (!empty($mapped_groups)) {
                return $mapped_groups;
            }
        }

        $mapped = [];
        foreach ($options as $option) {
            $label = sanitize_textarea_field((string) ($option['label'] ?? ''));
            $value = sanitize_key((string) ($option['value'] ?? ''));

            if ($label === '' || $value === '') {
                continue;
            }

            $mapped[$label] = $value;
        }

        if ($field_type === 'selectfield') {
            return ['' => $mapped];
        }

        return [$mapped];
    }

    protected function removeAutoAttachedColumns(int $row_id): void
    {
        $row = $this->format_sql->itemValues($row_id, 'pagerow', ['rowcolumns']);
        if (!is_object($row) || empty($row->rowcolumns)) {
            return;
        }

        $column_ids = $this->decodeValue($row->rowcolumns);
        if (!is_array($column_ids)) {
            return;
        }

        foreach ($column_ids as $column_id) {
            $column_id = absint($column_id);
            if ($column_id > 0) {
                $this->format_sql->deleteRowColumn($column_id, $row_id);
            }
        }
    }

    protected function decodeValue($value)
    {
        if (is_array($value)) {
            return $value;
        }

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

        $maybe_unserialized = maybe_unserialize($value);
        if ($maybe_unserialized !== $value || $value === 'b:0;') {
            return $maybe_unserialized;
        }

        $decoded_json = json_decode($value, true);
        if (json_last_error() === JSON_ERROR_NONE) {
            return $decoded_json;
        }

        return $value;
    }

    protected function formTableHasStatusColumn(): bool
    {
        if ($this->status_column_exists !== null) {
            return $this->status_column_exists;
        }

        global $wpdb;
        $table = $this->config->getTable('form');
        $exists = $wpdb->get_var($wpdb->prepare(
            "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = %s AND COLUMN_NAME = %s",
            $table,
            'status'
        ));

        $this->status_column_exists = !empty($exists);
        return $this->status_column_exists;
    }

    protected function logLineageReport(int $form_id, array $lineage_report): void
    {
        $summary = (array) ($lineage_report['summary'] ?? []);
        $parts = [];
        foreach ($summary as $key => $value) {
            $parts[] = sanitize_key((string) $key) . '=' . absint($value);
        }

        $this->log->add(
            'info',
            'ai_draft',
            $form_id,
            (int) get_current_user_id(),
            'Shadow draft lineage summary: ' . implode(' ', $parts),
            'ai_lineage'
        );
    }

    protected function getFormIntegrationSettings(int $form_id): array
    {
        $integration_settings = $this->decodeValue($this->format_sql->getItemValue($form_id, 'integration_settings', 'form'));
        return is_array($integration_settings) ? $integration_settings : [];
    }
}
