import 'dotenv/config'

import type { BybitCategory } from './types.js'

const VALID_EXCHANGES = ['bybit', 'binance', 'okx', 'coinbase', 'kraken', 'hyperliquid', 'dydx'] as const
const VALID_CATEGORIES: BybitCategory[] = ['spot', 'linear', 'inverse']

export type SupportedExchangeToken = (typeof VALID_EXCHANGES)[number]

export interface AppConfig {
  demoApiKey?: string
  demoApiSecret?: string
  testnetApiKey?: string
  testnetApiSecret?: string
  useTestnet: boolean
  recvWindow: number
  symbolPrefix: string
  publicCategories: BybitCategory[]
  enabledExchanges: SupportedExchangeToken[]
  binanceIncludeFutures: boolean
  subscriptionChunkSize: number
  signalFastWindowMs: number
  signalSlowWindowMs: number
  signalEmitIntervalMs: number
  signalLargeTradeUsd: number
  defaultCategory: BybitCategory
  defaultSymbol: string
}

function parseBoolean(value: string | undefined, fallback: boolean): boolean {
  if (value == null || value === '') {
    return fallback
  }

  return ['1', 'true', 'yes', 'on'].includes(value.toLowerCase())
}

function parseNumber(value: string | undefined, fallback: number): number {
  const parsed = Number(value)
  return Number.isFinite(parsed) ? parsed : fallback
}

function parseCategories(value: string | undefined): BybitCategory[] {
  const selected = (value ?? 'spot,linear,inverse')
    .split(',')
    .map(entry => entry.trim().toLowerCase())
    .filter(Boolean)
    .filter((entry): entry is BybitCategory => VALID_CATEGORIES.includes(entry as BybitCategory))

  return selected.length > 0 ? selected : [...VALID_CATEGORIES]
}

function parseEnabledExchanges(value: string | undefined): SupportedExchangeToken[] {
  const selected = (value ?? VALID_EXCHANGES.join(','))
    .split(',')
    .map(entry => entry.trim().toLowerCase())
    .filter(Boolean)
    .filter((entry): entry is SupportedExchangeToken => VALID_EXCHANGES.includes(entry as SupportedExchangeToken))

  return selected.length > 0 ? selected : [...VALID_EXCHANGES]
}

function parseCategory(value: string | undefined, fallback: BybitCategory): BybitCategory {
  if (value && VALID_CATEGORIES.includes(value as BybitCategory)) {
    return value as BybitCategory
  }

  return fallback
}

export function loadConfig(): AppConfig {
  return {
    demoApiKey: process.env.BYBIT_DEMO_API_KEY || undefined,
    demoApiSecret: process.env.BYBIT_DEMO_API_SECRET || undefined,
    testnetApiKey: process.env.BYBIT_TESTNET_API_KEY || undefined,
    testnetApiSecret: process.env.BYBIT_TESTNET_API_SECRET || undefined,
    useTestnet: parseBoolean(process.env.BYBIT_TESTNET, false),
    recvWindow: parseNumber(process.env.BYBIT_RECV_WINDOW, 5000),
    symbolPrefix: (process.env.BYBIT_SYMBOL_PREFIX || 'BTC').toUpperCase(),
    publicCategories: parseCategories(process.env.BYBIT_PUBLIC_CATEGORIES),
    enabledExchanges: parseEnabledExchanges(process.env.ENABLED_EXCHANGES ?? process.env.ENABLED_VENUES),
    binanceIncludeFutures: parseBoolean(process.env.BINANCE_INCLUDE_FUTURES, true),
    subscriptionChunkSize: Math.max(1, Math.floor(parseNumber(process.env.BYBIT_SUBSCRIPTION_CHUNK_SIZE, 10))),
    signalFastWindowMs: Math.max(1000, Math.floor(parseNumber(process.env.SIGNAL_FAST_WINDOW_MS, 5000))),
    signalSlowWindowMs: Math.max(5000, Math.floor(parseNumber(process.env.SIGNAL_SLOW_WINDOW_MS, 60000))),
    signalEmitIntervalMs: Math.max(1000, Math.floor(parseNumber(process.env.SIGNAL_EMIT_INTERVAL_MS, 5000))),
    signalLargeTradeUsd: Math.max(1, parseNumber(process.env.SIGNAL_LARGE_TRADE_USD, 250000)),
    defaultCategory: parseCategory(process.env.BYBIT_DEFAULT_CATEGORY, 'linear'),
    defaultSymbol: (process.env.BYBIT_DEFAULT_SYMBOL || 'BTCUSDT').toUpperCase()
  }
}
