import { Request, Response } from "express";
import smartcar from "smartcar";
import { Types } from "mongoose";
import {
  smartcarClient,
  SMARTCAR_CONFIGURED,
  SMARTCAR_EFFECTIVE_REDIRECT_URI,
  SMARTCAR_EFFECTIVE_WEBHOOK_URL,
} from "../config/smartcar";
import SmartcarAccountModel from "../models/SmartcarAccountModel";
import VehicleModel from "../models/vehicleModel";
import UserModel from "../models/UserModel";
import { CustomRequest } from "../interfaces/auth";
import {
  TANK_TRACK_APP_HOME_URL,
  TANK_TRACK_APP_HOME_DEEPLINK,
  TANK_TRACK_APP_DEEPLINK,
  API_PREFIX,
  PUBLIC_API_URL,
  SMARTCAR_SIMPLE_OAUTH_REDIRECT,
  SMARTCAR_OAUTH_SUCCESS_URL,
  SMARTCAR_OAUTH_FAILURE_URL,
  SMARTCAR_APP_HANDOFF_URL,
  SMARTCAR_ALLOW_DIRECT_DEEPLINK_REDIRECT,
} from "../config/environment";
import { absoluteOrRelativeRedirect } from "../utils/publicRedirect";
import {
  afterSmartcarVehicleSync,
  syncSmartcarVehicles,
  withSmartcarAccessToken,
} from "../services/smartcarService";
import { getCachedSmartcarIamApplicationAccessToken } from "../services/smartcarIamApplicationToken";
import {
  linkSmartcarWithAuthorizationCode,
  exchangeSmartcarCodeOnly,
} from "../services/smartcarOAuthExchange";
import {
  consumeSmartcarOauthBridgeState,
  issueSmartcarOauthBridgeState,
  isSmartcarOAuthBridgeStateToken,
} from "../services/smartcarOAuthBridgeState";
import { resolveTankTrackUserIdFromStoredSmartcarAccount } from "../services/smartcarConnectRedirectIdentity";
import {
  hashSmartcarWebhookChallenge,
  verifySmartcarPayloadSignature,
} from "../utils/smartcarWebhookChallenge";
import { syncIsCarsRegisteredFromDelivery } from "../services/smartcarWebhookUserSync";
import {
  getDefaultWebhookIdFromEnv,
  subscribeVehiclesToDefaultWebhook,
  createWebhookSubscriptionViaManagementApi,
  getApplicationManagementTokenFromEnv,
} from "../services/smartcarWebhookSubscription";
import {
  parseTankUserIdFromOAuthState,
  parseTankUserIdFromSmartcarOAuthSession,
  clearSmartcarOAuthSessionFields,
  signSmartcarOauthStateTankUserId,
  resolveTankTrackUserIdFromSmartcarRedirectState,
  trySmartcarSimulatorTankTrackUserFromEnv,
} from "../utils/smartcarCallbackUser";
import {
  issueSmartcarOAuthBindCookie,
  readTankTrackUserIdFromOAuthBindCookie,
  clearSmartcarOAuthBindCookie,
} from "../utils/smartcarOAuthBindCookie";
import {
  smartcarDebugLog,
  smartcarFlowLog,
} from "../utils/smartcarOAuthLogging";

const TAG = "[Smartcar]";

