<?php

namespace GriffinForms\Includes\Payments;

use GriffinForms\Traits\Singleton;

class SubmissionStatusManager extends \GriffinForms\Format
{
    use Singleton;

    /** @var string */
    protected $submission_table;

    /** @var array<string> */
    protected $allowed_statuses = array('pending', 'paid', 'failed', 'abandoned');

    protected function setSubmissiontable(): void
    {
        $this->submission_table = $this->config->getTable('submission');
    }

    /**
     * Update payment status for a submission.
     *
     * @param int    $submission_id
     * @param string $status
     * @param array  $context
     *
     * @return bool
     */
    public function updateStatus(int $submission_id, string $status, array $context = array()): bool
    {
        $status = strtolower($status);
        if ($submission_id <= 0 || !in_array($status, $this->allowed_statuses, true)) {
            return false;
        }

        $submission = $this->loadSubmission($submission_id);
        if (!$submission) {
            return false;
        }

        if (isset($submission['payment_status']) && $submission['payment_status'] === $status) {
            return true;
        }
        $is_payment_form = !empty($submission['is_payment_form']);

        global $wpdb;
        $updated = $wpdb->update(
            $this->submission_table,
            array(
                'payment_status' => $status,
                'last_editor'    => get_current_user_id(),
                'last_edited'    => current_time('mysql', false),
            ),
            array('id' => $submission_id),
            array('%s', '%d', '%s'),
            array('%d')
        );

        if ($updated === false) {
            return false;
        }

        $old_status = $submission['payment_status'] ?? 'pending';
        $submission['payment_status'] = $status;

        /**
         * Fired whenever a submission's payment_status value changes.
         */
        do_action('griffinforms_payment_status_changed', $submission_id, $old_status, $status, $context);

        $gateway = $context['gateway'] ?? '';
        if (empty($gateway)) {
            $gateway = 'pending';
        }
        $category = $is_payment_form ? sprintf('Payment Integration (%s)', $gateway) : 'submission';

        if ($status === 'paid') {
            $this->runPostSubmissionPipeline($submission, $context);
            $this->log->add(
                'info',
                'submission',
                $submission_id,
                get_current_user_id(),
                $is_payment_form
                    ? __('Submission marked as paid. Post-submission actions executed (where configured).', 'griffinforms-form-builder')
                    : __('Submission saved as paid (no payment required). Post-submission actions executed (where configured).', 'griffinforms-form-builder'),
                $category
            );

            /**
             * Fires after a submission has been marked as paid and post-submission actions executed.
             */
            do_action('griffinforms_payment_completed', $submission_id, $submission, $context);
        } elseif ($status === 'failed') {
            $this->log->add(
                'error',
                'submission',
                $submission_id,
                get_current_user_id(),
                $is_payment_form
                    ? __('Payment failed for this submission.', 'griffinforms-form-builder')
                    : __('Payment status set to failed (submission did not require payment).', 'griffinforms-form-builder'),
                $category
            );
        } elseif ($status === 'abandoned') {
            $this->log->add(
                'warning',
                'submission',
                $submission_id,
                get_current_user_id(),
                $is_payment_form
                    ? __('Payment was abandoned before completion.', 'griffinforms-form-builder')
                    : __('Payment status set to abandoned (submission did not require payment).', 'griffinforms-form-builder'),
                $category
            );
        }

        return true;
    }

    /**
     * Convenience wrapper to mark a submission as paid.
     */
    public function markPaid(int $submission_id, array $context = array()): bool
    {
        return $this->updateStatus($submission_id, 'paid', $context);
    }

    /**
     * Convenience wrapper to mark a submission as failed.
     */
    public function markFailed(int $submission_id, array $context = array()): bool
    {
        return $this->updateStatus($submission_id, 'failed', $context);
    }

    /**
     * Convenience wrapper to mark a submission as abandoned.
     */
    public function markAbandoned(int $submission_id, array $context = array()): bool
    {
        return $this->updateStatus($submission_id, 'abandoned', $context);
    }

    protected function loadSubmission(int $submission_id): ?array
    {
        global $wpdb;

        $row = $wpdb->get_row(
            $wpdb->prepare('SELECT * FROM %i WHERE id = %d', $this->submission_table, $submission_id),
            ARRAY_A
        );

        return $row ?: null;
    }

    protected function runPostSubmissionPipeline(array $submission, array $context = array()): void
    {
        $form_id = isset($submission['form_id']) ? (int) $submission['form_id'] : 0;

        $action_payload = array(
            'form'       => $form_id,
            'submission' => $submission,
            'sub_id'     => isset($submission['id']) ? (int) $submission['id'] : 0,
            'sub_token'  => $submission['public_token'] ?? '',
            'position'   => 'last',
            'page'       => 0,
            'payment_context' => $context,
        );

        $actions = new \GriffinForms\Frontend\Actions\PostSubmission($action_payload);
        $actions->runActions();
        $actions->runResponse();
    }
}
