import crypto from "crypto";
import mongoose, { Types } from "mongoose";
import SmartcarOAuthBridgeStateModel from "../models/SmartcarOAuthBridgeStateModel";
import { parseTankUserIdFromOAuthState } from "../utils/smartcarCallbackUser";

const PREFIX = "ttb1_";

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

/**
 * Mongo-backed opaque `state` for Connect authorize URLs. Smartcar echoes `state` on your `REDIRECT_URI`
 * (<https://smartcar.com/docs/connect/handle-the-response>). Stored server-side so the redirect can work
 * without cookies. Partner to Smartcar redirect `user_id` (paired with Tank user via SmartcarAccount; see
 * <https://smartcar.com/docs/getting-started/how-to/api-authentication> for `sc-user-id` usage with app tokens).
 */
export function isSmartcarOAuthBridgeStateToken(raw: string | undefined): boolean {
  if (typeof raw !== "string") return false;
  const s = raw.trim();
  return new RegExp(`^${PREFIX}[a-fA-F0-9]{32}$`).test(s);
}

export async function issueSmartcarOauthBridgeState(
  tankUserId: string
): Promise<string | null> {
  if (process.env.SMARTCAR_OAUTH_SKIP_BRIDGE?.trim().toUpperCase() === "1") {
    return null;
  }
  if (mongoose.connection.readyState !== 1) return null;

  const clean = tankUserId.trim();
  const valid = parseTankUserIdFromOAuthState(clean);
  if (!valid) return null;

  const stateToken = `${PREFIX}${crypto.randomBytes(16).toString("hex")}`;
  const expiresAt = new Date(Date.now() + resolveBridgeTtlMs());

  try {
    await SmartcarOAuthBridgeStateModel.create({
      stateToken,
      userId: new Types.ObjectId(valid),
      expiresAt,
    });
    return stateToken;
  } catch (e) {
    console.error(
      "[Smartcar] issueSmartcarOauthBridgeState failed (fallback to JWT/state):",
      e instanceof Error ? e.message : e
    );
    return null;
  }
}

/** One-time consume: returns Tank user Mongo id hex or null (invalid/expired/already used). */
export async function consumeSmartcarOauthBridgeState(
  stateParam: string | undefined
): Promise<string | null> {
  if (!stateParam?.trim()) return null;
  let raw = stateParam.trim();
  try {
    raw = decodeURIComponent(raw).trim();
  } catch {
    /* keep raw */
  }
  if (!isSmartcarOAuthBridgeStateToken(raw)) return null;
  if (mongoose.connection.readyState !== 1) return null;

  try {
    const doc = await SmartcarOAuthBridgeStateModel.findOneAndDelete({
      stateToken: raw,
      expiresAt: { $gte: new Date() },
    })
      .select("userId")
      .lean();

    const id = doc?.userId ? String(doc.userId) : null;
    return parseTankUserIdFromOAuthState(id ?? undefined);
  } catch (e) {
    console.error(
      "[Smartcar] consumeSmartcarOauthBridgeState failed:",
      e instanceof Error ? e.message : e
    );
    return null;
  }
}