function appendSearchParam(rawUrl: string, key: string, value: string): string {
  if (!rawUrl) return rawUrl;
  const sep = rawUrl.includes("?") ? "&" : "?";
  return `${rawUrl}${sep}${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
}

/** Smartcar Connect query values (length-capped). @see https://smartcar.com/docs/connect/handle-the-response */
function trimConnectQueryParam(value: unknown, maxLen: number): string {
  if (typeof value !== "string") return "";
  return value.trim().slice(0, maxLen);
}

type SmartcarVehicleCompatHint = {
  vin?: string;
  make?: string;
  model?: string;
  year?: string;
};

function vehicleCompatFromConnectRedirectQuery(
  req: Request,
): SmartcarVehicleCompatHint | undefined {
  const vin = trimConnectQueryParam(req.query.vin, 64);
  const make = trimConnectQueryParam(req.query.make, 80);
  const model = trimConnectQueryParam(req.query.model, 80);
  const year = trimConnectQueryParam(req.query.year, 8);
  if (!vin && !make && !model && !year) return undefined;
  return { vin, make, model, year };
}

function appendVehicleCompatQueryParams(
  url: string,
  vc?: SmartcarVehicleCompatHint,
): string {
  if (!vc) return url;
  let u = url;
  if (vc.vin) u = appendSearchParam(u, "sc_vehicle_vin", vc.vin);
  if (vc.make) u = appendSearchParam(u, "sc_vehicle_make", vc.make);
  if (vc.model) u = appendSearchParam(u, "sc_vehicle_model", vc.model);
  if (vc.year) u = appendSearchParam(u, "sc_vehicle_year", vc.year);
  return u;
}

function isHttpsOrHttpUrl(raw: string): boolean {
  const t = raw.trim().toLowerCase();
  return t.startsWith("https://") || t.startsWith("http://");
}

/** Prefer HTTPS SPA/PWA so the **frontend** can open native app (don't `302` to tanktrack:// unless explicitly opted in — browsers often reject it). */
function resolveHttpsOAuthSuccessLanding(): string {
  return (
    SMARTCAR_OAUTH_SUCCESS_URL.trim() ||
    SMARTCAR_APP_HANDOFF_URL.trim() ||
    TANK_TRACK_APP_HOME_URL.trim()
  );
}

function resolveHttpsOAuthFailureLanding(): string {
  return (
    SMARTCAR_OAUTH_FAILURE_URL.trim() ||
    SMARTCAR_APP_HANDOFF_URL.trim() ||
    SMARTCAR_OAUTH_SUCCESS_URL.trim() ||
    TANK_TRACK_APP_HOME_URL.trim()
  );
}

/** Custom schemes or absolute URLs — OAuth completion redirect. */
function redirectBrowserAbsoluteOrConfigured(res: Response, location: string) {
  if (
    location.startsWith("http://") ||
    location.startsWith("https://") ||
    /^\w+:\/\//.test(location)
  ) {
    return res.redirect(302, location);
  }
  return res.redirect(
    302,
    absoluteOrRelativeRedirect(
      location.startsWith("/") ? location : `/${location}`,
    ),
  );
}

function attachNativeDeeplinkForSpaHttps(
  targetUrl: string,
  chooseBaseHttps: boolean,
): string {
  if (!chooseBaseHttps) return targetUrl;
  let u = targetUrl;
  const deeplink = TANK_TRACK_APP_DEEPLINK.trim();
  const homeDeeplink = TANK_TRACK_APP_HOME_DEEPLINK.trim();
  if (deeplink) u = appendSearchParam(u, "native_deeplink", deeplink);
  if (homeDeeplink) u = appendSearchParam(u, "home_deeplink", homeDeeplink);
  return u;
}

/** HTTPS SPA URL with smartcar + native deeplink query params (safe from api.* pages). */
function buildSmartcarSpaHandoffUrl(opts: {
  ok: boolean;
  smartcarUserId?: string;
  vehicles?: number;
}): string | null {
  const httpsBase = resolveHttpsOAuthSuccessLanding();
  if (!httpsBase || !isHttpsOrHttpUrl(httpsBase)) return null;

  let u = appendSearchParam(httpsBase, "smartcar", opts.ok ? "ok" : "error");
  u = attachNativeDeeplinkForSpaHttps(u, true);
  const uid = opts.smartcarUserId?.trim();
  if (uid) u = appendSearchParam(u, "smartcar_uid", uid);
  if (opts.vehicles != null && opts.vehicles > 0) {
    u = appendSearchParam(u, "vehicles", String(opts.vehicles));
  }
  return u;
}

function resolveSmartcarPostLoginBrowserUrl(smartcarUserId: string): string {
  const httpsBase = resolveHttpsOAuthSuccessLanding();
  let base =
    httpsBase ||
    (SMARTCAR_ALLOW_DIRECT_DEEPLINK_REDIRECT
      ? TANK_TRACK_APP_DEEPLINK.trim()
      : "");

  if (!base) {
    if (
      TANK_TRACK_APP_DEEPLINK.trim() &&
      !SMARTCAR_ALLOW_DIRECT_DEEPLINK_REDIRECT
    ) {
      console.warn(
        `${TAG} Only TANK_TRACK_APP_DEEPLINK is set; OAuth will not 302 directly to native scheme (blocked in many browsers). Add SMARTCAR_APP_HANDOFF_URL or TANK_TRACK_APP_HOME_URL (HTTPS) and open the native app there; set SMARTCAR_ALLOW_DIRECT_DEEPLINK_REDIRECT=true to force redirect to the scheme anyway.`,
      );
    }
    return absoluteOrRelativeRedirect(`${API_PREFIX}/smartcar/oauth-done?ok=1`);
  }

  let u = appendSearchParam(base, "smartcar", "ok");
  if (httpsBase && isHttpsOrHttpUrl(httpsBase)) {
    u = attachNativeDeeplinkForSpaHttps(u, true);
  }

  if (smartcarUserId.trim().length > 0) {
    u = appendSearchParam(u, "smartcar_uid", smartcarUserId.trim());
  }

  return u;
}

function resolveSmartcarPostLoginFailureUrl(opts: {
  reasonCode: string;
  jwtRecoverHint?: boolean;
  recoveryCode?: string;
  /** Smartcar Connect `?error=` (e.g. access_denied). @see https://smartcar.com/docs/connect/handle-the-response */
  connectErrorCode?: string;
  vehicleIncompatible?: SmartcarVehicleCompatHint;
}): string {
  const httpsBase = resolveHttpsOAuthFailureLanding();
  let base =
    httpsBase ||
    (SMARTCAR_ALLOW_DIRECT_DEEPLINK_REDIRECT
      ? TANK_TRACK_APP_DEEPLINK.trim()
      : "");

  let u: string;
  if (!base) {
    if (
      TANK_TRACK_APP_DEEPLINK.trim() &&
      !SMARTCAR_ALLOW_DIRECT_DEEPLINK_REDIRECT
    ) {
      console.warn(
        `${TAG} OAuth failure redirect: only TANK_TRACK_APP_DEEPLINK is set; use HTTPS SMARTCAR_APP_HANDOFF_URL / HOME (frontend opens native app) or SMARTCAR_ALLOW_DIRECT_DEEPLINK_REDIRECT=true.`,
      );
    }
    u = absoluteOrRelativeRedirect(`${API_PREFIX}/smartcar/oauth-done?ok=0`);
  } else {
    u = base;
    if (httpsBase && isHttpsOrHttpUrl(httpsBase)) {
      u = attachNativeDeeplinkForSpaHttps(u, true);
    }
  }

  const rc = opts.reasonCode.slice(0, 120);
  u = appendSearchParam(u, "smartcar", "error");
  u = appendSearchParam(u, "reason", rc);
  const scErr = opts.connectErrorCode?.trim();
  if (scErr) {
    u = appendSearchParam(u, "sc_error", scErr.slice(0, 160));
  }
  u = appendVehicleCompatQueryParams(u, opts.vehicleIncompatible);
  if (opts.jwtRecoverHint && opts.recoveryCode?.trim()) {
    u = appendSearchParam(u, "oauth_code", opts.recoveryCode.trim());
    u = appendSearchParam(u, "recover", "jwt");
  }
  return u;
}

/** Prefer `smartcar.hashChallenge` when the SDK exposes it; else HMAC-SHA256(secret, challenge) lowercase hex per Smartcar VERIFY docs. */
function computeSmartcarVerifyHmac(secret: string, challenge: string): string {
  const sdk = smartcar as unknown as {
    hashChallenge?: (
      applicationManagementToken: string,
      challengeString: string,
    ) => string;
  };
  if (typeof sdk.hashChallenge === "function") {
    try {
      return sdk.hashChallenge(secret.trim(), challenge);
    } catch {
      /* fallback */
    }
  }
  return hashSmartcarWebhookChallenge(secret, challenge);
}

const SMARTCAR_SCOPES = [
  "read_vehicle_info",
  "read_odometer",
  "read_fuel",
  "read_location",
  "read_charge_records",
  "read_climate",
  "read_user_profile",
  "control_navigation",
  "control_pin",
];

function smartcarCompletionPageQuery(
  props: Record<string, string | undefined | null>,
): string {
  const usp = new URLSearchParams();
  for (const [k, v] of Object.entries(props)) {
    if (v != null && String(v).length > 0) usp.append(k, String(v));
  }
  if (TANK_TRACK_APP_HOME_URL.length > 0) {
    usp.set("appHome", TANK_TRACK_APP_HOME_URL);
  }
  if (SMARTCAR_APP_HANDOFF_URL.length > 0) {
    usp.set("appHandoff", SMARTCAR_APP_HANDOFF_URL);
  }
  if (TANK_TRACK_APP_DEEPLINK.length > 0) {
    usp.set("deeplink", TANK_TRACK_APP_DEEPLINK);
  }
  if (TANK_TRACK_APP_HOME_DEEPLINK.length > 0) {
    usp.set("homeDeeplink", TANK_TRACK_APP_HOME_DEEPLINK);
  }
  return usp.toString();
}

function smartcarRecoverCompleteConnectAbsoluteUrl(): string {
  const path = `${API_PREFIX}/smartcar/complete-connect`;
  if (PUBLIC_API_URL.length > 0) return `${PUBLIC_API_URL}${path}`;
  return path;
}

function redirectSmartcarStatusSuccess(
  res: Response,
  opts?: { vehicles?: number; smartcarUserId?: string },
) {
  const handoff = buildSmartcarSpaHandoffUrl({
    ok: true,
    smartcarUserId: opts?.smartcarUserId,
    vehicles: opts?.vehicles,
  });
  if (handoff) {
    return res.redirect(302, handoff);
  }

  const qs = smartcarCompletionPageQuery({
    status: "success",
    ...(opts?.vehicles ? { vehicles: String(opts.vehicles) } : {}),
    ...(opts?.smartcarUserId ? { userId: opts.smartcarUserId } : {}),
  });
  return res.redirect(
    absoluteOrRelativeRedirect(`/assets/smartcar-status.html?${qs}`),
  );
}

function redirectSmartcarStatusCodeHandoff(
  res: Response,
  code: string,
  smartcarUserId?: string,
) {
  const qs = smartcarCompletionPageQuery({
    status: "error",
    detail:
      "Your vehicle authorization is ready. Open the Tank Track app to finish connecting.",
    ...(code ? { code } : {}),
    ...(smartcarUserId ? { user_id: smartcarUserId } : {}),
  });
  return res.redirect(
    absoluteOrRelativeRedirect(`/assets/smartcar-status.html?${qs}`),
  );
}

function redirectSmartcarStatusError(
  res: Response,
  detail: string,
  opts?: {
    jwtRecoverHint?: boolean;
    recoveryOauthCode?: string;
    connectErrorCode?: string;
    vehicleIncompatible?: SmartcarVehicleCompatHint;
  },
) {
  const safe = detail.slice(0, 450);
  const oauthCode = opts?.recoveryOauthCode?.trim();
  const scErr = opts?.connectErrorCode?.trim().slice(0, 160);
  const vc = opts?.vehicleIncompatible;
  const qs = smartcarCompletionPageQuery({
    status: "error",
    detail: safe,
    ...(opts?.jwtRecoverHint
      ? {
          jwtRecover: "1",
          recoveryApi: smartcarRecoverCompleteConnectAbsoluteUrl(),
          ...(oauthCode && oauthCode.length > 0
            ? { recoveryOauthCode: oauthCode }
            : {}),
        }
      : {}),
    ...(scErr ? { sc_error: scErr } : {}),
    ...(vc?.vin ? { sc_vehicle_vin: vc.vin } : {}),
    ...(vc?.make ? { sc_vehicle_make: vc.make } : {}),
    ...(vc?.model ? { sc_vehicle_model: vc.model } : {}),
    ...(vc?.year ? { sc_vehicle_year: vc.year } : {}),
  });
  return res.redirect(
    absoluteOrRelativeRedirect(`/assets/smartcar-status.html?${qs}`),
  );
}

/** Session bridge: /redirect exchanges the code → sets this → GET /callback shows the outcome. */
type SmartcarOauthCompletionFlash =
  | { variant: "success"; smartcarUserId: string }
  | {
      variant: "error";
      detail: string;
      jwtRecoverHint?: boolean;
      /** Included when jwtRecoverHint — one-time Smartcar `code` for POST /complete-connect or app deep link. */
      recoveryOauthCode?: string;
      /** Smartcar Connect `error` query param when the user denies or Connect fails. */
      connectErrorCode?: string;
      /** Optional vehicle hints when Smartcar reports an incompatible vehicle. */
      vehicleIncompatible?: SmartcarVehicleCompatHint;
    };

const SMARTCAR_OAUTH_COMPLETION_SESSION_KEY = "smartcar_oauth_completion";

function isSmartcarOauthCompletionFlash(
  v: unknown,
): v is SmartcarOauthCompletionFlash {
  if (!v || typeof v !== "object") return false;
  const o = v as Record<string, unknown>;
  if (o.variant === "success")
    return (
      typeof o.smartcarUserId === "string" && o.smartcarUserId.trim().length > 0
    );
  if (o.variant === "error")
    return typeof o.detail === "string" && String(o.detail).trim().length > 0;
  return false;
}

/**
 * Persist Smartcar OAuth outcome: by default `302` straight to app/site (see SMARTCAR_SIMPLE_OAUTH_REDIRECT).
 */
function finishOAuthWithFlash(
  req: Request,
  res: Response,
  flash: SmartcarOauthCompletionFlash,
) {
  if (SMARTCAR_SIMPLE_OAUTH_REDIRECT) {
    if (flash.variant === "success") {
      return redirectBrowserAbsoluteOrConfigured(
        res,
        resolveSmartcarPostLoginBrowserUrl(flash.smartcarUserId),
      );
    }
    const reason =
      flash.jwtRecoverHint && flash.recoveryOauthCode
        ? "use_post_complete_connect_with_jwt_and_code"
        : "oauth_failed";
    return redirectBrowserAbsoluteOrConfigured(
      res,
      resolveSmartcarPostLoginFailureUrl({
        reasonCode: reason,
        jwtRecoverHint: flash.jwtRecoverHint,
        recoveryCode: flash.recoveryOauthCode,
        connectErrorCode: flash.connectErrorCode,
        vehicleIncompatible: flash.vehicleIncompatible,
      }),
    );
  }

  if (!req.session) {
    if (flash.variant === "success") {
      const qsOk = smartcarCompletionPageQuery({
        status: "success",
        userId: flash.smartcarUserId,
      });
      return res.redirect(
        absoluteOrRelativeRedirect(`/assets/smartcar-status.html?${qsOk}`),
      );
    }
    return redirectSmartcarStatusError(res, flash.detail, {
      jwtRecoverHint: flash.jwtRecoverHint,
      recoveryOauthCode: flash.recoveryOauthCode,
      connectErrorCode: flash.connectErrorCode,
      vehicleIncompatible: flash.vehicleIncompatible,
    });
  }
  const s = req.session as unknown as Record<string, unknown>;
  s[SMARTCAR_OAUTH_COMPLETION_SESSION_KEY] = flash;
  clearSmartcarOAuthSessionFields(
    req.session as unknown as Record<string, unknown>,
  );
  return res.redirect(
    302,
    absoluteOrRelativeRedirect(`${API_PREFIX}/smartcar/callback`),
  );
}

/**
 * GET /smartcar/login (authenticated)
 * Returns the Smartcar OAuth authorization URL. The mobile app should open this
 * in a system browser / WebView. State param = userId so the callback can link
 * the Smartcar account to the right Tank Track user.
 */
export const redirectToSmartcar = async (req: CustomRequest, res: Response) => {
  try {
    const userId = String(req.userId ?? "").trim();
    const email = req.email;

    if (!userId) {
      return res.status(401).json({
        error: "missing_user",
        message:
          "Smartcar link requires a Tank user id from your JWT (logged-in session).",
      });
    }

    let oauthState = signSmartcarOauthStateTankUserId(userId) ?? userId;
    const mongoBridge = await issueSmartcarOauthBridgeState(userId);
    if (mongoBridge) oauthState = mongoBridge;

    const link = smartcarClient.getAuthUrl(SMARTCAR_SCOPES, {
      state: oauthState,
      /** Dashboard analytics aggregate; OAuth `state` is what Smartcar echoes for callback user id — see Smartcar AuthClient.getAuthUrl docs. */
      user: userId,
    });

    try {
      const auth = new URL(link);
      const echoed = auth.searchParams.get("state");
      if (!echoed || echoed.length === 0) {
        console.error(
          `${TAG} getAuthUrl produced no state= in authorize URL (Smartcar SDK omits state when options.state is falsy). userId length=${userId.length}`,
        );
        return res.status(500).json({
          error: "oauth_state_not_generated",
          message:
            "Cannot build Smartcar authorization URL with state. Ensure JWT id is non-empty.",
        });
      }
    } catch {
      /* non-URL link — skip parse */
    }

    if (req.session) {
      const s = req.session as unknown as Record<string, unknown>;
      s.smartcar_oauth_user_id = userId;
      s.smartcar_oauth_started_at = Date.now();
    }

    /** HttpOnly JWT cookie — survives some WebViews when Mongo `state` echo is stripped; JWT_SECRET required. */
    const stampOAuthLoginArtifacts = (): void => {
      issueSmartcarOAuthBindCookie(res, userId);
    };

    const redirectUrlExampleOnSuccess =
      resolveSmartcarPostLoginBrowserUrl("SMARTCAR_USER_UUID");

    const sendLoginJson = (): void =>
      void res.json({
        message: "Smartcar OAuth",
        data: {
          link,
          userId,
          email,
          oauthRedirectUri: SMARTCAR_EFFECTIVE_REDIRECT_URI,
          completeConnectEndpoint: `${API_PREFIX}/smartcar/complete-connect`,
        },
      });

    if (req.session) {
      return void req.session.save((saveErr) => {
        if (saveErr) {
          console.error(
            `${TAG} session.save after oauth bridge failed:`,
            saveErr,
          );
          return void res.status(500).json({
            error: "session_save_failed",
            message:
              "Could not persist OAuth session. Retry Connect from the app.",
          });
        }
        stampOAuthLoginArtifacts();
        smartcarFlowLog("oauth_login", "GET /smartcar/login OK", {
          userIdSnippet: `${userId.slice(0, 6)}…`,
          stateIsBridge: oauthState.startsWith("ttb1_"),
          sessionBacked: true,
        });
        sendLoginJson();
      });
    }

    stampOAuthLoginArtifacts();
    smartcarFlowLog("oauth_login", "GET /smartcar/login OK", {
      userIdSnippet: `${userId.slice(0, 6)}…`,
      stateIsBridge: oauthState.startsWith("ttb1_"),
      sessionBacked: false,
    });
    sendLoginJson();
  } catch (err) {
    console.error(`${TAG} Error building auth URL:`, err);
    res.status(500).json({
      error: "Failed to generate Smartcar authorization link",
      message: (err as Error).message || "Unknown error",
    });
  }
};

/**
 * GET `/smartcar/redirect` — OAuth return (`SMARTCAR_REDIRECT_URI`).
 *
 * Per [Smartcar Connect — Handle the Response](https://smartcar.com/docs/connect/handle-the-response):
 * - **Success**: `user_id` (Smartcar UUID, use as `sc-user-id` for IAM-based APIs), echoed `state`, and OAuth `code` (exchanged server-side via SDK).
 * - **Error**: `error`, `error_description`, echoed `state`, and optionally `vin` / `make` / `model` / `year` for incompatible vehicles.
 */
export const smartcarOAuthRedirect = async (req: Request, res: Response) => {
  try {
    smartcarDebugLog("oauth_redirect", "GET /smartcar/redirect entry", {
      queryKeys: Object.keys(req.query ?? {}),
      hasSession: Boolean(req.session),
    });

    if (!SMARTCAR_CONFIGURED || !SMARTCAR_EFFECTIVE_REDIRECT_URI) {
      console.error(
        `${TAG} /redirect hit but SMARTCAR_* env or PUBLIC_API_URL is missing (no redirect_uri).`,
      );
      return finishOAuthWithFlash(req, res, {
        variant: "error",
        detail:
          "Smartcar OAuth is not configured on this server. Set SMARTCAR_CLIENT_ID and SMARTCAR_REDIRECT_URI (or PUBLIC_API_URL + API_PREFIX).",
      });
    }

    const oauthDenied =
      typeof req.query.error === "string" ? req.query.error.trim() : "";
    const oauthDesc =
      typeof req.query.error_description === "string"
        ? req.query.error_description.trim()
        : "";
    if (oauthDenied) {
      const vehicleIncompatible = vehicleCompatFromConnectRedirectQuery(req);
      let detail =
        oauthDesc || oauthDenied || "Smartcar authorization was not completed.";
      if (vehicleIncompatible) {
        const parts = [
          vehicleIncompatible.year,
          vehicleIncompatible.make,
          vehicleIncompatible.model,
        ].filter((p): p is string => typeof p === "string" && p.length > 0);
        const summary = parts.join(" ").trim();
        const vin = vehicleIncompatible.vin;
        if (summary && vin) {
          detail = `${detail} (${summary}; VIN ${vin})`;
        } else if (summary) {
          detail = `${detail} (${summary})`;
        } else if (vin) {
          detail = `${detail} (VIN ${vin})`;
        }
      }

      console.warn(
        `${TAG} Connect redirect error:`,
        oauthDenied,
        oauthDesc || "",
        vehicleIncompatible || "",
      );

      return finishOAuthWithFlash(req, res, {
        variant: "error",
        detail,
        connectErrorCode: oauthDenied,
        ...(vehicleIncompatible ? { vehicleIncompatible } : {}),
      });
    }

    const code =
      typeof req.query.code === "string" ? req.query.code.trim() : "";
    /** Smartcar Connect echoes this UUID in the redirect; it identifies the Smartcar user, not our DB user. */
    const smartcarUserIdQuery =
      typeof req.query.user_id === "string" ? req.query.user_id.trim() : "";

    const stateParam =
      typeof req.query.state === "string" ? req.query.state : undefined;
    const tankIdFromState =
      resolveTankTrackUserIdFromSmartcarRedirectState(stateParam);
    const tankIdFromMongoBridge =
      !tankIdFromState && stateParam
        ? await consumeSmartcarOauthBridgeState(stateParam)
        : null;
    const tankIdFromStoredSmartcarUser =
      !tankIdFromState && !tankIdFromMongoBridge && smartcarUserIdQuery
        ? await resolveTankTrackUserIdFromStoredSmartcarAccount(
            smartcarUserIdQuery,
          )
        : null;
    const sessionData = req.session as unknown as Record<string, unknown>;
    let tankTrackUserId =
      tankIdFromState ||
      tankIdFromMongoBridge ||
      tankIdFromStoredSmartcarUser ||
      parseTankUserIdFromSmartcarOAuthSession(sessionData);

    let tankIdFromBindCookie: string | null = null;
    if (!tankTrackUserId) {
      tankIdFromBindCookie = readTankTrackUserIdFromOAuthBindCookie(req);
      if (tankIdFromBindCookie) {
        tankTrackUserId = tankIdFromBindCookie;
        smartcarFlowLog("redirect", "tank_user_from_bind_cookie", {
          note: "HttpOnly cookie set at GET /smartcar/login (requires JWT_SECRET)",
        });
        console.info(
          `${TAG} Tank user resolved via OAuth bind cookie (fallback when echoed state/session alone were insufficient on this hop).`,
        );
      }
    }

    const tankIdFromSimulator = !tankTrackUserId
      ? trySmartcarSimulatorTankTrackUserFromEnv()
      : null;
    if (tankIdFromSimulator) {
      tankTrackUserId = tankIdFromSimulator;
      const live = process.env.SMARTCAR_MODE?.trim().toLowerCase() === "live";
      console.warn(
        `${TAG} SMARTCAR_SIMULATOR_TANK_TRACK_USER_ID used (${tankIdFromSimulator.slice(0, 6)}…) — missing ?state= / session.${live ? " SMARTCAR_SIMULATOR_BRIDGE is set (staging hack); unset for production." : ""}`,
      );
    }

    if (tankIdFromMongoBridge) {
      console.info(
        `${TAG} OAuth user resolved from server-backed Mongo state (no JWT/session required for this hop).`,
      );
    }

    if (tankIdFromStoredSmartcarUser) {
      console.info(
        `${TAG} OAuth Tank user resolved from Smartcar Connect redirect \`user_id\` ↔ SmartcarAccount (store alongside internal user per Smartcar “Handle the Response”).`,
      );
    }

    if (
      !tankIdFromState &&
      !tankIdFromMongoBridge &&
      !tankIdFromStoredSmartcarUser &&
      !tankIdFromBindCookie &&
      tankTrackUserId
    ) {
      console.warn(
        `${TAG} OAuth redirect missing resolvable ?state= — recovered Tank user from session cookie (same browser that called /smartcar/login).`,
      );
    }

    const hadStateQuery =
      typeof req.query.state === "string" && req.query.state.trim().length > 0;

    smartcarFlowLog("redirect", "GET /smartcar/redirect", {
      queryKeys: Object.keys(req.query),
      hasOauthCode: code.length > 0,
      smartcarUserIdEcho: smartcarUserIdQuery
        ? `${smartcarUserIdQuery.slice(0, 8)}…`
        : "",
      hadStateQuery,
      resolvedTankUser: Boolean(tankTrackUserId),
      viaMongoBridge: Boolean(tankIdFromMongoBridge),
      viaStateParam: Boolean(tankIdFromState),
      viaPriorSmartcarAccount: Boolean(tankIdFromStoredSmartcarUser),
      viaBindCookie: Boolean(tankIdFromBindCookie),
      viaSessionFields: Boolean(
        parseTankUserIdFromSmartcarOAuthSession(sessionData),
      ),
    });

    if (code && !tankIdFromState && !tankTrackUserId && !hadStateQuery) {
      const hasCookie =
        typeof req.headers.cookie === "string" && req.headers.cookie.length > 0;
      const oauthFields =
        typeof sessionData?.smartcar_oauth_user_id === "string" ||
        typeof sessionData?.smartcar_oauth_started_at === "number";
      const uuidHint = smartcarUserIdQuery
        ? ` Smartcar redirect includes user_id=${smartcarUserIdQuery.slice(
            0,
            8,
          )}… — that maps to Tank only after a prior SmartcarAccount row exists; first Connect still needs echoed \`state\` from GET /smartcar/login (or JWT POST …/complete-connect).`
        : "";
      console.warn(
        `${TAG} Redirect has code but no echoed \`state\` query and no Tank user yet.${uuidHint} JWT recovery: POST ${API_PREFIX}/smartcar/complete-connect with same Bearer as /login + JSON {"code":"<from redirect>"}. URL keys: ${Object.keys(req.query).join(", ") || "(none)"}. Cookie: ${hasCookie}. Session bridge fields: ${oauthFields}. Hint: Mongo session store for PM2; state can be short Mongo bridge (ttb1_…) or signed JWT.`,
      );
    }

    if (!code) {
      return finishOAuthWithFlash(req, res, {
        variant: "error",
        detail:
          "Missing authorization code. Open the Tank Track app and tap Connect Smartcar again.",
      });
    }

    if (!tankTrackUserId) {
      console.log(
        "[Smartcar][DEBUG] REDIRECT — no tankTrackUserId resolved, code=%s user_id=%s",
        code ? code.slice(0, 12) + "…" : "(none)",
        smartcarUserIdQuery ? smartcarUserIdQuery.slice(0, 8) + "…" : "(none)",
      );
      if (code) {
        console.log(
          "[Smartcar][DEBUG] REDIRECT — exchanging code WITHOUT user link (mobile WebView path)…",
        );

        const codeExchange = await exchangeSmartcarCodeOnly(
          code,
          smartcarUserIdQuery || undefined,
        );
        if (!codeExchange.ok) {
          console.error(
            "[Smartcar][DEBUG] REDIRECT — code exchange FAILED: %s invalidClient=%s",
            codeExchange.detail,
            codeExchange.invalidClient ?? false,
          );
          const jwtRecoverHint =
            code.length > 0 &&
            (codeExchange.invalidGrant === true ||
              codeExchange.invalidClient === true);
          return finishOAuthWithFlash(req, res, {
            variant: "error",
            detail: codeExchange.detail,
            ...(jwtRecoverHint
              ? { jwtRecoverHint: true as const, recoveryOauthCode: code }
              : {}),
            ...(codeExchange.invalidClient
              ? { connectErrorCode: "invalid_client" }
              : {}),
          });
        }

        console.log(
          "[Smartcar][DEBUG] REDIRECT — code exchange OK, saving SmartcarAccount (no userId)…",
        );
        await SmartcarAccountModel.findOneAndUpdate(
          { smartcarUserId: codeExchange.smartcarUserId },
          {
            smartcarUserId: codeExchange.smartcarUserId,
            accessToken: codeExchange.accessToken,
            refreshToken: codeExchange.refreshToken,
            tokenExpiresAt: codeExchange.tokenExpiresAt,
            status: true,
          },
          { upsert: true, new: true },
        );

        console.log(
          "[Smartcar][DEBUG] REDIRECT — SmartcarAccount saved ✓ smartcarUserId=%s → redirecting to success HTML",
          codeExchange.smartcarUserId.slice(0, 8) + "…",
        );

        return redirectSmartcarStatusSuccess(res, {
          vehicles: 0,
          smartcarUserId: codeExchange.smartcarUserId,
        });
      }

      console.error(
        "[Smartcar][DEBUG] REDIRECT — no code AND no tankTrackUserId → showing error",
      );
      return finishOAuthWithFlash(req, res, {
        variant: "error",
        detail:
          "Missing authorization code. Open the Tank Track app and tap Connect Smartcar again.",
      });
    }

    console.log(
      "[Smartcar][DEBUG] REDIRECT — tankTrackUserId=%s resolved, exchanging code with user link…",
      tankTrackUserId.slice(0, 8) + "…",
    );
    const exchange = await linkSmartcarWithAuthorizationCode({
      tankTrackUserId,
      code,
      smartcarUserIdQuery: smartcarUserIdQuery || undefined,
    });
    if (!exchange.ok) {
      const jwtRecoverHint =
        code.length > 0 &&
        (exchange.invalidGrant === true || exchange.invalidClient === true);

      smartcarDebugLog("oauth_redirect", "code exchange failed", {
        invalidGrant: exchange.invalidGrant === true,
        invalidClient: exchange.invalidClient === true,
        jwtRecoverHint,
      });
      return finishOAuthWithFlash(req, res, {
        variant: "error",
        detail: exchange.detail,
        ...(jwtRecoverHint
          ? { jwtRecoverHint: true as const, recoveryOauthCode: code }
          : {}),
      });
    }
    clearSmartcarOAuthBindCookie(res);
    smartcarFlowLog("redirect", "code_exchange_ok", {
      smartcarUserId: `${exchange.smartcarUserId.slice(0, 8)}…`,
      vehicleTokensPersisted: true,
    });
    smartcarDebugLog("oauth_redirect", "code exchange OK", {
      vehiclesRegistered: exchange.vehicles.length,
      vehicleSyncCompleted: exchange.vehicleSyncCompleted,
    });

    return redirectSmartcarStatusSuccess(res, {
      vehicles: exchange.vehicles.length,
      smartcarUserId: exchange.smartcarUserId,
    });
  } catch (err: unknown) {
    const msg = err instanceof Error ? err.message : String(err);
    console.error(`${TAG} OAuth redirect unexpected error:`, msg);
    smartcarDebugLog("oauth_redirect", "unexpected error", { message: msg });
    return finishOAuthWithFlash(req, res, { variant: "error", detail: msg });
  }
};

