<?php

declare(strict_types=1);

namespace App\Framework\Validation;

use App\Framework\Http\MiddlewareContext;
use App\Framework\Http\Response;
use App\Framework\Http\Responses\JsonResponse;
use App\Framework\Http\Responses\RedirectResponse;
use App\Framework\Http\Session\FormIdGenerator;
use App\Framework\Http\Session\Session;
use App\Framework\Http\Session\SessionManager;
use App\Framework\Http\Status;
use App\Framework\Validation\Exceptions\ValidationException;

/**
 * Handles ValidationException by storing form data and errors in session
 * and creating appropriate responses (redirect for web, JSON for API)
 */
final readonly class ValidationFormHandler
{
    public function __construct(
        private Session $session,
        private FormIdGenerator $formIdGenerator,
        private SessionManager $sessionManager,
    ) {
    }

    /**
     * Handle a ValidationException by storing form data and errors
     * and creating an appropriate response
     */
    public function handle(ValidationException $exception, ?MiddlewareContext $context = null): Response
    {
        $isApiRequest = $this->isApiRequest($context);

        // For API requests, return JSON response directly without storing in session
        if ($isApiRequest) {
            error_log("ValidationFormHandler: API request detected, returning direct JSON response");

            return $this->createJsonResponse($exception);
        }

        // For web requests, store data in session for form repopulation
        $formId = $this->formIdGenerator->generateFromRequestContext($context);

        // Store old input data for form repopulation
        $requestData = $this->extractRequestData($context);

        error_log("ValidationFormHandler: Extracted request data: " . json_encode($requestData));
        if (! empty($requestData)) {
            $this->session->form->store($formId, $requestData);
            error_log("ValidationFormHandler: Stored form data in session");
        }

        // Store validation errors for web form redisplay
        $this->session->validation->add($formId, $exception->getAllErrors());

        $response = $this->sessionManager->saveSession($this->session, $this->createRedirectResponse($context));

        error_log("ValidationFormHandler: Session data stored: " . json_encode($this->session->all()));

        return $response;
    }

    /**
     * Extract request data from various sources
     */
    private function extractRequestData(?MiddlewareContext $context): array
    {
        if (! $context?->request) {
            error_log("ValidationFormHandler: No request in context");

            return [];
        }

        // Try different data sources
        $data = [];

        // From parsed body
        if ($context->request->parsedBody?->data) {
            error_log("ValidationFormHandler: Found parsedBody->data: " . json_encode($context->request->parsedBody->data));
            $data = array_merge($data, $context->request->parsedBody->data);
        } else {
            error_log("ValidationFormHandler: No parsedBody->data found");
        }

        // From POST data directly
        if (! empty($_POST)) {
            error_log("ValidationFormHandler: Found POST data: " . json_encode($_POST));
            $data = array_merge($data, $_POST);
        } else {
            error_log("ValidationFormHandler: No POST data found");
        }

        // Remove system fields and potential honeypot fields
        unset($data['_token'], $data['_form_id'], $data['_honeypot_name'], $data['_form_start_time']);

        // Remove common honeypot field names
        $honeypotNames = ['email_confirm', 'website_url', 'phone_number', 'user_name', 'company_name'];
        foreach ($honeypotNames as $honeypotName) {
            unset($data[$honeypotName]);
        }

        return $data;
    }

    /**
     * Check if this is an API request based on Accept header
     */
    private function isApiRequest(?MiddlewareContext $context): bool
    {
        if (! $context?->request) {
            return false;
        }

        $requestedWith = $context->request->headers->getFirst('X-Requested-With');
        if ($requestedWith === 'XMLHttpRequest') {
            return true;
        }

        // Get the first Accept header value or empty string if not present
        $acceptHeader = $context->request->headers->getFirst('Accept', '');

        return str_contains($acceptHeader, 'application/json') ||
               str_contains($acceptHeader, 'application/api') ||
               str_starts_with($context->request->path, '/api/');
    }

    /**
     * Create JSON response for API requests
     */
    private function createJsonResponse(ValidationException $exception): JsonResponse
    {
        return new JsonResponse(
            body: [
                'error' => 'Validation Error',
                'message' => $exception->getMessage(),
                'errors' => $exception->getAllErrors(),
            ],
            status: Status::UNPROCESSABLE_ENTITY,
        );
    }

    /**
     * Create redirect response for web requests
     */
    private function createRedirectResponse(?MiddlewareContext $context): RedirectResponse
    {
        // Try to get referer URL for redirect
        $refererUrl = $context?->request?->server?->getRefererUri();

        if (! $refererUrl) {
            // Fallback to current request URL without query parameters
            $currentUrl = $context?->request?->path ?? '/';
            $refererUrl = $currentUrl;
        }

        return new RedirectResponse($refererUrl);
    }

    /**
     * Check if there are validation errors for a specific form
     */
    public function hasErrors(string $formId): bool
    {
        return $this->session->validation->has($formId);
    }

    /**
     * Get validation errors for a specific form
     */
    public function getErrors(string $formId): array
    {
        return $this->session->validation->get($formId);
    }

    /**
     * Check if there is old input data for a specific form
     */
    public function hasOldInput(string $formId): bool
    {
        return $this->session->form->has($formId);
    }

    /**
     * Get old input data for a specific form
     */
    public function getOldInput(string $formId): array
    {
        return $this->session->form->get($formId);
    }

    /**
     * Clear validation errors and old input for a specific form
     * (typically called after successful form submission)
     */
    public function clearForm(string $formId): void
    {
        $this->session->validation->clear($formId);
        $this->session->form->clear($formId);
    }

    /**
     * Clear all validation errors and old input data
     */
    public function clearAll(): void
    {
        $this->session->validation->clearAll();
        $this->session->form->clearAll();
    }
}
