import smartcar from "smartcar";
import { API_PREFIX, PUBLIC_API_URL } from "./environment";

/**
 * Smartcar Dashboard vs Tank Track routes
 * --------------------------------------------------------------------------
 * **OAuth “Redirect URIs” (browser GET after Connect):** Register exactly
 * `SMARTCAR_REDIRECT_URI` — typically `…/api/v1/smartcar/callback` or `…/smartcar/redirect`.
 * Same idea as Smartcar’s sample `GET /callback`; we use authenticated `GET …/smartcar/login`
 * to build the authorize `link` (state + Tank user binding), then Smartcar returns to
 * this URI with `?code=&user_id=&state=`.
 *
 * **Vehicle data / webhook (POST):** Dashboard “Vehicle data callback URI” = `SMARTCAR_VEHICLE_WEBHOOK_URL`
 * (default `…/smartcar/webhook`). May be the **same path** as OAuth callback here: Express routes
 * by method (`GET` OAuth vs `POST` VERIFY + payloads).
 *
 * **Mobile:** After successful exchange we `302` to your deeplink / handoff URLs from env, not
 * Smartcar’s sample inline HTML — see `smartcarController.finishOAuthWithFlash`.
 * @see https://smartcar.com/docs/connect/dashboard-config
 *
 * Canonicalize redirect URI used in Connect + token exchange so it matches exactly
 * what is registered in the Smartcar dashboard (fixes invalid_grant when env has extra
 * ?query / trailing slash mismatches vs the dashboard).
 */
function normalizeSmartcarRedirectUri(raw: string): string {
  let s = raw.trim();
  try {
    const u = new URL(s);
    if (u.pathname.length > 1) {
      u.pathname = u.pathname.replace(/\/+$/, "");
    }
    if (u.search || u.hash) {
      const hadExtras = !!(u.search || u.hash);
      u.search = "";
      u.hash = "";
      if (hadExtras) {
        console.warn(
          "[Smartcar] SMARTCAR_REDIRECT_URI contained ?query or #fragment — removed. " +
            "OAuth redirect_uri registration must not include Smartcar-added params; use bare callback URL.",
        );
      }
    }
    const out = u.toString().replace(/\/+$/, "");
    if (PUBLIC_API_URL) {
      const origin = PUBLIC_API_URL.toLowerCase();
      if (!out.toLowerCase().startsWith(origin)) {
        console.warn(
          `[Smartcar] Redirect URI hostname differs from PUBLIC_API_URL — ensure Dashboard matches computed URI: ${out}`,
        );
      }
    }
    return out;
  } catch {
    return s.replace(/\/+$/, "").split("?")[0]?.split("#")[0] ?? s;
  }
}

function resolveSmartcarRedirectUri(): string | undefined {
  const explicit = process.env.SMARTCAR_REDIRECT_URI?.trim();
  if (explicit) return normalizeSmartcarRedirectUri(explicit);
  if (PUBLIC_API_URL)
    return normalizeSmartcarRedirectUri(
      `${PUBLIC_API_URL}${API_PREFIX}/smartcar/redirect`,
    );
  return undefined;
}

function normalizeSmartcarVehicleWebhookUrl(raw: string): string {
  try {
    const u = new URL(raw.trim());
    if (u.search || u.hash) {
      u.search = "";
      u.hash = "";
    }
    if (u.pathname.length > 1) {
      u.pathname = u.pathname.replace(/\/+$/, "");
    }
    return u.toString().replace(/\/+$/, "");
  } catch {
    return (
      raw.trim().replace(/\/+$/, "").split("?")[0]?.split("#")[0] ?? raw.trim()
    );
  }
}

/**
 * Dashboard “Vehicle data callback URI” (POST VERIFY + payloads).
 * Set SMARTCAR_VEHICLE_WEBHOOK_URL to match exactly, e.g. …/smartcar/callback .
 * Omit to default …/smartcar/webhook .
 */
function resolveSmartcarVehicleWebhookUrl(): string | undefined {
  const explicit =
    process.env.SMARTCAR_VEHICLE_WEBHOOK_URL?.trim() ||
    process.env.SMARTCAR_WEBHOOK_URL?.trim();
  if (explicit) return normalizeSmartcarVehicleWebhookUrl(explicit);
  if (PUBLIC_API_URL && API_PREFIX)
    return `${PUBLIC_API_URL}${API_PREFIX}/smartcar/webhook`;
  return undefined;
}

