<?php

declare(strict_types=1);

namespace App\Middleware;

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

/**
 * TenantMiddleware
 *
 * Attach to every tenant-facing route (the CRM app itself) via
 * ->get(..., middleware: ['tenant', 'auth']) — tenant resolution goes
 * BEFORE auth, since which company you're even trying to log into
 * depends on the subdomain, not the session.
 *
 * Root domain / www / no matching tenant -> bounce to the marketing +
 * signup site. Matching tenant but subscription lapsed -> bounce to a
 * "your subscription has ended" page (still same subdomain, so billing
 * details are unambiguous) UNLESS the route itself is the billing/renew
 * flow, which must stay reachable while locked out.
 *
 * Also guards against a logged-in session leaking across tenants: if
 * someone is authenticated but their session's business line doesn't
 * match the subdomain they're now on, they're logged out rather than
 * silently shown (or worse, allowed to act on) another company's data.
 */
final class TenantMiddleware implements MiddlewareInterface
{
    /** Routes that must stay reachable even when the tenant's subscription has lapsed. */
    private const BILLING_ROUTE_PREFIXES = ['/billing', '/logout'];

    public function handle(Request $request, callable $next): Response
    {
        $appDomain = (string) config('app.domain', '');
        $host = (string) ($request->header('Host') ?? '');

        $slug = $appDomain !== '' ? Tenant::subdomainFromHost($host, $appDomain) : null;

        if ($slug === null) {
            return Response::redirect(rtrim((string) config('app.marketing_url', config('app.url')), '/'));
        }

        $tenant = Tenant::findBySlug($slug);

        if ($tenant === null) {
            return Response::redirect(rtrim((string) config('app.marketing_url', config('app.url')), '/') . '/signup?unknown=' . urlencode($slug));
        }

        Tenant::setCurrent($tenant);

        // A session authenticated on another tenant's subdomain is not
        // valid here — log it out rather than trust it.
        if (Auth::check() && !$this->userBelongsToTenant((int) Auth::id(), (int) $tenant['id'])) {
            Auth::logout();
        }

        $path = $request->path();
        $isBillingRoute = false;
        foreach (self::BILLING_ROUTE_PREFIXES as $prefix) {
            if (str_starts_with($path, $prefix)) {
                $isBillingRoute = true;
                break;
            }
        }

        if (Tenant::isExpired() && !$isBillingRoute) {
            return Response::redirect(base_url('billing/expired'));
        }

        return $next($request);
    }

    private function userBelongsToTenant(int $userId, int $businessLineId): bool
    {
        $stmt = \App\Core\Database::connection()->prepare(
            'SELECT 1 FROM user_business_lines WHERE user_id = :user_id AND business_line_id = :bl LIMIT 1'
        );
        $stmt->execute(['user_id' => $userId, 'bl' => $businessLineId]);

        return $stmt->fetchColumn() !== false;
    }
}
