<?php

namespace GriffinForms\Includes\Ai;

use GriffinForms\Admin\Sql\App;
use GriffinForms\Admin\Sql\Format;
use GriffinForms\Includes\ImportExport\FormExport;

class Aidraftsnapshotmanager
{
    protected App $app_sql;
    protected Format $format_sql;
    protected \GriffinForms\Config $config;
    protected \GriffinForms\Log $log;
    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->log = \GriffinForms\Log::getInstance();
    }

    public function ensureBaselineSnapshot(int $form_id): bool
    {
        $settings = $this->getFormIntegrationSettings($form_id);
        $ai_draft = (array) ($settings['ai_draft'] ?? []);

        if (!empty($ai_draft['baseline_snapshot'])) {
            return true;
        }

        $snapshot = $this->exportSnapshot($form_id);
        if (!is_array($snapshot)) {
            return false;
        }

        $ai_draft['baseline_snapshot'] = wp_json_encode($snapshot);
        $ai_draft['baseline_snapshot_captured_at'] = current_time('mysql', true);
        $settings['ai_draft'] = $ai_draft;

        return $this->persistIntegrationSettings($form_id, $settings);
    }

    public function ensureBaselineSnapshotForCurrentState(int $form_id): bool
    {
        $settings = $this->getFormIntegrationSettings($form_id);
        $ai_draft = (array) ($settings['ai_draft'] ?? []);

        if (empty($ai_draft['baseline_snapshot'])) {
            return $this->ensureBaselineSnapshot($form_id);
        }

        if (!empty($ai_draft['last_applied_at'])) {
            return true;
        }

        $snapshot = $this->exportSnapshot($form_id);
        if (!is_array($snapshot)) {
            return false;
        }

        $ai_draft['baseline_snapshot'] = wp_json_encode($snapshot);
        $ai_draft['baseline_snapshot_captured_at'] = current_time('mysql', true);
        $settings['ai_draft'] = $ai_draft;

        return $this->persistIntegrationSettings($form_id, $settings);
    }

    public function capturePreActionSnapshot(int $form_id): ?array
    {
        return $this->exportSnapshot($form_id);
    }

    public function storePreActionSnapshot(int $form_id, array $snapshot): bool
    {
        $settings = $this->getFormIntegrationSettings($form_id);
        $ai_draft = (array) ($settings['ai_draft'] ?? []);
        $ai_draft['pre_action_snapshot'] = wp_json_encode($snapshot);
        $ai_draft['pre_action_snapshot_captured_at'] = current_time('mysql', true);
        $settings['ai_draft'] = $ai_draft;

        return $this->persistIntegrationSettings($form_id, $settings);
    }

    public function undoLastAction(int $form_id): array
    {
        $settings = $this->getFormIntegrationSettings($form_id);
        $snapshot = $this->decodeStoredSnapshot($settings['ai_draft']['pre_action_snapshot'] ?? '');

        if (!is_array($snapshot)) {
            return [
                'success' => false,
                'message' => __('There is no saved AI action to undo yet.', 'griffinforms-form-builder'),
            ];
        }

        $restored = $this->restoreSnapshot($form_id, $snapshot, 'undo');
        if (!$restored['success']) {
            return $restored;
        }

        $settings = $this->getFormIntegrationSettings($form_id);
        $ai_draft = (array) ($settings['ai_draft'] ?? []);
        unset($ai_draft['pre_action_snapshot'], $ai_draft['pre_action_snapshot_captured_at']);
        $ai_draft['last_undo_at'] = current_time('mysql', true);
        $settings['ai_draft'] = $ai_draft;
        $this->persistIntegrationSettings($form_id, $settings);

        return [
            'success' => true,
            'message' => __('Undid the last AI action and restored the previous draft state.', 'griffinforms-form-builder'),
        ];
    }

    public function resetToBaseline(int $form_id): array
    {
        $settings = $this->getFormIntegrationSettings($form_id);
        $snapshot = $this->decodeStoredSnapshot($settings['ai_draft']['baseline_snapshot'] ?? '');

        if (!is_array($snapshot)) {
            return [
                'success' => false,
                'message' => __('The starting draft snapshot is missing, so reset is not available for this draft.', 'griffinforms-form-builder'),
            ];
        }

        $restored = $this->restoreSnapshot($form_id, $snapshot, 'reset');
        if (!$restored['success']) {
            return $restored;
        }

        $settings = $this->getFormIntegrationSettings($form_id);
        $ai_draft = (array) ($settings['ai_draft'] ?? []);
        unset($ai_draft['pre_action_snapshot'], $ai_draft['pre_action_snapshot_captured_at']);
        $ai_draft['last_reset_at'] = current_time('mysql', true);
        $settings['ai_draft'] = $ai_draft;
        $this->persistIntegrationSettings($form_id, $settings);

        return [
            'success' => true,
            'message' => __('Reset the draft back to its starting state.', 'griffinforms-form-builder'),
        ];
    }

    protected function exportSnapshot(int $form_id): ?array
    {
        $exporter = new FormExport($form_id);
        $snapshot = $exporter->getFormData();

        if (!is_array($snapshot)) {
            return null;
        }

        return $this->prepareSnapshotForStorage($snapshot);
    }

    protected function prepareSnapshotForStorage(array $snapshot): array
    {
        $integration_settings = $this->decodeValue($snapshot['integration_settings'] ?? []);
        if (is_array($integration_settings)) {
            unset($integration_settings['ai_draft']);
            $snapshot['integration_settings'] = $integration_settings;
        } else {
            unset($snapshot['integration_settings']);
        }

        return $snapshot;
    }

    protected function restoreSnapshot(int $form_id, array $snapshot, string $reason): array
    {
        global $wpdb;

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

        try {
            $current_settings = $this->getFormIntegrationSettings($form_id);
            $current_ai_draft = (array) ($current_settings['ai_draft'] ?? []);

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

            $this->wipeExistingStructure($form_id);
            $page_ids = $this->restorePages($form_id, (array) ($snapshot['pages'] ?? []));
            $this->updateFormRecordFromSnapshot($form_id, $snapshot, $page_ids, $current_ai_draft, $reason);

            $wpdb->query('COMMIT');

            $this->log->add(
                'info',
                'ai_draft',
                $form_id,
                (int) get_current_user_id(),
                'AI draft snapshot restored via ' . sanitize_key($reason) . '.',
                'ai_snapshot_restore'
            );

            return ['success' => true];
        } catch (\Throwable $throwable) {
            $wpdb->query('ROLLBACK');
            $this->log->add(
                'error',
                'ai_draft',
                $form_id,
                (int) get_current_user_id(),
                'AI draft snapshot restore failed: ' . sanitize_text_field($throwable->getMessage()),
                'ai_snapshot_restore'
            );

            return [
                'success' => false,
                'message' => __('Griffin Assist could not restore the requested draft snapshot. The current 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 restorePages(int $form_id, array $pages): array
    {
        $page_ids = [];
        foreach ($pages as $page) {
            if (!is_array($page)) {
                continue;
            }

            $page_id = absint($page['id'] ?? 0);
            if ($page_id <= 0) {
                throw new \RuntimeException('Snapshot page is missing a valid ID.');
            }

            $row_ids = $this->restoreRows($form_id, (array) ($page['rows'] ?? []));
            $this->insertPage($page_id, $form_id, $page, $row_ids);
            $page_ids[] = $page_id;
        }

        return $page_ids;
    }

    protected function restoreRows(int $form_id, array $rows): array
    {
        $row_ids = [];
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }

            $row_id = absint($row['id'] ?? 0);
            if ($row_id <= 0) {
                throw new \RuntimeException('Snapshot row is missing a valid ID.');
            }

            $column_ids = $this->restoreColumns($form_id, (array) ($row['columns'] ?? []));
            $this->insertRow($row_id, $form_id, $row, $column_ids);
            $row_ids[] = $row_id;
        }

        return $row_ids;
    }

    protected function restoreColumns(int $form_id, array $columns): array
    {
        $column_ids = [];
        foreach ($columns as $column) {
            if (!is_array($column)) {
                continue;
            }

            $column_id = absint($column['id'] ?? 0);
            if ($column_id <= 0) {
                throw new \RuntimeException('Snapshot column is missing a valid ID.');
            }

            $field_ids = $this->restoreFields((array) ($column['field_data'] ?? []));
            $this->insertColumn($column_id, $form_id, $column, $field_ids);
            $column_ids[] = $column_id;
        }

        return $column_ids;
    }

    protected function restoreFields(array $fields): array
    {
        $field_ids = [];
        foreach ($fields as $field) {
            if (!is_array($field)) {
                continue;
            }

            $field_id = absint($field['id'] ?? 0);
            if ($field_id <= 0) {
                throw new \RuntimeException('Snapshot field is missing a valid ID.');
            }

            $this->insertField($field_id, $field);
            $field_ids[] = $field_id;
        }

        return $field_ids;
    }

    protected function insertPage(int $page_id, int $form_id, array $page, array $row_ids): void
    {
        global $wpdb;

        $inserted = $wpdb->insert(
            $this->config->getTable('formpage'),
            [
                'id' => $page_id,
                'form_id' => $form_id,
                'name' => sanitize_text_field((string) ($page['name'] ?? __('Page', 'griffinforms-form-builder'))),
                'heading' => sanitize_text_field((string) ($page['heading'] ?? '')),
                'description' => sanitize_textarea_field((string) ($page['description'] ?? '')),
                'pagerows' => maybe_serialize($row_ids),
                'previous_label' => sanitize_text_field((string) ($page['previous_label'] ?? 'Previous')),
                'next_label' => sanitize_text_field((string) ($page['next_label'] ?? 'Next')),
                'page_theme' => absint($page['page_theme'] ?? 0),
                'author' => sanitize_text_field((string) ($page['author'] ?? get_current_user_id())),
                'created' => sanitize_text_field((string) ($page['created'] ?? current_time('mysql', true))),
                'last_editor' => sanitize_text_field((string) ($page['last_editor'] ?? get_current_user_id())),
                'last_edited' => sanitize_text_field((string) ($page['last_edited'] ?? current_time('mysql', true))),
            ],
            ['%d', '%d', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s', '%s', '%s', '%s']
        );

        if ($inserted === false) {
            throw new \RuntimeException('Failed to restore page.');
        }
    }

    protected function insertRow(int $row_id, int $form_id, array $row, array $column_ids): void
    {
        global $wpdb;

        $inserted = $wpdb->insert(
            $this->config->getTable('pagerow'),
            [
                'id' => $row_id,
                'form_id' => $form_id,
                'name' => sanitize_text_field((string) ($row['name'] ?? __('Row', 'griffinforms-form-builder'))),
                'heading' => sanitize_text_field((string) ($row['heading'] ?? '')),
                'description' => sanitize_textarea_field((string) ($row['description'] ?? '')),
                'rowcolumns' => maybe_serialize($column_ids),
                'row_theme' => absint($row['row_theme'] ?? 0),
                'author' => sanitize_text_field((string) ($row['author'] ?? get_current_user_id())),
                'created' => sanitize_text_field((string) ($row['created'] ?? current_time('mysql', true))),
                'last_editor' => sanitize_text_field((string) ($row['last_editor'] ?? get_current_user_id())),
                'last_edited' => sanitize_text_field((string) ($row['last_edited'] ?? current_time('mysql', true))),
            ],
            ['%d', '%d', '%s', '%s', '%s', '%s', '%d', '%s', '%s', '%s', '%s']
        );

        if ($inserted === false) {
            throw new \RuntimeException('Failed to restore row.');
        }
    }

    protected function insertColumn(int $column_id, int $form_id, array $column, array $field_ids): void
    {
        global $wpdb;

        $inserted = $wpdb->insert(
            $this->config->getTable('rowcolumn'),
            [
                'id' => $column_id,
                'form_id' => $form_id,
                'name' => sanitize_text_field((string) ($column['name'] ?? __('Column', 'griffinforms-form-builder'))),
                'heading' => sanitize_text_field((string) ($column['heading'] ?? '')),
                'description' => sanitize_textarea_field((string) ($column['description'] ?? '')),
                'fields' => maybe_serialize($field_ids),
                'width' => absint($column['width'] ?? 12),
                'author' => sanitize_text_field((string) ($column['author'] ?? get_current_user_id())),
                'created' => sanitize_text_field((string) ($column['created'] ?? current_time('mysql', true))),
                'last_editor' => sanitize_text_field((string) ($column['last_editor'] ?? get_current_user_id())),
                'last_edited' => sanitize_text_field((string) ($column['last_edited'] ?? current_time('mysql', true))),
            ],
            ['%d', '%d', '%s', '%s', '%s', '%s', '%d', '%s', '%s', '%s', '%s']
        );

        if ($inserted === false) {
            throw new \RuntimeException('Failed to restore column.');
        }
    }

    protected function insertField(int $field_id, array $field): void
    {
        global $wpdb;

        $inserted = $wpdb->insert(
            $this->config->getTable('field'),
            [
                'id' => $field_id,
                'field_type' => sanitize_key((string) ($field['field_type'] ?? 'inputtext')),
                'name' => sanitize_text_field((string) ($field['name'] ?? '')),
                'heading' => sanitize_text_field((string) ($field['heading'] ?? '')),
                'description' => sanitize_textarea_field((string) ($field['description'] ?? '')),
                'is_required' => !empty($field['is_required']) ? 1 : 0,
                'is_hidden' => !empty($field['is_hidden']) ? 1 : 0,
                'field_options' => $this->encodeStructuredValue($field['field_options'] ?? []),
                'placeholder' => sanitize_text_field((string) ($field['placeholder'] ?? '')),
                'default_value' => $this->encodeStructuredValue($field['default_value'] ?? []),
                'is_readonly' => !empty($field['is_readonly']) ? 1 : 0,
                'is_disabled' => !empty($field['is_disabled']) ? 1 : 0,
                'autocomplete' => !empty($field['autocomplete']) ? 1 : 0,
                'iterations' => $this->encodeStructuredValue($field['iterations'] ?? []),
                'validations' => $this->encodeStructuredValue($field['validations'] ?? []),
                'field_settings' => $this->encodeStructuredValue($field['field_settings'] ?? []),
                'is_reusable' => !empty($field['is_reusable']) ? 1 : 0,
                'field_theme' => absint($field['field_theme'] ?? 0),
                'author' => sanitize_text_field((string) ($field['author'] ?? get_current_user_id())),
                'created' => sanitize_text_field((string) ($field['created'] ?? current_time('mysql', true))),
                'last_editor' => sanitize_text_field((string) ($field['last_editor'] ?? get_current_user_id())),
                'last_edited' => sanitize_text_field((string) ($field['last_edited'] ?? current_time('mysql', true))),
            ],
            ['%d', '%s', '%s', '%s', '%s', '%d', '%d', '%s', '%s', '%s', '%d', '%d', '%d', '%s', '%s', '%s', '%d', '%d', '%s', '%s', '%s', '%s']
        );

        if ($inserted === false) {
            throw new \RuntimeException('Failed to restore field.');
        }
    }

    protected function updateFormRecordFromSnapshot(
        int $form_id,
        array $snapshot,
        array $page_ids,
        array $current_ai_draft,
        string $reason
    ): void {
        global $wpdb;

        $form = $this->prepareSnapshotForStorage($snapshot);
        $integration_settings = $this->decodeValue($form['integration_settings'] ?? []);
        if (!is_array($integration_settings)) {
            $integration_settings = [];
        }

        $integration_settings['ai_draft'] = array_merge(
            $current_ai_draft,
            [
                'last_restored_at' => current_time('mysql', true),
                'last_restore_reason' => sanitize_key($reason),
                'auto_start_pending' => 0,
            ]
        );

        $update_data = [
            'name' => sanitize_text_field((string) ($form['name'] ?? __('AI Draft Form', 'griffinforms-form-builder'))),
            'heading' => sanitize_text_field((string) ($form['heading'] ?? '')),
            'description' => sanitize_textarea_field((string) ($form['description'] ?? '')),
            'folder' => absint($form['folder'] ?? 0),
            'autofill' => !empty($form['autofill']) ? 1 : 0,
            'limit_entries' => $this->encodeStructuredValue($form['limit_entries'] ?? []),
            'availability_window' => $this->encodeStructuredValue($form['availability_window'] ?? []),
            'lifetime_submission_count' => absint($form['lifetime_submission_count'] ?? 0),
            'access_level' => $this->encodeStructuredValue($form['access_level'] ?? []),
            'submission_edit' => $this->encodeStructuredValue($form['submission_edit'] ?? []),
            'user_action' => $this->encodeStructuredValue($form['user_action'] ?? []),
            'autoresponder' => $this->encodeStructuredValue($form['autoresponder'] ?? ''),
            'alert_emails' => $this->encodeStructuredValue($form['alert_emails'] ?? []),
            'conditional_logic' => $this->encodeStructuredValue($form['conditional_logic'] ?? []),
            'integration_settings' => maybe_serialize($integration_settings),
            'compliance_mode' => !empty($form['compliance_mode']) ? 1 : 0,
            'compliance_profile' => sanitize_text_field((string) ($form['compliance_profile'] ?? 'standard')),
            'compliance_flags' => $this->encodeStructuredValue($form['compliance_flags'] ?? []),
            'layout_version' => absint($form['layout_version'] ?? 0),
            'layout_hash' => sanitize_text_field((string) ($form['layout_hash'] ?? '')),
            'save_entries' => sanitize_text_field((string) ($form['save_entries'] ?? '')),
            'formpages' => maybe_serialize($page_ids),
            'submit_label' => sanitize_text_field((string) ($form['submit_label'] ?? 'Submit')),
            'button_layout' => sanitize_text_field((string) ($form['button_layout'] ?? 'ends')),
            'origin' => 'ai',
            'form_theme' => absint($form['form_theme'] ?? 0),
            'last_editor' => get_current_user_id(),
            'last_edited' => current_time('mysql', true),
        ];

        if ($this->formTableHasStatusColumn()) {
            $update_data['status'] = 'draft';
        }

        $updated = $wpdb->update(
            $this->config->getTable('form'),
            $update_data,
            ['id' => $form_id]
        );

        if ($updated === false) {
            throw new \RuntimeException('Failed to restore form record.');
        }
    }

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

        if (is_bool($value)) {
            return $value ? '1' : '0';
        }

        return $value;
    }

    protected function decodeStoredSnapshot($snapshot): ?array
    {
        if (!is_string($snapshot) || $snapshot === '') {
            return null;
        }

        $decoded = json_decode($snapshot, true);
        return is_array($decoded) ? $decoded : null;
    }

    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 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 : [];
    }

    protected function persistIntegrationSettings(int $form_id, array $settings): bool
    {
        global $wpdb;

        $updated = $wpdb->update(
            $this->config->getTable('form'),
            [
                'integration_settings' => maybe_serialize($settings),
                'last_editor' => get_current_user_id(),
                'last_edited' => current_time('mysql', true),
            ],
            ['id' => $form_id],
            ['%s', '%d', '%s'],
            ['%d']
        );

        return $updated !== false;
    }

    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;
    }
}
