import type {
  ApiEnvelope,
  CompositeDecompositionResponse,
  CompositeSignalSeriesResponse,
  CompositeSignalSummary,
  CompositeTransitionResponse,
  DatasetDetail,
  DatasetRowsResponse,
  DatasetSummary,
  EngineSummary,
  ExecutionAccountSummary,
  ExecutionActionResponse,
  ExecutionBalanceRecord,
  ExecutionFillRecord,
  ExecutionHealthRecord,
  ExecutionMarketSummary,
  ExecutionOrderRecord,
  ExecutionPositionRecord,
  ExecutionVenueSummary,
  MicrostructureFeatureResponse,
  MicrostructureHealth,
  MicrostructureRegimeResponse,
  MicrostructureSignalSeriesResponse,
  MicrostructureSignalSummary,
  OptionsDashboardResponse,
  OptionsDashboardSummary,
  OptionsSignalSeriesResponse,
  OptionsSignalSummary,
  PerpsDashboardResponse,
  PerpsDashboardSummary,
  PerpsSignalSeriesResponse,
  PerpsSignalSummary,
  PriceSeriesResponse,
  PriceSummary,
  RunRecord,
  RunsSummary,
  SignalSeriesResponse,
  SignalSummary,
  SystemHealth,
} from "./types";

const SERVER_API_BASE =
  process.env.NEXT_PUBLIC_API_BASE_URL ??
  process.env.API_BASE_URL ??
  "http://127.0.0.1:8000";
const DEFAULT_REQUEST_TIMEOUT_MS = 8000;

function getApiBase() {
  if (typeof window === "undefined") {
    return SERVER_API_BASE;
  }
  return window.location.origin;
}

async function request<T>(
  path: string,
  params?: Record<string, string | number | null | undefined>,
  signal?: AbortSignal,
): Promise<T | null> {
  const proxyPath = typeof window === "undefined" ? path : `/proxy${path.replace(/^\/api\/v1/, "")}`;
  const url = new URL(proxyPath, getApiBase());
  if (params) {
    for (const [key, value] of Object.entries(params)) {
      if (value !== undefined && value !== null && value !== "") {
        url.searchParams.set(key, String(value));
      }
    }
  }
  const controller = new AbortController();
  const timer = globalThis.setTimeout(() => controller.abort(), DEFAULT_REQUEST_TIMEOUT_MS);
  const onAbort = () => controller.abort();
  signal?.addEventListener("abort", onAbort, { once: true });
  try {
    const response = await fetch(url.toString(), {
      cache: "no-store",
      signal: controller.signal,
    });
    if (!response.ok) {
      return null;
    }
    return (await response.json()) as T;
  } catch {
    return null;
  } finally {
    globalThis.clearTimeout(timer);
    signal?.removeEventListener("abort", onAbort);
  }
}

async function postRequest<T>(path: string, body: Record<string, unknown>): Promise<T | null> {
  const proxyPath = typeof window === "undefined" ? path : `/proxy${path.replace(/^\/api\/v1/, "")}`;
  const url = new URL(proxyPath, getApiBase());
  const controller = new AbortController();
  const timer = globalThis.setTimeout(() => controller.abort(), DEFAULT_REQUEST_TIMEOUT_MS);
  try {
    const response = await fetch(url.toString(), {
      method: "POST",
      cache: "no-store",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(body),
      signal: controller.signal,
    });
    if (!response.ok) {
      return null;
    }
    return (await response.json()) as T;
  } catch {
    return null;
  } finally {
    globalThis.clearTimeout(timer);
  }
}

export async function getSystemHealth(): Promise<SystemHealth> {
  const response = await request<ApiEnvelope<SystemHealth>>("/api/v1/system/health");
  return (
    response?.data ?? {
      status: "degraded",
      api_version: "v1",
      data_root: "unavailable",
      websocket_enabled: false,
      dataset_count: 0,
      signal_count: 0,
      latest_signal_update: null,
      latest_run_update: null,
    }
  );
}

