<?php

namespace App\Core;

/**
 * Response - Gestion des réponses HTTP
 * 
 * Encapsule la réponse HTTP à envoyer au client
 */
class Response
{
    /**
     * Contenu de la réponse
     * @var string
     */
    protected string $content;

    /**
     * Code de statut HTTP
     * @var int
     */
    protected int $statusCode;

    /**
     * Headers HTTP
     * @var array
     */
    protected array $headers;

    /**
     * Messages de statut HTTP
     * @var array
     */
    protected static array $statusTexts = [
        200 => 'OK',
        201 => 'Created',
        202 => 'Accepted',
        204 => 'No Content',
        301 => 'Moved Permanently',
        302 => 'Found',
        304 => 'Not Modified',
        400 => 'Bad Request',
        401 => 'Unauthorized',
        403 => 'Forbidden',
        404 => 'Not Found',
        405 => 'Method Not Allowed',
        422 => 'Unprocessable Entity',
        429 => 'Too Many Requests',
        500 => 'Internal Server Error',
        502 => 'Bad Gateway',
        503 => 'Service Unavailable',
    ];

    /**
     * Constructeur
     *
     * @param string $content
     * @param int $statusCode
     * @param array $headers
     */
    public function __construct(string $content = '', int $statusCode = 200, array $headers = [])
    {
        $this->content = $content;
        $this->statusCode = $statusCode;
        $this->headers = $headers;
    }

    /**
     * Créer une réponse JSON
     *
     * @param mixed $data
     * @param int $statusCode
     * @param array $headers
     * @return self
     */
    public static function json($data, int $statusCode = 200, array $headers = []): self
    {
        $headers['Content-Type'] = 'application/json';
        return new self(json_encode($data), $statusCode, $headers);
    }

    /**
     * Créer une réponse de redirection
     *
     * @param string $url
     * @param int $statusCode
     * @return self
     */
    public static function redirect(string $url, int $statusCode = 302): self
    {
        return new self('', $statusCode, ['Location' => $url]);
    }

    /**
     * Créer une réponse de vue (HTML)
     *
     * @param string $view
     * @param array $data
     * @param int $statusCode
     * @return self
     */
    public static function view(string $view, array $data = [], int $statusCode = 200): self
    {
        // Charger la vue
        $viewPath = ROOT_PATH . '/resources/views/' . str_replace('.', '/', $view) . '.php';
        
        if (!file_exists($viewPath)) {
            return new self("View not found: {$view}", 500);
        }

        // Extraire les variables pour la vue
        extract($data);

        // Capturer le contenu de la vue
        ob_start();
        require $viewPath;
        $content = ob_get_clean();

        return new self($content, $statusCode, ['Content-Type' => 'text/html']);
    }

    /**
     * Créer une réponse de fichier à télécharger
     *
     * @param string $filePath
     * @param string|null $name
     * @return self
     */
    public static function download(string $filePath, ?string $name = null): self
    {
        if (!file_exists($filePath)) {
            return new self('File not found', 404);
        }

        $name = $name ?? basename($filePath);
        $content = file_get_contents($filePath);
        
        $headers = [
            'Content-Type' => mime_content_type($filePath),
            'Content-Disposition' => 'attachment; filename="' . $name . '"',
            'Content-Length' => filesize($filePath)
        ];

        return new self($content, 200, $headers);
    }

    /**
     * Créer une réponse vide (204 No Content)
     *
     * @return self
     */
    public static function noContent(): self
    {
        return new self('', 204);
    }

    /**
     * Définir le contenu
     *
     * @param string $content
     * @return self
     */
    public function setContent(string $content): self
    {
        $this->content = $content;
        return $this;
    }

    /**
     * Définir le contenu HTML (alias de setContent)
     *
     * @param string $html
     * @return self
     */
    public function html(string $html): self
    {
        $this->content = $html;
        if (!isset($this->headers['Content-Type'])) {
            $this->headers['Content-Type'] = 'text/html; charset=UTF-8';
        }
        return $this;
    }

    /**
     * Définir le contenu JSON (méthode d'instance)
     *
     * @param mixed $data
     * @return self
     */
    public function setJson($data): self
    {
        $this->content = json_encode($data);
        $this->headers['Content-Type'] = 'application/json';
        return $this;
    }

    /**
     * Obtenir le contenu
     *
     * @return string
     */
    public function getContent(): string
    {
        return $this->content;
    }

    /**
     * Définir le code de statut
     *
     * @param int $statusCode
     * @return self
     */
    public function setStatusCode(int $statusCode): self
    {
        $this->statusCode = $statusCode;
        return $this;
    }

    /**
     * Obtenir le code de statut
     *
     * @return int
     */
    public function getStatusCode(): int
    {
        return $this->statusCode;
    }

    /**
     * Ajouter un header
     *
     * @param string $key
     * @param string $value
     * @return self
     */
    public function withHeader(string $key, string $value): self
    {
        $this->headers[$key] = $value;
        return $this;
    }

    /**
     * Ajouter plusieurs headers
     *
     * @param array $headers
     * @return self
     */
    public function withHeaders(array $headers): self
    {
        $this->headers = array_merge($this->headers, $headers);
        return $this;
    }

    /**
     * Obtenir un header
     *
     * @param string $key
     * @return string|null
     */
    public function getHeader(string $key): ?string
    {
        return $this->headers[$key] ?? null;
    }

    /**
     * Obtenir tous les headers
     *
     * @return array
     */
    public function getHeaders(): array
    {
        return $this->headers;
    }

    /**
     * Ajouter un cookie
     *
     * @param string $name
     * @param string $value
     * @param int $expire
     * @param string $path
     * @param string $domain
     * @param bool $secure
     * @param bool $httpOnly
     * @return self
     */
    public function withCookie(
        string $name,
        string $value,
        int $expire = 0,
        string $path = '/',
        string $domain = '',
        bool $secure = false,
        bool $httpOnly = true
    ): self {
        setcookie($name, $value, $expire, $path, $domain, $secure, $httpOnly);
        return $this;
    }

    /**
     * Envoyer la réponse au client
     *
     * @return void
     */
    public function send(): void
    {
        // Envoyer le code de statut
        http_response_code($this->statusCode);

        // Envoyer les headers
        foreach ($this->headers as $key => $value) {
            header("{$key}: {$value}");
        }

        // Envoyer le contenu
        echo $this->content;
    }

    /**
     * Convertir en chaîne
     *
     * @return string
     */
    public function __toString(): string
    {
        return $this->content;
    }

    /**
     * Obtenir le texte du statut HTTP
     *
     * @return string
     */
    public function getStatusText(): string
    {
        return self::$statusTexts[$this->statusCode] ?? 'Unknown';
    }

    /**
     * Vérifier si la réponse est un succès (2xx)
     *
     * @return bool
     */
    public function isSuccessful(): bool
    {
        return $this->statusCode >= 200 && $this->statusCode < 300;
    }

    /**
     * Vérifier si la réponse est une redirection (3xx)
     *
     * @return bool
     */
    public function isRedirection(): bool
    {
        return $this->statusCode >= 300 && $this->statusCode < 400;
    }

    /**
     * Vérifier si la réponse est une erreur client (4xx)
     *
     * @return bool
     */
    public function isClientError(): bool
    {
        return $this->statusCode >= 400 && $this->statusCode < 500;
    }

    /**
     * Vérifier si la réponse est une erreur serveur (5xx)
     *
     * @return bool
     */
    public function isServerError(): bool
    {
        return $this->statusCode >= 500 && $this->statusCode < 600;
    }
}
