<?php

namespace Formatrica\REST;

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

use Formatrica\Submission\Exception\Submission_Exception;
use Formatrica\Submission\Submission_Service;
use WP_REST_Request;
use WP_REST_Response;

final class Submission_Controller extends Base_Rest_Controller implements Rest_Controller
{
    // Inherit namespace from Base_Rest_Controller

    private Submission_Service $service;

    private \Formatrica\Submission\Security\IP_Handler $ip_handler;

    public function __construct(?Submission_Service $service = null, ?\Formatrica\Submission\Security\IP_Handler $ip_handler = null)
    {
        $this->service = $service ?? new Submission_Service();
        $this->ip_handler = $ip_handler ?? new \Formatrica\Submission\Security\IP_Handler();
    }

    public function register_routes(): void
    {
        /**
         * Public endpoint: accept form submissions from unauthenticated visitors.
         *
         * This route is intentionally public (`__return_true`) because front-end
         * forms must be submittable by any site visitor, including logged-out users.
         *
         * Security is enforced at the application layer by the Submission_Service:
         *  - CSRF token validation (`_fmt_nonce`)
         *  - Honeypot field detection
         *  - Minimum submission time check (anti-bot)
         *  - IP-based rate limiting
         *  - CAPTCHA verification (reCAPTCHA v3 / Turnstile / FriendlyCaptcha)
         *  - Field-level sanitisation and validation
         *  - File upload type and size restrictions
         */
        register_rest_route(
            self::NAMESPACE,
            '/forms/(?P<form_id>\\d+)/submissions',
            [
                'methods' => \WP_REST_Server::CREATABLE,
                'callback' => [$this, 'handle_submission'],
                'permission_callback' => '__return_true',
            ]
        );
    }

    public function handle_submission(WP_REST_Request $request): WP_REST_Response
    {
        $form_id = (int) $request->get_param('form_id');

        // Bound the request body before parsing (F-03). A JSON/urlencoded form
        // gets a tight 1 MB cap (transitively limiting field count, value sizes,
        // and array entries). Multipart requests carry file uploads, so they get
        // a separate, larger ceiling sized for the permitted upload total plus
        // multipart overhead; the per-file and aggregate byte caps in the
        // uploader still apply independently.
        $content_type = (string) $request->get_header('content-type');
        if (stripos($content_type, 'multipart/form-data') !== false) {
            $default_multipart = min((int) wp_max_upload_size(), 50 * MB_IN_BYTES) + (5 * MB_IN_BYTES);
            $max_body = (int) apply_filters('formatrica_max_multipart_request_body', $default_multipart, $form_id);
        } else {
            $max_body = (int) apply_filters('formatrica_max_request_body', 1048576, $form_id);
        }
        if ($this->body_too_large($request, $max_body)) {
            return $this->payload_too_large_response();
        }

        $data = $request->get_json_params();

        if (!is_array($data) || empty($data)) {
            $data = $request->get_body_params();

            if (isset($data['payload']) && is_string($data['payload'])) {
                $decoded = json_decode($data['payload'], true);
                if (is_array($decoded)) {
                    $data = $decoded;
                } else {
                    $data = [];
                }
            }
        }

        $files = $request->get_file_params();

        $context = [
            'ip_address' => $this->ip_handler->detect(),
            'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(wp_unslash((string) $_SERVER['HTTP_USER_AGENT'])) : '',
        ];

        // Headless API mode (Pro api.headless): the API-key header only REQUESTS
        // headless handling — it is an untrusted signal, never proof of auth.
        // Only the Pro Headless_Auth_Stage may set 'headless_authenticated' after
        // its key/capability/IP gates pass; the browser nonce/anti-bot/captcha
        // stages skip solely on that authenticated marker. On a Free package
        // (no Pro auth stage) a requested-but-unauthenticated request therefore
        // fails the nonce gate closed instead of bypassing it.
        $api_key = $request->get_header('X-Formatrica-Api-Key');
        if (is_string($api_key) && $api_key !== '') {
            $context['headless_requested'] = true;
            $context['headless_api_key'] = $api_key;
        }

        try {
            $result = $this->service->handle($form_id, $data, $context, $files);
        } catch (Submission_Exception $exception) {
            return new WP_REST_Response([
                'success' => false,
                'message' => $exception->getMessage(),
                'errors' => $exception->errors(),
            ], $exception->status());
        } catch (\Throwable $error) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                do_action('formatrica_debug_log', '[Formatrica] Submission failure: ' . $error->getMessage());
            }

            // Never return raw exception text to unauthenticated visitors, even
            // under WP_DEBUG — it can leak filesystem paths, SQL fragments, or
            // provider internals. The detail goes to the debug log only.
            return new WP_REST_Response([
                'success' => false,
                'message' => __('Unexpected error. Please try again later.', 'formatrica'),
            ], 500);
        }

        return new WP_REST_Response($result, 200);
    }
}
