<?php

declare(strict_types=1);

namespace App\Middleware;

use App\Core\MiddlewareInterface;
use App\Core\Request;
use App\Core\Response;

/**
 * CorsMiddleware
 *
 * Applied only to the specific public, cross-origin endpoints meant to
 * be called directly from browser JS on another domain (the marketing
 * website) — the Chatbot widget and the public inquiry/newsletter
 * endpoint. Every other route in this app is same-origin (the CRM's
 * own frontend) and deliberately has no CORS headers at all, which is
 * the safer default — CORS is opt-in per route here, never global.
 *
 * Allowed origins come from two places, unioned together:
 *   1. CORS_ALLOWED_ORIGINS in .env (comma separated) — a static,
 *      server-level list.
 *   2. chatbot_settings.allowed_origin, per business line — set from
 *      Admin > Website Chatbot, no server access or restart needed.
 *      This is the one most admins should actually use day to day;
 *      the .env list exists for anything that isn't tied to a specific
 *      business line's chatbot config (or for environments where DB
 *      access from this middleware isn't wanted).
 *
 * Never a wildcard "*", since these endpoints can create real records
 * (leads) and a wildcard would let any site on the internet call them
 * from a visitor's browser.
 */
final class CorsMiddleware implements MiddlewareInterface
{
    public function handle(Request $request, callable $next): Response
    {
        $allowedOrigins = $this->allowedOrigins();
        $requestOrigin = $request->header('Origin');

        $originToAllow = ($requestOrigin !== null && in_array($requestOrigin, $allowedOrigins, true))
            ? $requestOrigin
            : null;

        if ($request->method() === 'OPTIONS') {
            $response = Response::html('', 204);

            return $this->withCorsHeaders($response, $originToAllow);
        }

        $response = $next($request);

        return $this->withCorsHeaders($response, $originToAllow);
    }

    /** @return array<int,string> */
    private function allowedOrigins(): array
    {
        $fromEnv = array_filter(array_map('trim', explode(',', (string) config('app.cors_allowed_origins', ''))));

        $fromDb = [];
        try {
            $stmt = \App\Core\Database::connection()->query(
                'SELECT DISTINCT allowed_origin FROM chatbot_settings WHERE allowed_origin IS NOT NULL AND allowed_origin != \'\''
            );
            $fromDb = $stmt->fetchAll(\PDO::FETCH_COLUMN);
        } catch (\Throwable $e) {
            // A DB hiccup here should never take down the widget entirely —
            // fall back to whatever the .env list allows and move on.
            \App\Core\Logger::error('CorsMiddleware: failed to load allowed_origin from chatbot_settings', ['error' => $e->getMessage()]);
        }

        return array_values(array_unique(array_merge($fromEnv, $fromDb)));
    }

    private function withCorsHeaders(Response $response, ?string $originToAllow): Response
    {
        if ($originToAllow === null) {
            return $response;
        }

        return $response
            ->withHeader('Access-Control-Allow-Origin', $originToAllow)
            ->withHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
            ->withHeader('Access-Control-Allow-Headers', 'Content-Type')
            ->withHeader('Access-Control-Max-Age', '600')
            ->withHeader('Vary', 'Origin');
    }
}