/**
 * POST /smartcar/complete-connect (authenticated)
 *
 * Accepts EITHER:
 *   `{ "code": "<oauth code>" }` — exchange code and link to current user
 *   `{ "smartcarUserId": "<uuid>" }` — claim an existing unlinked SmartcarAccount
 *
 * After linking, syncs vehicles and sets `User.isCarsRegistered = true`.
 */
export const completeSmartcarConnectWithJwt = async (
  req: CustomRequest,
  res: Response,
) => {
  const userId = req.userId;
  smartcarDebugLog(
    "complete_connect",
    "POST /smartcar/complete-connect entry",
    {
      hasUserId: Boolean(userId),
    },
  );
  if (!userId) {
    return res.status(401).json({
      message: "UnAuthorized Request",
      error: "user_missing",
    });
  }

  const body = req.body as {
    code?: unknown;
    smartcarUserId?: unknown;
  };
  const code = typeof body?.code === "string" ? body.code.trim() : "";
  const smartcarUserIdBody =
    typeof body?.smartcarUserId === "string" ? body.smartcarUserId.trim() : "";

  console.log(
    "[Smartcar][DEBUG] COMPLETE-CONNECT — userId=%s code=%s smartcarUserId=%s",
    userId.slice(0, 8) + "…",
    code ? code.slice(0, 12) + "…" : "(none)",
    smartcarUserIdBody ? smartcarUserIdBody.slice(0, 8) + "…" : "(none)",
  );

  if (!code && !smartcarUserIdBody) {
    console.error(
      "[Smartcar][DEBUG] COMPLETE-CONNECT — MISSING both code and smartcarUserId",
    );
    return res.status(400).json({
      error: "code_or_smartcar_user_id_required",
      message:
        'Provide JSON body: { "code": "<oauth code>" } or { "smartcarUserId": "<from redirect>" }.',
    });
  }

  if (smartcarUserIdBody) {
    console.log(
      "[Smartcar][DEBUG] COMPLETE-CONNECT — claiming existing SmartcarAccount by smartcarUserId…",
    );
    const existing = await SmartcarAccountModel.findOne({
      smartcarUserId: smartcarUserIdBody,
      status: true,
    });
    if (!existing) {
      console.error(
        "[Smartcar][DEBUG] COMPLETE-CONNECT — SmartcarAccount NOT FOUND for smartcarUserId=%s",
        smartcarUserIdBody.slice(0, 8) + "…",
      );
      return res.status(404).json({
        error: "smartcar_account_not_found",
        message:
          "No Smartcar account found for this smartcarUserId. Try Connect again.",
      });
    }

    console.log(
      "[Smartcar][DEBUG] COMPLETE-CONNECT — found SmartcarAccount, linking userId=%s…",
      userId.slice(0, 8) + "…",
    );

    let activeAccessToken = existing.accessToken;
    const isIamToken = !existing.refreshToken;
    const tokenExpired =
      existing.tokenExpiresAt && new Date(existing.tokenExpiresAt) < new Date();

    if (isIamToken && tokenExpired) {
      console.log(
        "[Smartcar][DEBUG] COMPLETE-CONNECT — IAM token expired, getting fresh token…",
      );
      try {
        const freshIam = await getCachedSmartcarIamApplicationAccessToken({
          clientId: process.env.SMARTCAR_IAM_CLIENT_ID?.trim(),
          clientSecret: process.env.SMARTCAR_CLIENT_SECRET?.trim(),
        });
        activeAccessToken = freshIam.accessToken;
        await SmartcarAccountModel.updateOne(
          { _id: existing._id },
          {
            $set: {
              userId: new Types.ObjectId(userId),
              accessToken: freshIam.accessToken,
              tokenExpiresAt: new Date(freshIam.expiresAtMs),
            },
          },
        );
        console.log(
          "[Smartcar][DEBUG] COMPLETE-CONNECT — fresh IAM token obtained ✓ expires_in=%ds",
          freshIam.expiresIn,
        );
      } catch (iamErr) {
        console.error(
          "[Smartcar][DEBUG] COMPLETE-CONNECT — fresh IAM token FAILED:",
          iamErr,
        );
        await SmartcarAccountModel.updateOne(
          { _id: existing._id },
          { $set: { userId: new Types.ObjectId(userId) } },
        );
      }
    } else {
      await SmartcarAccountModel.updateOne(
        { _id: existing._id },
        { $set: { userId: new Types.ObjectId(userId) } },
      );
    }

    console.log(
      "[Smartcar][DEBUG] COMPLETE-CONNECT — SmartcarAccount linked ✓ syncing vehicles… tokenExpired=%s isIam=%s",
      tokenExpired,
      isIamToken,
    );

    let syncedVehicles: Array<Record<string, unknown>> = [];
    try {
      const iamScUserId = isIamToken ? smartcarUserIdBody : undefined;
      syncedVehicles = await syncSmartcarVehicles(
        userId,
        activeAccessToken,
        smartcarUserIdBody,
        iamScUserId,
      );
      console.log(
        "[Smartcar][DEBUG] COMPLETE-CONNECT — vehicles synced ✓ count=%d",
        syncedVehicles.length,
      );
    } catch (syncErr) {
      console.error(
        "[Smartcar][DEBUG] COMPLETE-CONNECT — vehicle sync FAILED (non-fatal):",
        syncErr,
      );
    }

    console.log(
      "[Smartcar][DEBUG] COMPLETE-CONNECT — setting isCarsRegistered=true…",
    );
    await afterSmartcarVehicleSync(
      userId,
      smartcarUserIdBody,
      syncedVehicles.length > 0,
    );

    clearSmartcarOAuthBindCookie(res);
    console.log(
      "[Smartcar][DEBUG] COMPLETE-CONNECT — COMPLETE ✓ linked=%s vehicles=%d isCarsRegistered=true",
      smartcarUserIdBody.slice(0, 8) + "…",
      syncedVehicles.length,
    );

    return res.status(200).json({
      linked: true,
      smartcarUserId: smartcarUserIdBody,
      vehicleSyncCompleted: syncedVehicles.length > 0,
      vehiclesRegistered: syncedVehicles.length,
      vehicles: syncedVehicles,
    });
  }

  console.log(
    "[Smartcar][DEBUG] COMPLETE-CONNECT — exchanging code with user link…",
  );
  const exchange = await linkSmartcarWithAuthorizationCode({
    tankTrackUserId: userId,
    code,
  });
  if (!exchange.ok) {
    smartcarDebugLog("complete_connect", "exchange failed", {
      invalidGrant: exchange.invalidGrant === true,
      invalidClient: exchange.invalidClient === true,
    });
    const status = exchange.invalidGrant || exchange.invalidClient ? 400 : 502;
    return res.status(status).json({
      error: exchange.invalidGrant
        ? "invalid_grant"
        : exchange.invalidClient
          ? "invalid_client"
          : "smartcar_error",
      message: exchange.detail,
    });
  }

  clearSmartcarOAuthBindCookie(res);
  smartcarFlowLog("oauth_login", "POST /smartcar/complete-connect OK", {
    smartcarUserId: `${exchange.smartcarUserId.slice(0, 8)}…`,
  });
  smartcarDebugLog("complete_connect", "exchange OK", {
    vehiclesRegistered: exchange.vehicles.length,
    vehicleSyncCompleted: exchange.vehicleSyncCompleted,
  });

  return res.status(200).json({
    linked: true,
    smartcarUserId: exchange.smartcarUserId,
    vehicleSyncCompleted: exchange.vehicleSyncCompleted,
    vehiclesRegistered: exchange.vehicles.length,
    vehicles: exchange.vehicles,
    redirectUrl: resolveSmartcarPostLoginBrowserUrl(exchange.smartcarUserId),
  });
};

