<?php

namespace Sitecake\Http;

/**
 * JSON response — replaces Symfony\Component\HttpFoundation\JsonResponse.
 */
class JsonResponse extends Response
{
    protected ?string $callback = null;

    /** @var mixed Raw data before JSON encoding */
    protected $data;

    /**
     * @param mixed $data       Data to encode as JSON
     * @param int   $statusCode HTTP status code
     * @param array $headers    Additional headers
     */
    public function __construct($data = [], int $statusCode = 200, array $headers = [])
    {
        parent::__construct('', $statusCode, $headers);
        $this->data = $data;
        $this->rebuild();
    }

    /**
     * Set a JSONP callback name (wraps JSON in a function call).
     * MED-1: Validates the callback name to only allow safe JS identifiers.
     */
    public function setCallback(?string $callback): self
    {
        if ($callback !== null) {
            // Only allow safe identifier characters — reject anything else
            if (!preg_match('/^[a-zA-Z_$][a-zA-Z0-9_$.]*$/', $callback)) {
                $callback = null;
            }
        }
        $this->callback = $callback;
        $this->rebuild();
        return $this;
    }

    private function rebuild(): void
    {
        $json = json_encode($this->data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        if ($json === false) {
            // Fallback: try encoding with JSON_PARTIAL_OUTPUT_ON_ERROR or plain ASCII
            $json = json_encode($this->data, JSON_PARTIAL_OUTPUT_ON_ERROR);
        }
        if ($json === false) {
            $json = '{"status":-1,"errMessage":"JSON encoding failed: ' . addslashes(json_last_error_msg()) . '"}';
        }

        if ($this->callback !== null) {
            $this->headers->set('Content-Type', 'application/javascript');
            $this->content = $this->callback . '(' . $json . ')';
        } else {
            $this->headers->set('Content-Type', 'application/json');
            $this->content = $json;
        }
    }
}