export async function getSignals(): Promise<SignalSummary[]> {
  const response = await request<ApiEnvelope<SignalSummary[]>>("/api/v1/signals");
  return response?.data ?? [];
}

export async function getSignalLatest(signalId: string, source = "dvol"): Promise<SignalSummary | null> {
  const response = await request<ApiEnvelope<SignalSummary>>(`/api/v1/signals/${signalId}/latest`, {
    source,
  });
  return response?.data ?? null;
}

export async function getSignalSeries(
  signalId: string,
  source = "dvol",
  start?: string,
  end?: string,
  quality?: string,
  maxPoints?: number,
): Promise<SignalSeriesResponse | null> {
  const response = await request<ApiEnvelope<SignalSeriesResponse>>(
    `/api/v1/signals/${signalId}/series`,
    { source, start, end, quality, max_points: maxPoints },
  );
  return response?.data ?? null;
}

export async function getDatasets(): Promise<DatasetSummary[]> {
  const response = await request<ApiEnvelope<DatasetSummary[]>>("/api/v1/datasets");
  return response?.data ?? [];
}

export async function getDataset(datasetId: string): Promise<DatasetDetail | null> {
  const response = await request<ApiEnvelope<DatasetDetail>>(`/api/v1/datasets/${datasetId}`);
  return response?.data ?? null;
}

export async function getDatasetRows(
  datasetId: string,
  params: Record<string, string | number | null | undefined>,
): Promise<DatasetRowsResponse | null> {
  const response = await request<ApiEnvelope<DatasetRowsResponse>>(
    `/api/v1/datasets/${datasetId}/rows`,
    params,
  );
  return response?.data ?? null;
}

export async function getRuns(params?: Record<string, string | number | null | undefined>): Promise<RunRecord[]> {
  const response = await request<ApiEnvelope<RunRecord[]>>("/api/v1/runs", params);
  return response?.data ?? [];
}

export async function getRunsSummary(): Promise<RunsSummary> {
  const response = await request<ApiEnvelope<RunsSummary>>("/api/v1/runs/summary");
  return (
    response?.data ?? {
      total_runs: 0,
      success_count: 0,
      failed_count: 0,
      gap_count: 0,
      skipped_count: 0,
      latest_logical_ts: null,
      latest_run_lag_hours: null,
    }
  );
}

export async function getEngines(): Promise<EngineSummary[]> {
  const response = await request<ApiEnvelope<EngineSummary[]>>("/api/v1/engines");
  return response?.data ?? [];
}

export async function getExecutionVenues(): Promise<ExecutionVenueSummary[]> {
  const response = await request<ApiEnvelope<ExecutionVenueSummary[]>>("/api/v1/execution/venues");
  return response?.data ?? [];
}

export async function getExecutionMarkets(
  venue?: string,
  asset?: string,
): Promise<ExecutionMarketSummary[]> {
  const response = await request<ApiEnvelope<ExecutionMarketSummary[]>>("/api/v1/execution/markets", {
    venue,
    asset,
  });
  return response?.data ?? [];
}

export async function getExecutionAccounts(): Promise<ExecutionAccountSummary[]> {
  const response = await request<ApiEnvelope<ExecutionAccountSummary[]>>("/api/v1/execution/accounts");
  return response?.data ?? [];
}

export async function getExecutionOrders(params?: Record<string, string | number | null | undefined>): Promise<ExecutionOrderRecord[]> {
  const response = await request<ApiEnvelope<ExecutionOrderRecord[]>>("/api/v1/execution/orders", params);
  return response?.data ?? [];
}

export async function getExecutionFills(params?: Record<string, string | number | null | undefined>): Promise<ExecutionFillRecord[]> {
  const response = await request<ApiEnvelope<ExecutionFillRecord[]>>("/api/v1/execution/fills", params);
  return response?.data ?? [];
}