/**
 * GET `/smartcar/callback` — **Completion**: reads the outcome staged by `/redirect`, then sends the user to
 * `smartcar-status.html` (clean URL — OAuth `code` is not forwarded).
 *
 * **Legacy:** Dashboard may still register `/callback`; if this request carries `?code=`, reuse `/redirect` exchange logic first.
 *
 * Dashboard **webhooks** use POST `/smartcar/webhook` — not this GET route.
 */
export const smartcarOAuthCallback = async (req: Request, res: Response) => {
  const code = typeof req.query.code === "string" ? req.query.code.trim() : "";
  smartcarFlowLog("callback", "GET /smartcar/callback", {
    hasCodeQuery: code.length > 0,
    usingSessionFlash: !code,
  });
  smartcarDebugLog("oauth_callback", "GET /smartcar/callback", {
    queryKeys: Object.keys(req.query ?? {}),
    hasCodeQuery: code.length > 0,
    hasStateQuery:
      typeof req.query.state === "string" && req.query.state.trim().length > 0,
    hasSession: Boolean(req.session),
    sessionHasFlash: Boolean(
      req.session &&
      (req.session as unknown as Record<string, unknown>)[
        SMARTCAR_OAUTH_COMPLETION_SESSION_KEY
      ],
    ),
  });
  if (code) {
    return smartcarOAuthRedirect(req, res);
  }

  if (req.session) {
    const s = req.session as unknown as Record<string, unknown>;
    const flash = s[SMARTCAR_OAUTH_COMPLETION_SESSION_KEY];
    if (isSmartcarOauthCompletionFlash(flash)) {
      delete s[SMARTCAR_OAUTH_COMPLETION_SESSION_KEY];
      if (flash.variant === "success") {
        if (SMARTCAR_SIMPLE_OAUTH_REDIRECT) {
          return redirectBrowserAbsoluteOrConfigured(
            res,
            resolveSmartcarPostLoginBrowserUrl(flash.smartcarUserId),
          );
        }
        const qsOk = smartcarCompletionPageQuery({
          status: "success",
          userId: flash.smartcarUserId,
        });
        return res.redirect(
          absoluteOrRelativeRedirect(`/assets/smartcar-status.html?${qsOk}`),
        );
      }
      if (SMARTCAR_SIMPLE_OAUTH_REDIRECT) {
        const reason =
          flash.jwtRecoverHint && flash.recoveryOauthCode
            ? "use_post_complete_connect_with_jwt_and_code"
            : "oauth_failed";
        return redirectBrowserAbsoluteOrConfigured(
          res,
          resolveSmartcarPostLoginFailureUrl({
            reasonCode: reason,
            jwtRecoverHint: flash.jwtRecoverHint,
            recoveryCode: flash.recoveryOauthCode,
            connectErrorCode: flash.connectErrorCode,
            vehicleIncompatible: flash.vehicleIncompatible,
          }),
        );
      }
      return redirectSmartcarStatusError(res, flash.detail, {
        jwtRecoverHint: flash.jwtRecoverHint,
        recoveryOauthCode: flash.recoveryOauthCode,
        connectErrorCode: flash.connectErrorCode,
        vehicleIncompatible: flash.vehicleIncompatible,
      });
    }
  }

  if (SMARTCAR_SIMPLE_OAUTH_REDIRECT) {
    return redirectBrowserAbsoluteOrConfigured(
      res,
      resolveSmartcarPostLoginFailureUrl({
        reasonCode: "oauth_session_missing",
      }),
    );
  }

  return redirectSmartcarStatusError(
    res,
    "No OAuth completion in session. The `code` is redeemed server-side only on `/smartcar/redirect`; `/callback` is the clean follow-up. Use `/smartcar/login` → open its `link` → let Smartcar `/redirect` run, then `/callback` opens automatically.",
  );
};

