// API + WebSocket client for the mobile app. Mirrors src/lib/backend-client.ts
// from the web app but reads env from Expo Constants and uses SecureStore for
// the token instead of localStorage.

import Constants from "expo-constants";
import * as SecureStore from "expo-secure-store";

const extra = (Constants.expoConfig?.extra ?? {}) as {
  apiBaseUrl?: string;
  wsUrl?: string;
};

export const API_BASE_URL =
  process.env.EXPO_PUBLIC_API_BASE_URL ?? extra.apiBaseUrl ?? "https://backend.thebyteflare.com/api";
export const WS_URL =
  process.env.EXPO_PUBLIC_WS_URL ?? extra.wsUrl ?? "ws://localhost:4000/ws";

const TOKEN_KEY = "delta-terminal-token";
const REFRESH_KEY = "delta-terminal-refresh";

export async function getToken() {
  return SecureStore.getItemAsync(TOKEN_KEY);
}
export async function setToken(t: string | null, refresh?: string | null) {
  if (t) await SecureStore.setItemAsync(TOKEN_KEY, t);
  else await SecureStore.deleteItemAsync(TOKEN_KEY);
  if (refresh !== undefined) {
    if (refresh) await SecureStore.setItemAsync(REFRESH_KEY, refresh);
    else await SecureStore.deleteItemAsync(REFRESH_KEY);
  }
}
export async function getRefreshToken() {
  return SecureStore.getItemAsync(REFRESH_KEY);
}

async function req<T>(path: string, init: RequestInit = {}): Promise<T> {
  const token = await getToken();
  const res = await fetch(`${API_BASE_URL}${path}`, {
    ...init,
    headers: {
      "Content-Type": "application/json",
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
      ...(init.headers ?? {}),
    },
  });
  if (!res.ok) {
    const body = await res.text().catch(() => res.statusText);
    throw new Error(`${res.status} ${body}`);
  }
  if (res.status === 204) return null as unknown as T;
  return (await res.json()) as T;
}

// ---------- Types ----------
export type OrderStatus =
  | "PENDING_APPROVAL" | "SUBMITTED" | "PARTIALLY_FILLED"
  | "FILLED" | "CANCELED" | "REJECTED";

export interface User {
  id: string; email: string;
  first_name: string | null; last_name: string | null;
  role: "user" | "admin";
  impersonating?: unknown;
}
export interface AuthResponse { token: string; refresh_token?: string; user: User; }

export interface Position {
  _id: string; coin_symbol: string;
  position_side: "LONG" | "SHORT";
  entry_price: number; current_price: number;
  size: number; leverage: number;
  stop_loss: number; take_profit: number;
  pnl: number; pnl_pct: number;
  order_status: OrderStatus;
  execution_mode?: "paper" | "live";
  opened_at: string;
}
export interface Signal {
  _id: string; coin_symbol: string;
  position_side: "LONG" | "SHORT";
  entry_price: number; stop_loss: number; take_profit: number;
  leverage: number; confidence: number;
  rationale: string; algo?: string; algo_name?: string;
  timeframe?: string;
  created_at: string; expires_at: string;
}
export interface DeltaAccount {
  connected: boolean;
  account_id?: string; email?: string;
  balance?: number; currency?: string;
  permissions?: string[];
  last_synced_at?: string;
}
export interface AdminSettings {
  branding: Record<string, string>;
  features: Record<string, boolean>;
  oauth: Record<string, { client_id: string; redirect_uri: string; has_secret: boolean }>;
}