export async function getExecutionPositions(params?: Record<string, string | number | null | undefined>): Promise<ExecutionPositionRecord[]> {
  const response = await request<ApiEnvelope<ExecutionPositionRecord[]>>("/api/v1/execution/positions", params);
  return response?.data ?? [];
}

export async function getExecutionBalances(params?: Record<string, string | number | null | undefined>): Promise<ExecutionBalanceRecord[]> {
  const response = await request<ApiEnvelope<ExecutionBalanceRecord[]>>("/api/v1/execution/balances", params);
  return response?.data ?? [];
}

export async function getExecutionHealth(): Promise<ExecutionHealthRecord[]> {
  const response = await request<ApiEnvelope<ExecutionHealthRecord[]>>("/api/v1/execution/health");
  return response?.data ?? [];
}

export async function postExecutionQuote(
  body: Record<string, unknown>,
): Promise<ExecutionActionResponse | null> {
  const response = await postRequest<ApiEnvelope<ExecutionActionResponse>>("/api/v1/execution/quote", body);
  return response?.data ?? null;
}

export async function postExecutionOrder(
  body: Record<string, unknown>,
): Promise<ExecutionActionResponse | null> {
  const response = await postRequest<ApiEnvelope<ExecutionActionResponse>>("/api/v1/execution/order", body);
  return response?.data ?? null;
}

export async function postExecutionCancel(
  body: Record<string, unknown>,
): Promise<ExecutionActionResponse | null> {
  const response = await postRequest<ApiEnvelope<ExecutionActionResponse>>("/api/v1/execution/cancel", body);
  return response?.data ?? null;
}

export async function postExecutionCancelAll(
  body: Record<string, unknown>,
): Promise<ExecutionActionResponse | null> {
  const response = await postRequest<ApiEnvelope<ExecutionActionResponse>>("/api/v1/execution/cancel-all", body);
  return response?.data ?? null;
}

export async function getOptionsDashboards(): Promise<OptionsDashboardSummary[]> {
  const response = await request<ApiEnvelope<OptionsDashboardSummary[]>>("/api/v1/options/dashboards");
  return response?.data ?? [];
}

export async function getOptionsDashboardLatest(
  dashboardId: string,
  underlying?: string,
  signal?: AbortSignal,
): Promise<OptionsDashboardSummary | null> {
  const response = await request<ApiEnvelope<OptionsDashboardSummary>>(
    `/api/v1/options/${dashboardId}/latest`,
    { underlying },
    signal,
  );
  return response?.data ?? null;
}

export async function getOptionsDashboardSeries(
  dashboardId: string,
  underlying?: string,
  start?: string,
  end?: string,
  quality?: string,
  maxPoints?: number,
  signal?: AbortSignal,
): Promise<OptionsDashboardResponse | null> {
  const response = await request<ApiEnvelope<OptionsDashboardResponse>>(
    `/api/v1/options/${dashboardId}/series`,
    { underlying, start, end, quality, max_points: maxPoints },
    signal,
  );
  return response?.data ?? null;
}

export async function getOptionsSignals(): Promise<OptionsSignalSummary[]> {
  const response = await request<ApiEnvelope<OptionsSignalSummary[]>>("/api/v1/options/signals");
  return response?.data ?? [];
}

export async function getOptionsSignalLatest(
  signalId: string,
  underlying = "BTC",
  signal?: AbortSignal,
): Promise<OptionsSignalSummary | null> {
  const response = await request<ApiEnvelope<OptionsSignalSummary>>(
    `/api/v1/options/signals/${signalId}/latest`,
    { underlying },
    signal,
  );
  return response?.data ?? null;
}

export async function getOptionsSignalSeries(
  signalId: string,
  underlying = "BTC",
  start?: string,
  end?: string,
  quality?: string,
  maxPoints?: number,
): Promise<OptionsSignalSeriesResponse | null> {
  const response = await request<ApiEnvelope<OptionsSignalSeriesResponse>>(
    `/api/v1/options/signals/${signalId}/series`,
    { underlying, start, end, quality, max_points: maxPoints },
  );
  return response?.data ?? null;
}

