<?php

declare(strict_types=1);

namespace BotGuard\Core;

/**
 * HTTP response value object.
 *
 * Collects status, headers and body so middleware can post-process a response
 * (e.g. attach security headers) before it is sent to the client exactly once.
 */
final class Response
{
    private int $status = 200;

    private string $body = '';

    /** @var array<string, string> */
    private array $headers = [];

    /** @var array<int, array{0:string,1:string,2:array<string,mixed>}> */
    private array $cookies = [];

    private bool $sent = false;

    public function __construct(string $body = '', int $status = 200, array $headers = [])
    {
        $this->body = $body;
        $this->status = $status;
        $this->headers = $headers;
    }

    public static function make(string $body = '', int $status = 200): self
    {
        return new self($body, $status);
    }

    /**
     * Build a JSON response, encoding the payload safely.
     */
    public static function json(mixed $data, int $status = 200): self
    {
        $body = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);

        return (new self($body === false ? '{}' : $body, $status))
            ->withHeader('Content-Type', 'application/json; charset=utf-8');
    }

    /**
     * Build an HTTP redirect response.
     */
    public static function redirect(string $url, int $status = 302): self
    {
        return (new self('', $status))->withHeader('Location', $url);
    }

    public function withHeader(string $name, string $value): self
    {
        $this->headers[$name] = $value;

        return $this;
    }

    public function withStatus(int $status): self
    {
        $this->status = $status;

        return $this;
    }

    public function withCookie(string $name, string $value, array $options = []): self
    {
        $this->cookies[] = [$name, $value, $options];

        return $this;
    }

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

    public function body(): string
    {
        return $this->body;
    }

    public function header(string $name): ?string
    {
        return $this->headers[$name] ?? null;
    }

    /**
     * Emit the response to the client. Safe to call once.
     */
    public function send(): void
    {
        if ($this->sent) {
            return;
        }

        if (! headers_sent()) {
            http_response_code($this->status);

            foreach ($this->headers as $name => $value) {
                header("{$name}: {$value}", true);
            }

            foreach ($this->cookies as [$name, $value, $options]) {
                setcookie($name, $value, $options);
            }
        }

        echo $this->body;
        $this->sent = true;
    }
}
