<?php
declare(strict_types=1);

namespace App\Http;

use JsonException;

final class JsonResponse
{
    public function __construct(
        private readonly int $status,
        private readonly mixed $data = null,
        private readonly array $meta = [],
        private readonly ?array $error = null,
    ) {
    }

    public static function success(mixed $data = null, array $meta = [], int $status = 200): self
    {
        return new self($status, $data, $meta, null);
    }

    public static function failure(int $status, string $code, string $message, array $meta = []): self
    {
        return new self($status, null, $meta, [
            'code' => $code,
            'message' => $message,
        ]);
    }

    public function status(): int
    {
        return $this->status;
    }

    /**
     * @return array<string, string>
     */
    public function headers(): array
    {
        return [
            'Content-Type' => 'application/json; charset=utf-8',
            'Cache-Control' => $this->status >= 400 ? 'no-store' : 'private, no-cache',
            'X-Quaeriva-Version' => app_version(),
            'X-Content-Type-Options' => 'nosniff',
            'X-Frame-Options' => 'DENY',
            'Referrer-Policy' => 'no-referrer',
        ];
    }

    /**
     * @return array{data: mixed, meta: array<string, mixed>, error: ?array<string, string>}
     */
    public function toArray(): array
    {
        return [
            'data' => $this->data,
            'meta' => $this->meta,
            'error' => $this->error,
        ];
    }

    /**
     * @throws JsonException
     */
    public function toJson(): string
    {
        return json_encode(
            $this->toArray(),
            JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE
        );
    }

    public function send(): void
    {
        http_response_code($this->status);

        foreach ($this->headers() as $name => $value) {
            header($name . ': ' . $value);
        }

        echo $this->toJson();
    }
}