export async function getPerpsDashboards(): Promise<PerpsDashboardSummary[]> {
  const response = await request<ApiEnvelope<PerpsDashboardSummary[]>>("/api/v1/perps/dashboards");
  return response?.data ?? [];
}

export async function getPerpsDashboardLatest(
  dashboardId: string,
  asset = "BTC",
  venueScope = "all",
  signal?: AbortSignal,
): Promise<PerpsDashboardSummary | null> {
  const response = await request<ApiEnvelope<PerpsDashboardSummary>>(
    `/api/v1/perps/${dashboardId}/latest`,
    { asset, venue_scope: venueScope },
    signal,
  );
  return response?.data ?? null;
}

export async function getPerpsDashboardSeries(
  dashboardId: string,
  asset = "BTC",
  venueScope = "all",
  start?: string,
  end?: string,
  quality?: string,
  maxPoints?: number,
  signal?: AbortSignal,
): Promise<PerpsDashboardResponse | null> {
  const response = await request<ApiEnvelope<PerpsDashboardResponse>>(
    `/api/v1/perps/${dashboardId}/series`,
    { asset, venue_scope: venueScope, start, end, quality, max_points: maxPoints },
    signal,
  );
  return response?.data ?? null;
}

export async function getPerpsSignals(): Promise<PerpsSignalSummary[]> {
  const response = await request<ApiEnvelope<PerpsSignalSummary[]>>("/api/v1/perps/signals");
  return response?.data ?? [];
}

export async function getPerpsSignalLatest(
  signalId: string,
  asset = "BTC",
  venueScope = "all",
  signal?: AbortSignal,
): Promise<PerpsSignalSummary | null> {
  const response = await request<ApiEnvelope<PerpsSignalSummary>>(
    `/api/v1/perps/signals/${signalId}/latest`,
    { asset, venue_scope: venueScope },
    signal,
  );
  return response?.data ?? null;
}

export async function getPerpsSignalSeries(
  signalId: string,
  asset = "BTC",
  venueScope = "all",
  start?: string,
  end?: string,
  quality?: string,
  maxPoints?: number,
  signal?: AbortSignal,
): Promise<PerpsSignalSeriesResponse | null> {
  const response = await request<ApiEnvelope<PerpsSignalSeriesResponse>>(
    `/api/v1/perps/signals/${signalId}/series`,
    { asset, venue_scope: venueScope, start, end, quality, max_points: maxPoints },
    signal,
  );
  return response?.data ?? null;
}

export async function getCompositeSignals(): Promise<CompositeSignalSummary[]> {
  const response = await request<ApiEnvelope<CompositeSignalSummary[]>>("/api/v1/composite/signals");
  return response?.data ?? [];
}

export async function getCompositeSignalLatest(
  signalId: string,
  signal?: AbortSignal,
): Promise<CompositeSignalSummary | null> {
  const response = await request<ApiEnvelope<CompositeSignalSummary>>(
    `/api/v1/composite/signals/${signalId}/latest`,
    undefined,
    signal,
  );
  return response?.data ?? null;
}

export async function getCompositeSignalSeries(
  signalId: string,
  start?: string,
  end?: string,
  quality?: string,
  maxPoints?: number,
  signal?: AbortSignal,
): Promise<CompositeSignalSeriesResponse | null> {
  const response = await request<ApiEnvelope<CompositeSignalSeriesResponse>>(
    `/api/v1/composite/signals/${signalId}/series`,
    { start, end, quality, max_points: maxPoints },
    signal,
  );
  return response?.data ?? null;
}