function escapeHtml(s: string): string {
  return s
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;");
}

/**
 * Minimal OAuth completion landing (`?ok=1` | `ok=0`) when no front-end URL env is configured.
 * JWT recovery (`?recover=jwt` & `oauth_code`): redirects to SPA handoff URLs when configured
 * (`SMARTCAR_OAUTH_FAILURE_URL` / HANDOFF / …); otherwise HTML/JSON recovery instructions.
 * Use `?stay=1` or `?redirect=0` to force staying on this page (debug).
 */
export const smartcarOAuthDoneMinimal = (req: Request, res: Response) => {
  const ok = req.query.ok !== "0" && req.query.ok !== "false";
  const uid =
    typeof req.query.smartcar_uid === "string" ? req.query.smartcar_uid : "";
  const wantsJson =
    req.query.format === "json" ||
    String(req.headers.accept ?? "").includes("application/json");
  /** Some stacks emit `recover-jwt` or `recover_jwt`; Smartcar redirects use standard `recover=jwt`. */
  const recoverJwt =
    (typeof req.query.recover === "string" && req.query.recover === "jwt") ||
    Object.prototype.hasOwnProperty.call(req.query, "recover-jwt") ||
    (typeof req.query.recover_jwt === "string" &&
      /^1|true|yes$/i.test(req.query.recover_jwt));
  const oauthCode =
    typeof req.query.oauth_code === "string" ? req.query.oauth_code.trim() : "";
  const reason =
    typeof req.query.reason === "string" ? req.query.reason.trim() : "";
  const stayOnMinimalPage =
    req.query.stay === "1" ||
    req.query.redirect === "0" ||
    req.query.redirect === "false";
  const handoffHttps = resolveHttpsOAuthFailureLanding();

  if (
    !ok &&
    recoverJwt &&
    oauthCode &&
    handoffHttps.length > 0 &&
    !stayOnMinimalPage &&
    !wantsJson
  ) {
    return redirectBrowserAbsoluteOrConfigured(
      res,
      resolveSmartcarPostLoginFailureUrl({
        reasonCode:
          reason.slice(0, 120).length > 0
            ? reason.slice(0, 120)
            : "use_post_complete_connect_with_jwt_and_code",
        jwtRecoverHint: true,
        recoveryCode: oauthCode,
      }),
    );
  }

  if (wantsJson) {
    const recoverReason =
      reason.slice(0, 120).length > 0
        ? reason.slice(0, 120)
        : "use_post_complete_connect_with_jwt_and_code";
    let recoveryAppDeeplink = "";
    const dlRaw = TANK_TRACK_APP_DEEPLINK.trim();
    if (dlRaw && recoverJwt && oauthCode) {
      recoveryAppDeeplink = appendSearchParam(dlRaw, "oauth_code", oauthCode);
      recoveryAppDeeplink = appendSearchParam(
        recoveryAppDeeplink,
        "recover",
        "jwt",
      );
    }
    return res.status(ok ? 200 : 400).json({
      ok,
      ...(uid ? { smartcarUserId: uid } : {}),
      ...(reason ? { reason } : {}),
      ...(recoverJwt ? { recover: "jwt" as const } : {}),
      ...(recoverJwt && oauthCode ? { oauthCode } : {}),
      ...(recoverJwt && oauthCode
        ? {
            recovery: `POST ${smartcarRecoverCompleteConnectAbsoluteUrl()} with Authorization: Bearer <same as GET /smartcar/login> and JSON body { "code": "<oauthCode>" }.`,
            smartcarConnectDocs:
              "https://smartcar.com/docs/connect/handle-the-response",
          }
        : {}),
      ...(recoverJwt && oauthCode && handoffHttps.length > 0
        ? {
            recoveryHandoff: resolveSmartcarPostLoginFailureUrl({
              reasonCode: recoverReason,
              jwtRecoverHint: true,
              recoveryCode: oauthCode,
            }),
          }
        : {}),
      ...(recoverJwt && oauthCode && recoveryAppDeeplink.length > 0
        ? { recoveryAppDeeplink }
        : {}),
    });
  }

  if (!ok && recoverJwt && oauthCode) {
    const recoverReason =
      reason.length > 0
        ? reason.slice(0, 120)
        : "use_post_complete_connect_with_jwt_and_code";
    const spaRecoveryHref =
      handoffHttps.length > 0
        ? resolveSmartcarPostLoginFailureUrl({
            reasonCode: recoverReason,
            jwtRecoverHint: true,
            recoveryCode: oauthCode,
          })
        : "";

    let deeplinkRecoveryHref = "";
    const deeplinkRaw = TANK_TRACK_APP_DEEPLINK.trim();
    if (deeplinkRaw) {
      deeplinkRecoveryHref = appendSearchParam(
        deeplinkRaw,
        "oauth_code",
        oauthCode,
      );
      deeplinkRecoveryHref = appendSearchParam(
        deeplinkRecoveryHref,
        "recover",
        "jwt",
      );
    }

    const api = escapeHtml(smartcarRecoverCompleteConnectAbsoluteUrl());
    const bodyJson = escapeHtml(JSON.stringify({ code: oauthCode }));

    const quickLinks =
      spaRecoveryHref || deeplinkRecoveryHref
        ? `
<p style="margin:1.25rem 0"><strong>Next steps (recommended)</strong></p>
<ul style="margin:0 0 1rem 1rem;padding:0;line-height:1.6;">
${
  spaRecoveryHref
    ? `<li><a href="${escapeHtml(spaRecoveryHref)}">Continue on Tank Track (web)</a> — loads your site with the same <code>oauth_code</code> + <code>recover=jwt</code> flags; finish there while logged in (SPA calls <code>POST …/complete-connect</code>).</li>`
    : ""
}${
            deeplinkRecoveryHref
              ? `<li><a href="${escapeHtml(deeplinkRecoveryHref)}">Open Tank Track (app)</a> — only if your mobile app intercepts these query params (<code>oauth_code</code>, <code>recover=jwt</code>) and completes linking.</li>`
              : ""
          }${
            !spaRecoveryHref
              ? `<li>This server has no HTTPS handoff URL (<code>TANK_TRACK_APP_HOME_URL</code>, <code>SMARTCAR_APP_HANDOFF_URL</code>, or <code>SMARTCAR_OAUTH_FAILURE_URL</code>). Set one on the API so users skip this page automatically.</li>`
              : ""
          }
</ul>
<p style="opacity:.92"><strong>Or</strong> from a logged-in REST client / mobile networking layer:</p>`
        : "";

    const html = `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width"/><title>Tank Track — finish Smartcar</title></head><body style="font-family:system-ui,sans-serif;max-width:44rem;margin:2rem;line-height:1.45;">
<h1>Finish linking Smartcar</h1>
<p>Smartcar redirects to our API with an authorization <code>code</code> (and <code>user_id</code> / echoed <code>state</code> — see <a href="https://smartcar.com/docs/connect/handle-the-response">Smartcar &ldquo;Handle the Response&rdquo;</a>). We could not map this browser to your Tank account because <code>state</code> from <code>GET …/smartcar/login</code> was missing or invalid and no matching session cookie was present.</p>
<p>Use the full <strong><code>link</code></strong> from <code>/smartcar/login</code> when starting Connect — it carries OAuth <code>state</code> Smartcar echoes back; do not paste a bare Dashboard authorize URL.</p>
${quickLinks}
<ol>
<li><code>POST</code> <strong>${api}</strong></li>
<li>Header <code>Authorization: Bearer &lt;your JWT&gt;</code> (same as when you requested <code>/smartcar/login</code>)</li>
<li>JSON body:<br/><pre style="background:#f4f4f4;padding:.75rem;overflow:auto"><code>${bodyJson}</code></pre></li>
</ol>
<p style="font-size:92%;opacity:.88">If your API sets <code>TANK_TRACK_APP_HOME_URL</code>, <code>SMARTCAR_APP_HANDOFF_URL</code>, or <code>SMARTCAR_OAUTH_FAILURE_URL</code>, you are usually redirected to that HTTPS site automatically with <code>recover=jwt</code> and <code>oauth_code</code> instead of staying here. Add <code>?stay=1</code> to force this instructional page.</p>
<p>One authorization <code>code</code> is single-use; if it expired, reconnect from the app&apos;s Smartcar button.</p></body></html>`;
    return res.status(400).type("html").send(html);
  }

  return res
    .status(ok ? 200 : 400)
    .type("text/plain")
    .send(ok ? "ok" : reason ? `error (${reason})` : "error");
};