// ---------- REST ----------
export const api = {
  health: () => req<{ ok: true }>("/health"),

  login: (email: string, password: string) =>
    req<AuthResponse>("/auth/login", { method: "POST", body: JSON.stringify({ email, password }) }),
  register: (body: { email: string; password: string; first_name?: string; last_name?: string }) =>
    req<AuthResponse>("/auth/register", { method: "POST", body: JSON.stringify(body) }),
  me: () => req<User>("/auth/me"),
  refresh: (refresh_token: string) =>
    req<AuthResponse>("/auth/refresh", { method: "POST", body: JSON.stringify({ refresh_token }) }),

  getPublicSettings: () => req<Record<string, unknown>>("/settings/public"),
  getAdminSettings: () => req<AdminSettings>("/settings/admin"),
  saveAdminSettings: (patch: Partial<AdminSettings>) =>
    req<AdminSettings>("/settings/admin", { method: "POST", body: JSON.stringify(patch) }),

  saveDeltaCredentials: (body: { api_key: string; api_secret: string; region?: string; account_label?: string }) =>
    req<{ ok: true; account: DeltaAccount }>("/delta/credentials", { method: "POST", body: JSON.stringify(body) }),
  getDeltaAccount: () => req<DeltaAccount>("/delta/account"),
  getLiveTrading: () => req<{ ok: true; live_trading_enabled: boolean; has_credentials: boolean }>("/delta/live-trading"),
  setLiveTrading: (enabled: boolean) =>
    req<{ ok: true; live_trading_enabled: boolean }>("/delta/live-trading", { method: "POST", body: JSON.stringify({ enabled }) }),

  listPositions: () => req<Position[]>("/positions"),
  placeManualTrade: (body: {
    coin_symbol: string; position_side: "LONG" | "SHORT";
    entry_price: number; stop_loss: number; take_profit: number;
    order_size_value: number; leverage?: number;
    order_sizing_type?: "FIXED_AMOUNT" | "PERCENTAGE";
  }) => req<{ ok: true; position_id: string; position: Position }>("/positions/manual", { method: "POST", body: JSON.stringify(body) }),
  closePosition: (id: string) => req<{ ok: true }>(`/positions/${id}/close`, { method: "POST" }),
  updateSlTp: (id: string, body: { stop_loss: number; take_profit: number }) =>
    req<{ ok: true; position: Position }>(`/positions/${id}/sl-tp`, { method: "POST", body: JSON.stringify(body) }),

  listSignals: (status = "pending") => req<Signal[]>(`/signals?status=${status}`),
  approveSignal: (id: string) => req<{ ok: true }>(`/signals/${id}/approve`, { method: "POST" }),
  rejectSignal: (id: string, reason?: string) =>
    req<{ ok: true }>(`/signals/${id}/reject`, { method: "POST", body: JSON.stringify({ reason }) }),
  signalHistory: (params: Record<string, string | number> = {}) => {
    const qs = new URLSearchParams(Object.entries(params).map(([k, v]) => [k, String(v)])).toString();
    return req<{ items: Signal[]; total: number; page: number; pageSize: number; stats: Record<string, number> }>(
      `/signals/history${qs ? `?${qs}` : ""}`,
    );
  },
  algoStats: () => req<{ items: Array<Record<string, number | string>> }>("/signals/algo-stats"),

  adminUsers: () => req<User[]>("/admin/users"),
  adminAudit: (params: Record<string, string | number> = {}) => {
    const qs = new URLSearchParams(Object.entries(params).map(([k, v]) => [k, String(v)])).toString();
    return req<Array<Record<string, unknown>>>(`/admin/audit${qs ? `?${qs}` : ""}`);
  },
  adminImpersonate: (userId: string) =>
    req<AuthResponse>(`/admin/users/${userId}/impersonate`, { method: "POST" }),
};

// ---------- WebSocket ----------
export type WsEvent =
  | { type: "position.update"; position: Position }
  | { type: "position.close"; position_id: string }
  | { type: "signal.new"; signal: Signal }
  | { type: "signal.expired"; signal_id: string }
  | { type: "account.update"; account: DeltaAccount }
  | { type: "pong" };

export class BackendWs {
  private ws: WebSocket | null = null;
  private listeners = new Set<(e: WsEvent) => void>();
  private attempt = 0;
  private stopped = false;
  private hbTimer: ReturnType<typeof setInterval> | null = null;
  private lastPong = 0;

  onEvent(fn: (e: WsEvent) => void) { this.listeners.add(fn); return () => this.listeners.delete(fn); }
  async start() {
    this.stopped = false;
    const token = await getToken();
    const url = token ? `${WS_URL}?token=${encodeURIComponent(token)}` : WS_URL;
    try { this.ws = new WebSocket(url); } catch { this.scheduleReconnect(); return; }
    this.ws.onopen = () => { this.attempt = 0; this.startHeartbeat(); };
    this.ws.onmessage = (e) => {
      try {
        const evt = JSON.parse(String(e.data)) as WsEvent;
        this.lastPong = Date.now();
        if (evt.type === "pong") return;
        this.listeners.forEach((l) => l(evt));
      } catch { /* ignore */ }
    };
    this.ws.onclose = () => { this.clearHeartbeat(); this.scheduleReconnect(); };
    this.ws.onerror = () => this.ws?.close();
  }
  stop() {
    this.stopped = true;
    this.clearHeartbeat();
    this.ws?.close();
    this.ws = null;
  }
  private startHeartbeat() {
    this.clearHeartbeat();
    this.lastPong = Date.now();
    this.hbTimer = setInterval(() => {
      if (!this.ws || this.ws.readyState !== 1) return;
      if (Date.now() - this.lastPong > 60_000) { try { this.ws.close(); } catch {} return; }
      try { this.ws.send(JSON.stringify({ type: "ping", ts: Date.now() })); } catch {}
    }, 25_000);
  }
  private clearHeartbeat() { if (this.hbTimer) { clearInterval(this.hbTimer); this.hbTimer = null; } }
  private scheduleReconnect() {
    if (this.stopped) return;
    this.attempt += 1;
    const base = Math.min(30_000, 500 * 2 ** (this.attempt - 1));
    const delay = base / 2 + Math.random() * base;
    setTimeout(() => this.start(), delay);
  }
}

let singleton: BackendWs | null = null;
export function getBackendWs() {
  if (!singleton) singleton = new BackendWs();
  return singleton;
}