export async function getCompositeDecomposition(
  signalId: string,
  start?: string,
  end?: string,
  quality?: string,
  maxPoints?: number,
  signal?: AbortSignal,
): Promise<CompositeDecompositionResponse | null> {
  const response = await request<ApiEnvelope<CompositeDecompositionResponse>>(
    `/api/v1/composite/decomposition/${signalId}`,
    { start, end, quality, max_points: maxPoints },
    signal,
  );
  return response?.data ?? null;
}

export async function getCompositeTransitions(
  signalId: string,
  start?: string,
  end?: string,
  quality?: string,
  maxPoints?: number,
  signal?: AbortSignal,
): Promise<CompositeTransitionResponse | null> {
  const response = await request<ApiEnvelope<CompositeTransitionResponse>>(
    `/api/v1/composite/transitions/${signalId}`,
    { start, end, quality, max_points: maxPoints },
    signal,
  );
  return response?.data ?? null;
}

export async function getMicrostructureSignals(): Promise<MicrostructureSignalSummary[]> {
  const response = await request<ApiEnvelope<MicrostructureSignalSummary[]>>("/api/v1/microstructure/signals");
  return response?.data ?? [];
}

export async function getMicrostructureSignalLatest(
  signalId: string,
): Promise<MicrostructureSignalSummary | null> {
  const response = await request<ApiEnvelope<MicrostructureSignalSummary>>(
    `/api/v1/microstructure/signals/${signalId}/latest`,
  );
  return response?.data ?? null;
}

export async function getMicrostructureSignalSeries(
  signalId: string,
  start?: string,
  end?: string,
  quality?: string,
  signal?: AbortSignal,
  maxPoints?: number,
): Promise<MicrostructureSignalSeriesResponse | null> {
  const response = await request<ApiEnvelope<MicrostructureSignalSeriesResponse>>(
    `/api/v1/microstructure/signals/${signalId}/series`,
    { start, end, quality, max_points: maxPoints },
    signal,
  );
  return response?.data ?? null;
}

export async function getMicrostructureFeatures(
  asset: string,
  horizon = "1m",
  start?: string,
  end?: string,
  quality?: string,
  maxPoints?: number,
  signal?: AbortSignal,
): Promise<MicrostructureFeatureResponse | null> {
  const response = await request<ApiEnvelope<MicrostructureFeatureResponse>>(
    `/api/v1/microstructure/features/${asset}`,
    { horizon, start, end, quality, max_points: maxPoints },
    signal,
  );
  return response?.data ?? null;
}

export async function getMicrostructureRegime(
  asset: string,
  start?: string,
  end?: string,
  quality?: string,
  maxPoints?: number,
  signal?: AbortSignal,
): Promise<MicrostructureRegimeResponse | null> {
  const response = await request<ApiEnvelope<MicrostructureRegimeResponse>>(
    `/api/v1/microstructure/regime/${asset}`,
    { start, end, quality, max_points: maxPoints },
    signal,
  );
  return response?.data ?? null;
}

export async function getMicrostructureHealth(
  signal?: AbortSignal,
): Promise<MicrostructureHealth | null> {
  const response = await request<ApiEnvelope<MicrostructureHealth>>(
    "/api/v1/microstructure/health",
    undefined,
    signal,
  );
  return response?.data ?? null;
}

export async function getPrices(signal?: AbortSignal): Promise<PriceSummary[]> {
  const response = await request<ApiEnvelope<PriceSummary[]>>("/api/v1/prices", undefined, signal);
  return response?.data ?? [];
}

export async function getPriceSeries(
  asset = "BTC",
  venueScope = "all",
  timeframe = "15m",
  start?: string,
  end?: string,
  maxPoints = 500,
  signal?: AbortSignal,
): Promise<PriceSeriesResponse | null> {
  const response = await request<ApiEnvelope<PriceSeriesResponse>>(
    "/api/v1/prices/series",
    {
      asset,
      venue_scope: venueScope,
      timeframe,
      start,
      end,
      max_points: maxPoints,
    },
    signal,
  );
  return response?.data ?? null;
}