/**
 * POST **`/smartcar/webhook`** or **`/smartcar/callback`** — Smartcar “Vehicle data callback” (VERIFY + deliveries).
 * Use the same URI you register as **Vehicle data callback URI** (`SMARTCAR_VEHICLE_WEBHOOK_URL` matches `POST /callback`).
 * OAuth browser completion uses **GET** `/smartcar/callback` (different verb — Express routes by method).
 * @see https://smartcar.com/docs/integrations/webhooks/callback-verification
 *
 * Do not register GET `/smartcar/redirect` for webhooks — that path is OAuth return only.
 */
/**
 * Extract known signals from a VEHICLE_STATE webhook and persist them on the
 * matching VehicleModel document. Unknown signals are silently ignored.
 * @see https://smartcar.com/docs/integrations/webhooks/receiving-webhooks
 */
async function persistVehicleStateSignals(
  body: Record<string, unknown>,
): Promise<void> {
  const data = body.data as Record<string, unknown> | undefined;
  if (!data) return;

  const vehicle = data.vehicle as Record<string, unknown> | undefined;
  const smartcarVehicleId =
    typeof vehicle?.id === "string" ? vehicle.id.trim() : "";
  if (!smartcarVehicleId) return;

  const signals = Array.isArray(data.signals)
    ? (data.signals as Array<Record<string, unknown>>)
    : [];
  if (signals.length === 0) return;

  const $set: Record<string, unknown> = { lastWebhookSyncAt: new Date() };

  const signalValueMap: Record<string, (val: unknown) => void> = {
    "odometer-traveleddistance": (val) => {
      const b = val as Record<string, unknown>;
      if (typeof b?.value === "number") {
        $set.odoMeter = String(Math.round(b.value));
      }
    },
    "internalcombustionengine-fuellevel": (val) => {
      const b = val as Record<string, unknown>;
      if (typeof b?.value === "number") $set.fuelLevel = b.value;
    },
    "tractionbattery-stateofcharge": (val) => {
      const b = val as Record<string, unknown>;
      if (typeof b?.value === "number") $set.batteryLevel = b.value;
    },
    "closure-islocked": (val) => {
      const b = val as Record<string, unknown>;
      if (typeof b?.value === "boolean") $set.isLocked = b.value;
    },
    "vehicleidentification-vin": (val) => {
      const b = val as Record<string, unknown>;
      if (typeof b?.value === "string" && b.value) $set.vin = b.value;
    },
    "vehicleidentification-exteriorcolor": (val) => {
      const b = val as Record<string, unknown>;
      if (typeof b?.value === "string") $set.exteriorColor = b.value;
    },
    "vehicleidentification-trim": (val) => {
      const b = val as Record<string, unknown>;
      if (typeof b?.value === "string") $set.trim = b.value;
    },
    "vehicleidentification-nickname": (val) => {
      const b = val as Record<string, unknown>;
      if (typeof b?.value === "string") $set.nickname = b.value;
    },
    "connectivitysoftware-currentfirmwareversion": (val) => {
      const b = val as Record<string, unknown>;
      if (typeof b?.value === "string") $set.firmwareVersion = b.value;
    },
  };

  for (const sig of signals) {
    const code = typeof sig.code === "string" ? sig.code.toLowerCase() : "";
    const status = sig.status as Record<string, unknown> | undefined;
    if (status?.value !== "SUCCESS") continue;
    const handler = signalValueMap[code];
    if (handler) handler(sig.body);
  }

  if (typeof vehicle?.powertrainType === "string" && vehicle.powertrainType) {
    $set.powertrainType = vehicle.powertrainType;
  }

  if (Object.keys($set).length <= 1) return;

  await VehicleModel.findOneAndUpdate(
    { smartcarVehicleId, isDeleted: false },
    { $set },
  );

  smartcarDebugLog("webhook", "VEHICLE_STATE signals persisted", {
    smartcarVehicleId,
    fieldsUpdated: Object.keys($set).filter((k) => k !== "lastWebhookSyncAt"),
  });
}