const resolvedSmartcarRedirectUri = resolveSmartcarRedirectUri();

/** Warn when Dashboard redirect URL is likely missing `/api/v1` (or your `API_PREFIX`) before `/smartcar/...`. */
function warnIfRedirectUriPathMismatchesApiMount(
  redirectUri: string | undefined,
): void {
  if (!redirectUri || !API_PREFIX) return;
  const prefix = API_PREFIX.replace(/\/+$/, "");
  if (!prefix) return;
  try {
    const path = new URL(redirectUri).pathname.replace(/\/+$/, "") || "/";
    const expectedSegment = `${prefix}/smartcar`;
    if (path.includes("/smartcar") && !path.includes(expectedSegment)) {
      console.warn(
        `[Smartcar] SMARTCAR_REDIRECT_URI path "${path}" does not include "${expectedSegment}". ` +
          `Tank Track mounts OAuth at GET ${prefix}/smartcar/redirect or GET ${prefix}/smartcar/callback — ` +
          `register that full URL in the Smartcar Dashboard (see https://smartcar.com/docs/connect/dashboard-config ).`,
      );
    }
  } catch {
    /* non-URL redirect — skip */
  }
}

warnIfRedirectUriPathMismatchesApiMount(resolvedSmartcarRedirectUri);

const smartcarClientId = process.env.SMARTCAR_CLIENT_ID?.trim();
const smartcarIamClientId = process.env.SMARTCAR_IAM_CLIENT_ID?.trim();
const smartcarClientSecret = process.env.SMARTCAR_CLIENT_SECRET?.trim();
const smartcarMode =
  process.env.SMARTCAR_MODE?.trim().toLowerCase() === "live" ? "live" : "test";

const configured = !!(smartcarClientId && smartcarClientSecret);

/** Resolved POST webhook URL — paste into Dashboard “Vehicle data callback URI”. Alias routes: POST /webhook and POST /callback. */
export const SMARTCAR_EFFECTIVE_WEBHOOK_URL =
  resolveSmartcarVehicleWebhookUrl();

if (configured) {
  console.info("[Smartcar] Configured ✓");
  console.info(`  mode:         ${smartcarMode}`);
  console.info(`  clientId:     ${smartcarClientId!.slice(0, 12)}… (primary)`);
  if (smartcarIamClientId && smartcarIamClientId !== smartcarClientId) {
    console.info(
      `  fallbackId:   ${smartcarIamClientId.slice(0, 12)}… (IAM legacy)`,
    );
  }
  console.info(
    `  redirect_uri: ${resolvedSmartcarRedirectUri ?? "(none — set SMARTCAR_REDIRECT_URI or PUBLIC_API_URL)"}`,
  );
  if (SMARTCAR_EFFECTIVE_WEBHOOK_URL) {
    console.info(
      `  webhook POST: ${SMARTCAR_EFFECTIVE_WEBHOOK_URL} — VERIFY + deliveries (same handler on POST /webhook ; OAuth browser stays GET)`,
    );
  }
} else {
  console.warn(
    "[Smartcar] NOT configured — SMARTCAR_CLIENT_ID or SMARTCAR_CLIENT_SECRET missing. " +
      "Smartcar endpoints will return errors.",
  );
}

export const smartcarClient = new smartcar.AuthClient({
  clientId: smartcarClientId || "",
  clientSecret: smartcarClientSecret,
  redirectUri: resolvedSmartcarRedirectUri,
  mode: smartcarMode,
});

const fallbackId =
  smartcarIamClientId && smartcarIamClientId !== smartcarClientId
    ? smartcarIamClientId
    : null;

export const smartcarFallbackClient: InstanceType<
  typeof smartcar.AuthClient
> | null = fallbackId
  ? new smartcar.AuthClient({
      clientId: fallbackId,
      clientSecret: smartcarClientSecret,
      redirectUri: resolvedSmartcarRedirectUri,
      mode: smartcarMode,
    })
  : null;

/** Resolved redirect — useful for debugging invalid_grant. */
export const SMARTCAR_EFFECTIVE_REDIRECT_URI = resolvedSmartcarRedirectUri;

export const SMARTCAR_CONFIGURED = configured;
