"use client";

import { configureApi, getApiConfig } from "@enode/core/api/config";
import {
  getDeviceNameSuffix,
  resolveDeviceIdentity,
  type DeviceFacts,
  type DeviceIdentity,
  type DevicePlatform,
} from "@enode/core/api/device-identity";
import {
  resolveApiBaseUrl,
  resolveDevApiBasePath,
} from "@enode/core/api/servers";
import { getDeviceInfo } from "@enode/core/diagnostics/device-info";
import { installNativeHttpTransport } from "@enode/core/api/capacitor-http";
import { installNativeRealtimeTransport } from "@enode/core/realtime/capacitor-sse";

// Inject this app's API configuration into @enode/core, once, at module load.
// Mounted as the first child in the root layout — its client chunk evaluates
// during hydration, so configureApi has run before any store loader's effect
// fires its first apiRequest.
//
// Dev hits the Next rewrite proxy at /api (same-origin, no CORS); a static
// production build talks to the backend directly (Capacitor uses native HTTP).

// The web-bundle version, injected by next.config.ts from the nearest `v*` git
// tag ("dev" without one). On device the installed native version (App.getInfo)
// replaces it asynchronously. Bare version, no build number — this becomes the
// `app-version` header.
const APP_VERSION_FALLBACK = process.env.NEXT_PUBLIC_APP_VERSION ?? "dev";

// Dev-only override: a desktop browser registers as "web" (a monitor-category
// device session), which can't join a live session as a broadcaster — only
// phone/tablet categories can. `NEXT_PUBLIC_DEVICE_TYPE=phone npm run dev
// tracking …` lets a local dev instance register as a training device for
// end-to-end live-session testing. Never applied in production builds — the
// real device-type drives device-limit enforcement and the app-flavor gate.
function devDeviceTypeOverride(): string | null {
  if (process.env.NODE_ENV === "production") return null;
  return process.env.NEXT_PUBLIC_DEVICE_TYPE ?? null;
}

// Guarded for SSR/prerender where there is no window. The window cast (instead
// of importing @capacitor/core) keeps this module evaluatable on the server.
function capacitorPlatform(): DevicePlatform {
  if (typeof window === "undefined") return "web";
  const cap = (window as unknown as { Capacitor?: { getPlatform?: () => string } })
    .Capacitor;
  const platform = cap?.getPlatform?.();
  return platform === "ios" || platform === "android" ? platform : "web";
}

function smallestScreenSide(): number | null {
  if (typeof window === "undefined") return null;
  return Math.min(window.screen.width, window.screen.height);
}

// The ApiConfig `deviceType` is a plain string, so the free-form dev override
// can replace the resolved category key without widening DeviceIdentity.
function identityFor(
  facts: DeviceFacts,
  appVersion: string,
): Omit<DeviceIdentity, "deviceType"> & { deviceType: string } {
  const identity = resolveDeviceIdentity(
    facts,
    appVersion,
    getDeviceNameSuffix(),
  );
  const override = devDeviceTypeOverride();
  return override ? { ...identity, deviceType: override } : identity;
}

// Sync best-effort facts for the initial configureApi, UA-parsed. Precise on
// Android and iPhone; an iPad's WebView presents a desktop UA (no model, no OS
// version), so the initial pass can misread it — refineDeviceIdentity()
// corrects everything from the native plugins right after, long before login
// creates the backend device session from these headers.
function initialFacts(): DeviceFacts {
  const info = getDeviceInfo();
  return {
    platform: capacitorPlatform(),
    model: info.device.model,
    osVersion: info.os.version || null,
    userAssignedName: null,
    smallestScreenSide: smallestScreenSide(),
  };
}