function smartcarWebhookBodyDebugShape(
  body: Record<string, unknown>,
): Record<string, unknown> {
  const data = body.data;
  const payload = body.payload;
  const meta = body.meta;
  const shape: Record<string, unknown> = {
    topKeys: Object.keys(body).slice(0, 40),
    eventType: typeof body.eventType === "string" ? body.eventType : undefined,
    eventName: typeof body.eventName === "string" ? body.eventName : undefined,
  };
  if (data && typeof data === "object" && !Array.isArray(data)) {
    shape.dataKeys = Object.keys(data as Record<string, unknown>).slice(0, 25);
  }
  if (payload && typeof payload === "object" && !Array.isArray(payload)) {
    shape.payloadKeys = Object.keys(payload as Record<string, unknown>).slice(
      0,
      25,
    );
  }
  if (meta && typeof meta === "object" && !Array.isArray(meta)) {
    shape.metaKeys = Object.keys(meta as Record<string, unknown>).slice(0, 20);
  }
  return shape;
}

export const smartcarWebhook = async (req: Request, res: Response) => {
  try {
    smartcarDebugLog("webhook", "POST handler entry", {
      path: req.path,
      originalUrl: (req.originalUrl || "").split("?")[0],
      ip: req.ip,
      contentType: String(req.headers["content-type"] ?? ""),
    });

    if (!req.body || typeof req.body !== "object") {
      smartcarDebugLog("webhook", "reject: non-JSON body", {
        bodyType: req.body === undefined ? "undefined" : typeof req.body,
      });
      return res.status(400).json({ error: "JSON body required" });
    }

    const body = req.body as Record<string, unknown>;
    smartcarDebugLog(
      "webhook",
      "JSON body shape",
      smartcarWebhookBodyDebugShape(body),
    );
    const eventType =
      typeof body.eventType === "string" ? body.eventType.toUpperCase() : "";
    const eventName =
      typeof body.eventName === "string" ? body.eventName.toLowerCase() : "";

    const isModernVerify = eventType === "VERIFY";
    const isLegacyVerify = eventName === "verify";

    if (isModernVerify || isLegacyVerify) {
      const dataBlock = isModernVerify ? body.data : body.payload;
      const challenge =
        dataBlock &&
        typeof dataBlock === "object" &&
        typeof (dataBlock as Record<string, unknown>).challenge === "string"
          ? String((dataBlock as Record<string, unknown>).challenge).trim()
          : "";

      if (!challenge) {
        smartcarDebugLog("webhook", "VERIFY missing challenge", {
          isModernVerify,
          isLegacyVerify,
        });
        return res.status(400).json({
          error: "VERIFY payload missing data.challenge / payload.challenge",
        });
      }

      const amt =
        process.env.SMARTCAR_APPLICATION_MANAGEMENT_TOKEN?.trim() ||
        process.env.APPLICATION_MANAGEMENT_TOKEN?.trim();

      if (!amt) {
        console.error(
          `${TAG} VERIFY received but SMARTCAR_APPLICATION_MANAGEMENT_TOKEN (or APPLICATION_MANAGEMENT_TOKEN) is not set`,
        );
        smartcarDebugLog("webhook", "VERIFY rejected: AMT not configured", {
          challengeLen: challenge.length,
        });
        return res.status(503).json({
          error:
            "Webhook verification not configured (missing Application Management Token)",
        });
      }

      const hmacHex = computeSmartcarVerifyHmac(amt, challenge);
      smartcarFlowLog(
        "webhook_verify",
        "VERIFY challenge accepted (HMAC returned)",
        {
          challengeLen: challenge.length,
        },
      );
      smartcarDebugLog("webhook", "VERIFY OK (HMAC length)", {
        challengeLen: challenge.length,
        hmacHexLen: hmacHex.length,
      });
      return res.status(200).json({ challenge: hmacHex });
    }

    /* ── SC-Signature verification (non-VERIFY payloads) ────────────── */
    const amt =
      process.env.SMARTCAR_APPLICATION_MANAGEMENT_TOKEN?.trim() ||
      process.env.APPLICATION_MANAGEMENT_TOKEN?.trim();
    const scSignature =
      typeof req.headers["sc-signature"] === "string"
        ? req.headers["sc-signature"]
        : "";
    const rawBody = (req as unknown as Record<string, unknown>).rawBody as
      | string
      | undefined;

    if (amt && scSignature && rawBody) {
      if (!verifySmartcarPayloadSignature(amt, scSignature, rawBody)) {
        smartcarDebugLog("webhook", "SC-Signature mismatch — rejecting", {
          eventType,
        });
        return res.status(401).json({ error: "Invalid SC-Signature" });
      }
      smartcarDebugLog("webhook", "SC-Signature verified OK", { eventType });
    } else if (amt && !scSignature) {
      smartcarDebugLog(
        "webhook",
        "SC-Signature header missing — accepting (may be legacy)",
        {
          eventType,
        },
      );
    }

    if (typeof body.eventType === "string") {
      smartcarFlowLog("webhook_delivery", "vehicle webhook payload", {
        eventType: body.eventType,
        deliveryId: String(
          (body.meta as Record<string, unknown> | undefined)?.deliveryId ?? "",
        ),
      });
    }

    try {
      await syncIsCarsRegisteredFromDelivery(body);
    } catch (syncErr) {
      console.error(`${TAG} isCarsRegistered webhook sync skipped:`, syncErr);
    }

    /* ── Persist VEHICLE_STATE signals to VehicleModel ────────────── */
    if (eventType === "VEHICLE_STATE") {
      try {
        await persistVehicleStateSignals(body);
      } catch (sigErr) {
        console.error(`${TAG} signal persistence skipped:`, sigErr);
      }
    }

    smartcarDebugLog("webhook", "delivery OK 200", {
      eventType: typeof body.eventType === "string" ? body.eventType : "(none)",
    });
    return res.status(200).json({ received: true });
  } catch (e) {
    console.error(`${TAG} Webhook handler error:`, e);
    smartcarDebugLog("webhook", "handler threw", {
      err: e instanceof Error ? e.message : String(e),
    });
    return res.status(500).json({ error: "Internal server error" });
  }
};

/**
 * POST /smartcar/vehicle/:vehicleId/subscribe-webhook (authenticated)
 * Subscribes the Smartcar vehicle to your Dashboard webhook (Management API).
 * @see https://smartcar.com/docs/api-reference/create-subscription
 */
export const subscribeVehicleToSmartcarWebhook = async (
  req: CustomRequest,
  res: Response,
) => {
  try {
    const userId = req.userId!;
    const { vehicleId } = req.params;
    const body = (req.body ?? {}) as Record<string, unknown>;
    const bodyWebhookId =
      typeof body.webhookId === "string" ? body.webhookId.trim() : "";
    const webhookId = bodyWebhookId || getDefaultWebhookIdFromEnv();
    const amt = getApplicationManagementTokenFromEnv();

    if (!vehicleId || !parseTankUserIdFromOAuthState(vehicleId)) {
      return res.status(400).json({ error: "Invalid Tank Track vehicle id" });
    }

    if (!webhookId) {
      return res.status(503).json({
        error:
          "webhookId missing — set SMARTCAR_WEBHOOK_ID on the server or pass { webhookId } in the body",
      });
    }

    if (!amt) {
      return res.status(503).json({
        error:
          "Application Management Token missing — set APPLICATION_MANAGEMENT_TOKEN or SMARTCAR_APPLICATION_MANAGEMENT_TOKEN",
      });
    }

    const dbVehicle = await VehicleModel.findOne({
      _id: new Types.ObjectId(vehicleId),
      userId: new Types.ObjectId(userId),
      isDeleted: false,
    }).lean();

    if (!dbVehicle) {
      return res.status(404).json({ error: "Vehicle not found" });
    }

    const smartcarVehicleId =
      typeof dbVehicle.smartcarVehicleId === "string"
        ? dbVehicle.smartcarVehicleId.trim()
        : "";
    const smartcarUserIdRow =
      typeof dbVehicle.smartcarUserId === "string"
        ? dbVehicle.smartcarUserId.trim()
        : "";

    if (!smartcarVehicleId || !smartcarUserIdRow) {
      return res
        .status(400)
        .json({ error: "Vehicle is not linked to Smartcar" });
    }

    const result = await createWebhookSubscriptionViaManagementApi({
      applicationManagementToken: amt,
      webhookId,
      smartcarUserId: smartcarUserIdRow,
      smartcarVehicleId,
    });

    if (result.ok || result.status === 409) {
      return res.json({
        message:
          result.status === 409
            ? "Vehicle is already subscribed to this webhook"
            : "Webhook subscription accepted (processing at Smartcar)",
        data: {
          httpStatus: result.status,
          webhookId,
          smartcarVehicleId,
          smartcarUserId: smartcarUserIdRow,
        },
      });
    }

    return res
      .status(result.status >= 400 && result.status < 600 ? result.status : 502)
      .json({
        error: "Smartcar Management API subscription failed",
        data: { httpStatus: result.status, body: result.body },
      });
  } catch (e) {
    console.error(`${TAG} subscribe-webhook error:`, e);
    return res
      .status(500)
      .json({ error: "Failed to subscribe vehicle to webhook" });
  }
};

