import type { Request, Response } from "express";
import jwt, { type JwtPayload } from "jsonwebtoken";
import { IS_PRODUCTION, PUBLIC_API_URL } from "../config/environment";
import { parseTankUserIdFromOAuthState } from "./smartcarCallbackUser";

/** Claim — must match on verify */
const BIND_PURPOSE = "tt_smartcar_bind_v1";

function bindJwtSecret(): string {
  return String(process.env.JWT_SECRET ?? "").trim();
}

function bindTtlMinutes(): number {
  const raw = process.env.SMARTCAR_OAUTH_SESSION_MAX_MINUTES?.trim();
  const n = raw ? Number(raw) : NaN;
  const m = Number.isFinite(n) ? n : 120;
  return Math.min(720, Math.max(5, m));
}

function cookieSecureSameSite(): { secure: boolean; sameSite: "lax" | "strict" | "none" } {
  const publicHttps =
    PUBLIC_API_URL.length > 0 && PUBLIC_API_URL.toLowerCase().startsWith("https://");
  let sameSiteRaw = process.env.SESSION_COOKIE_SAMESITE?.trim().toLowerCase();
  let sameSite: "lax" | "strict" | "none" = "lax";
  if (sameSiteRaw === "strict") sameSite = "strict";
  else if (sameSiteRaw === "none") sameSite = "none";

  let secure =
    Boolean(IS_PRODUCTION || publicHttps || process.env.COOKIE_SECURE?.trim() === "true") ||
    sameSite === "none";
  if (sameSite === "none" && !secure) secure = true;

  return { secure, sameSite };
}

/**
 * HttpOnly JWT cookie: ties this browser (when it hits `/smartcar/redirect`) to the Tank user
 * issued at `/smartcar/login`, if echoed `Mongo bridge`/`state` JWT is missing.
 * Companion to session + `ttb1_…` OAuth state per [Smartcar Handle the Response](https://smartcar.com/docs/connect/handle-the-response).
 */
export function smartcarOAuthBindCookieName(): string {
  return (
    process.env.SMARTCAR_OAUTH_BIND_COOKIE?.trim().replace(/^["']|["']$/g, "") ||
    "tt_sc_bind"
  );
}

export function issueSmartcarOAuthBindCookie(res: Response, tankUserMongoIdHex: string): void {
  const secret = bindJwtSecret();
  if (!secret) return;
  const uid = parseTankUserIdFromOAuthState(tankUserMongoIdHex.trim());
  if (!uid) return;

  const token = jwt.sign(
    { sc_bind: BIND_PURPOSE, uid },
    secret,
    { expiresIn: `${bindTtlMinutes()}m`, algorithm: "HS256" }
  );

  const { secure, sameSite } = cookieSecureSameSite();
  const ms = bindTtlMinutes() * 60 * 1000;

  res.cookie(smartcarOAuthBindCookieName(), token, {
    maxAge: ms,
    httpOnly: true,
    secure,
    sameSite,
    path: "/",
  });
}

/** Parse Cookie header manually (cookie-parser not required). */
function readRawCookie(req: Request, name: string): string | null {
  const raw = typeof req.headers.cookie === "string" ? req.headers.cookie : "";
  if (!raw.trim()) return null;
  const target = `${name}=`;
  for (const part of raw.split(";")) {
    const p = part.trim();
    if (p.startsWith(target)) {
      try {
        return decodeURIComponent(p.slice(target.length));
      } catch {
        return p.slice(target.length);
      }
    }
  }
  return null;
}

export function readTankTrackUserIdFromOAuthBindCookie(req: Request): string | null {
  const secret = bindJwtSecret();
  if (!secret) return null;
  const tok = readRawCookie(req, smartcarOAuthBindCookieName());
  if (!tok?.trim()) return null;
  try {
    const d = jwt.verify(tok.trim(), secret, { algorithms: ["HS256"] }) as JwtPayload;
    if (d.sc_bind !== BIND_PURPOSE || typeof d.uid !== "string") return null;
    return parseTankUserIdFromOAuthState(d.uid.trim());
  } catch {
    return null;
  }
}

export function clearSmartcarOAuthBindCookie(res: Response): void {
  const { secure, sameSite } = cookieSecureSameSite();
  res.clearCookie(smartcarOAuthBindCookieName(), {
    path: "/",
    secure,
    sameSite,
  });
}