configureApi({
  // Switch backends centrally via NEXT_PUBLIC_API_SERVER / NEXT_PUBLIC_API_BASE_URL
  // (api/servers.ts). Dev hits a same-origin rewrite proxy (next.config.ts);
  // `resolveDevApiBasePath()` picks the prefix — `/api` (env default) or
  // `/api-<name>` when the debug Server tab has stored an override.
  baseUrl:
    process.env.NODE_ENV === "production"
      ? resolveApiBaseUrl()
      : resolveDevApiBasePath(),
  // The `pro_app`-subject api-key. The backend reads this `sub` to apply the
  // Pro-app flavor (the 314 app-flavor gate + device-limit). Override per-env
  // with NEXT_PUBLIC_API_KEY if needed.
  apiKey:
    process.env.NEXT_PUBLIC_API_KEY ??
    "eyJraWQiOiJwcml2YXRlIiwiYWxnIjoiRVM1MTIiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjY0MDkyMjExMjAwLCJzdWIiOiJwcm9fYXBwIn0.ANw_CPsaySsViQRH6-OxP18i5yD_DloDS3UmQwcXuSQhH38R2vz4aOon4DwlprqDI09pns8pY91AJJh16kLvPfIaAR_y_3xsnGxiAzpvMsqZLfopsgPYvGc5VG4_9gQje33POjfg3Axn6w9ICg3fZHprlrT47OAmS_5AMgXKRinmA_lQ",
  appVersion: APP_VERSION_FALLBACK,
  // Native runtimes use Capacitor's HTTP stack (no CORS), so they may send the
  // full identity header set (User-Agent + underscore/dash spellings); a web
  // build must not (see api/client.ts). The async refine below carries this
  // through via `...getApiConfig()`.
  isNative: capacitorPlatform() !== "web",
  ...identityFor(initialFacts(), APP_VERSION_FALLBACK),
});

// On native, the WebView can't see the user's device name, the iPad's real
// model/OS version, the native bundle version or its bundle id — fetch them via
// @capacitor/device / @capacitor/app and re-inject. Config is read per
// request, so everything that fires after this resolves (notably login, which
// creates the backend device session from these headers) carries the refined
// identity. Dynamic imports keep the plugins out of the SSR/web-first paint.
async function refineDeviceIdentity(): Promise<void> {
  const platform = capacitorPlatform();
  if (platform === "web") return;
  try {
    const [{ Device }, { App }] = await Promise.all([
      import("@capacitor/device"),
      import("@capacitor/app"),
    ]);
    const [info, appInfo] = await Promise.all([
      Device.getInfo(),
      App.getInfo().catch(() => null),
    ]);
    const appVersion = appInfo?.version || APP_VERSION_FALLBACK;
    const facts: DeviceFacts = {
      platform,
      model: info.model?.trim() || null,
      osVersion: info.osVersion?.trim() || null,
      // iOS 16+ reports the generic model name here without the
      // user-assigned-device-name entitlement; resolveDeviceName detects that
      // and falls back to "<model> <suffix>".
      userAssignedName: info.name?.trim() || null,
      smallestScreenSide: smallestScreenSide(),
    };
    configureApi({
      ...getApiConfig(),
      appVersion,
      // Which app this is, for push targeting and transport. The backend needs
      // it to route to Android at all — see ApiConfig.bundleId. Read from the
      // NATIVE bundle rather than capacitor.config's `appId`: the two differ
      // (the real ids are com.enode.enodeone(.debug) and
      // com.BMSportsTechnology.Vmaxpro), and only the native one is registered
      // with APNs/FCM.
      bundleId: appInfo?.id || undefined,
      ...identityFor(facts, appVersion),
    });
  } catch {
    // Best-effort: the sync UA-derived identity stays in place.
  }
}
void refineDeviceIdentity();

// On native (iOS / Android), route realtime SSE over the native plugin instead
// of the fetch-stream transport: `CapacitorHttp` reroutes `window.fetch` through
// the buffering native HTTP stack, which can't stream an event-stream. No-op on
// web. Runs at module load, before any store loader's effect opens a stream.
installNativeRealtimeTransport();

// On Android, route API GETs through the CapacitorHttp plugin instead of the
// WebView proxy `CapacitorHttp` sends them to: that proxy has to return an
// `android.webkit.WebResourceResponse`, which refuses any status in [300, 399]
// — destroying the API's 310/311/312 resumable redirects before `apiRequest`
// can see them. No-op on iOS (its interceptor carries any status) and on web.
// Runs at module load, before any request is made.
installNativeHttpTransport();

export function ApiConfigInit() {
  return null;
}