/**
 * GET /smartcar/vehicles (authenticated)
 * Returns the user's Smartcar-linked vehicles from our DB. If a `sync=true` query
 * param is passed, refreshes data from Smartcar first (uses stored/refreshed token).
 */
export const getSmartcarVehicles = async (
  req: CustomRequest,
  res: Response,
) => {
  try {
    const userId = req.userId!;
    const shouldSync = req.query.sync === "true";

    if (shouldSync) {
      try {
        let smartcarUserIdForWebhook = "";
        const vehicles = await withSmartcarAccessToken(
          userId,
          async (accessToken, account) => {
            smartcarUserIdForWebhook = account.smartcarUserId;
            return syncSmartcarVehicles(
              userId,
              accessToken,
              account.smartcarUserId,
            );
          },
        );
        if (vehicles == null) {
          return res.status(400).json({
            error:
              "Smartcar not connected or token expired. Please re-link your vehicle.",
            smartcarConnected: false,
          });
        }
        if (smartcarUserIdForWebhook) {
          await afterSmartcarVehicleSync(
            userId,
            smartcarUserIdForWebhook,
            true,
          );
        }
        if (
          process.env.SMARTCAR_AUTO_SUBSCRIBE_WEBHOOK?.trim() === "true" &&
          smartcarUserIdForWebhook
        ) {
          void subscribeVehiclesToDefaultWebhook({
            smartcarUserId: smartcarUserIdForWebhook,
            vehicles,
          }).catch((subErr) =>
            console.error(
              `${TAG} Auto webhook subscribe failed (non-fatal):`,
              subErr,
            ),
          );
        }
        return res.json({
          message: "Vehicles synced from Smartcar",
          data: { vehicles, smartcarConnected: true },
        });
      } catch (syncErr) {
        console.error(`${TAG} Sync failed:`, syncErr);
        // Fall through to return DB data
      }
    }

    const vehicles = await VehicleModel.find({
      userId: new Types.ObjectId(userId),
      isDeleted: false,
    })
      .sort({ isDefault: -1, createdAt: -1 })
      .lean();

    const account = await SmartcarAccountModel.findOne({
      userId: new Types.ObjectId(userId),
      status: true,
    })
      .select("smartcarUserId tokenExpiresAt")
      .lean();

    return res.json({
      message: "User vehicles",
      data: {
        vehicles,
        smartcarConnected: !!account,
      },
    });
  } catch (e) {
    console.error(`${TAG} getSmartcarVehicles error:`, e);
    return res.status(500).json({ error: "Failed to fetch vehicles" });
  }
};

/**
 * GET /smartcar/vehicle/:vehicleId (authenticated)
 * Returns a single vehicle's live data from Smartcar (odometer, fuel, location).
 */
export const getSmartcarVehicleLive = async (
  req: CustomRequest,
  res: Response,
) => {
  try {
    const userId = req.userId!;
    const { vehicleId } = req.params;

    if (!vehicleId) {
      return res.status(400).json({ error: "vehicleId param is required" });
    }

    const dbVehicle = await VehicleModel.findOne({
      _id: vehicleId,
      userId: new Types.ObjectId(userId),
      isDeleted: false,
    }).lean();

    if (!dbVehicle) {
      return res.status(404).json({ error: "Vehicle not found" });
    }

    if (!dbVehicle.smartcarVehicleId) {
      return res
        .status(400)
        .json({ error: "Vehicle is not linked to Smartcar" });
    }

    const live = await withSmartcarAccessToken(userId, async (accessToken) => {
      const vehicle = new smartcar.Vehicle(
        dbVehicle.smartcarVehicleId!,
        accessToken,
      );

      const attributes = await vehicle.attributes();

      let odometer: { distance?: number } | null = null;
      let fuel: { percentRemaining?: number; amountRemaining?: number } | null =
        null;
      let location: { latitude?: number; longitude?: number } | null = null;

      try {
        const batch = await vehicle.batch(["/odometer", "/fuel", "/location"]);
        odometer = batch.odometer();
        fuel = batch.fuel();
        location = batch.location();
      } catch {
        try {
          odometer = await vehicle.odometer();
        } catch {
          /* skip */
        }
        try {
          fuel = await vehicle.fuel();
        } catch {
          /* skip */
        }
        try {
          location = await vehicle.location();
        } catch {
          /* skip */
        }
      }

      const odometerMiles =
        odometer?.distance != null
          ? Math.round(odometer.distance * 0.621371)
          : null;

      return {
        attributes,
        odometerMiles,
        fuelPercent:
          fuel?.percentRemaining != null
            ? Math.round(fuel.percentRemaining * 100)
            : null,
        fuelGallons:
          fuel?.amountRemaining != null
            ? Math.round(fuel.amountRemaining * 0.264172 * 100) / 100
            : null,
        location: location
          ? { lat: location.latitude, lng: location.longitude }
          : null,
      };
    });

    if (live == null) {
      return res.status(400).json({
        error: "Smartcar token expired. Please re-link your vehicle.",
        smartcarConnected: false,
      });
    }

    return res.json({
      message: "Live vehicle data",
      data: {
        id: dbVehicle._id,
        smartcarVehicleId: dbVehicle.smartcarVehicleId,
        name: `${live.attributes.make} ${live.attributes.model}`,
        year: live.attributes.year,
        make: live.attributes.make,
        model: live.attributes.model,
        odometerMiles: live.odometerMiles,
        fuelPercent: live.fuelPercent,
        fuelGallons: live.fuelGallons,
        location: live.location,
      },
    });
  } catch (e) {
    const err = e as { type?: string; message?: string; statusCode?: number };
    console.error(`${TAG} getSmartcarVehicleLive error:`, err.message || e);

    if (err.type === "PERMISSION") {
      return res.status(403).json({
        error: "Insufficient Smartcar permissions. Re-link your vehicle.",
      });
    }
    if (err.statusCode === 429) {
      return res
        .status(429)
        .json({ error: "Smartcar rate limit. Try again shortly." });
    }
    if (err.statusCode === 401) {
      return res.status(400).json({
        error: "Smartcar access token rejected. Please re-link your vehicle.",
        smartcarConnected: false,
      });
    }

    return res.status(500).json({ error: "Failed to fetch live vehicle data" });
  }
};

/**
 * POST /smartcar/disconnect (authenticated)
 * Disconnects Smartcar — revokes the token and marks the account inactive.
 */
export const disconnectSmartcar = async (req: CustomRequest, res: Response) => {
  try {
    const userId = req.userId!;

    const account = await SmartcarAccountModel.findOne({
      userId: new Types.ObjectId(userId),
      status: true,
    });

    if (!account) {
      return res.status(404).json({ error: "No active Smartcar connection" });
    }

    account.status = false;
    await account.save();

    const vehicleCount = await VehicleModel.countDocuments({
      userId: new Types.ObjectId(userId),
      isDeleted: false,
    });
    await UserModel.findByIdAndUpdate(userId, {
      $set: {
        smartcarToken: null,
        isCarsRegistered: vehicleCount > 0,
      },
    });

    return res.json({ message: "Smartcar disconnected" });
  } catch (e) {
    console.error(`${TAG} disconnect error:`, e);
    return res.status(500).json({ error: "Failed to disconnect Smartcar" });
  }
};

/**
 * GET /smartcar/status (authenticated)
 * Quick check: is Smartcar connected? Is the token valid?
 */
export const getSmartcarStatus = async (req: CustomRequest, res: Response) => {
  try {
    const userId = req.userId!;

    const probeIam =
      typeof req.query.smartcar_probe_iam === "string" &&
      /^1|true|yes$/i.test(req.query.smartcar_probe_iam);

    let smartcarIamProbe:
      | { ok: false; detail: string }
      | {
          ok: true;
          applicationTokenExpiresInApprox: number;
        }
      | undefined;

    if (probeIam) {
      try {
        const iam = await getCachedSmartcarIamApplicationAccessToken();
        smartcarIamProbe = {
          ok: true,
          applicationTokenExpiresInApprox: iam.expiresIn,
        };
      } catch (probeErr: unknown) {
        const msg =
          probeErr instanceof Error ? probeErr.message : String(probeErr);
        smartcarIamProbe = { ok: false, detail: msg };
      }
    }

    const iamMeta =
      probeIam && smartcarIamProbe
        ? {
            iamDocs:
              "https://smartcar.com/docs/api-reference/authorization/request-access-token",
          }
        : {};

    const account = await SmartcarAccountModel.findOne({
      userId: new Types.ObjectId(userId),
      status: true,
    })
      .select("smartcarUserId tokenExpiresAt updatedAt lastVehicleSyncAt")
      .lean();

    if (!account) {
      return res.json({
        data: {
          connected: false,
          tokenValid: false,
          ...(smartcarIamProbe
            ? { smartcarIamApplicationAccess: smartcarIamProbe }
            : {}),
        },
        message: "Smartcar not connected",
        ...iamMeta,
      });
    }

    const tokenValid =
      account.tokenExpiresAt != null &&
      new Date(account.tokenExpiresAt).getTime() > Date.now();

    return res.json({
      data: {
        connected: true,
        tokenValid,
        smartcarUserId: account.smartcarUserId,
        tokenExpiresAt: account.tokenExpiresAt,
        lastVehicleSyncAt: account.lastVehicleSyncAt ?? null,
        ...(smartcarIamProbe
          ? { smartcarIamApplicationAccess: smartcarIamProbe }
          : {}),
      },
      message: "Smartcar status",
      ...iamMeta,
    });
  } catch (e) {
    console.error(`${TAG} status error:`, e);
    return res.status(500).json({ error: "Failed to check Smartcar status" });
  }
};
