import path from 'node:path'
import process from 'node:process'

import type { AdminDefaultUser, RuntimeConfig } from '../types/runtime-config.js'
import { parseBoolean, parseInteger, parseJsonArray } from './runtime-env.js'
import { deepMerge } from './runtime-merge.js'

type RuntimeConfigPath = readonly [string, ...string[]]
type RuntimeConfigValue = string | number | boolean | AdminDefaultUser[]
type RuntimeEnv = Record<string, string | undefined>

type EnvOverride = {
  env: string
  path: RuntimeConfigPath
  read: (rawValue: unknown) => RuntimeConfigValue | null
}

export const ENV_OVERRIDES: readonly EnvOverride[] = [
  integerEnv('PORT', ['server', 'port']),
  stringEnv('CHROME_PATH', ['browser', 'chromePath']),
  booleanEnv('TENCENT_BROWSER_HEADLESS', ['browser', 'headless']),
  booleanEnv('TENCENT_BROWSER_DEVTOOLS', ['browser', 'devtools']),
  booleanEnv('TENCENT_BROWSER_KEEP_ALIVE', ['browser', 'keepAlive']),
  booleanEnv('TENCENT_BROWSER_PREWARM', ['browser', 'prewarm']),
  integerEnv('TENCENT_BROWSER_SLOW_MO', ['browser', 'slowMoMs']),
  stringEnv('TENCENT_BROWSER_VNC_RESOLUTION', ['browser', 'vncResolution']),
  booleanEnv('TENCENT_SESSION_DEBUG', ['session', 'debug']),
  stringEnv('OCR_BASE_URL', ['ocr', 'baseUrl']),
  stringEnv('DATA_ROOT', ['data', 'root'], path.resolve),
  stringEnv('LOG_LEVEL', ['logging', 'level']),
  stringEnv('DATABASE_URL', ['database', 'url']),
  booleanEnv('DATABASE_SSL', ['database', 'ssl']),
  integerEnv('DATABASE_MAX_CONNECTIONS', ['database', 'maxConnections']),
  integerEnv('CLAIM_TOKEN_TTL_HOURS', ['orders', 'tokenTtlHours']),
  stringEnv('ADMIN_SESSION_SECRET', ['admin', 'sessionSecret']),
  integerEnv('ADMIN_SESSION_TTL_HOURS', ['admin', 'sessionTtlHours']),
  adminUsersJsonEnv('ADMIN_DEFAULT_USERS_JSON', ['admin', 'defaultUsers']),
  stringEnv('AGISO_APP_SECRET', ['platforms', 'agiso', 'appSecret']),
  stringEnv('AGISO_TRADE_DETAIL_ENDPOINT', ['platforms', 'agiso', 'tradeDetail', 'endpoint']),
  stringEnv('AGISO_TRADE_DETAIL_API_VERSION', ['platforms', 'agiso', 'tradeDetail', 'apiVersion']),
  integerEnv('AGISO_TRADE_DETAIL_TIMEOUT_MS', ['platforms', 'agiso', 'tradeDetail', 'timeoutMs']),
  booleanEnv('AGISO_AUTO_DELIVERY_ENABLED', ['platforms', 'agiso', 'autoDelivery', 'enabled']),
  stringEnv('AGISO_AUTO_DELIVERY_ENDPOINT', ['platforms', 'agiso', 'autoDelivery', 'endpoint']),
  stringEnv('AGISO_AUTO_DELIVERY_API_VERSION', ['platforms', 'agiso', 'autoDelivery', 'apiVersion']),
  stringEnv('AGISO_MESSAGE_API_VERSION', ['platforms', 'agiso', 'messaging', 'apiVersion']),
  stringEnv('AGISO_SEND_MESSAGE_ENDPOINT', ['platforms', 'agiso', 'messaging', 'sendMessageEndpoint']),
  booleanEnv('AGISO_MESSAGING_ENABLED', ['platforms', 'agiso', 'messaging', 'enabled']),
  stringEnv('NINETYONE_USER_ID', ['platforms', 'ninetyone', 'userId']),
  stringEnv('NINETYONE_SECRET', ['platforms', 'ninetyone', 'secret']),
  stringEnv('NINETYONE_VERSION', ['platforms', 'ninetyone', 'version']),
  stringEnv('NINETYONE_SHOP_ID', ['platforms', 'ninetyone', 'shopId']),
  stringEnv('NINETYONE_SHOP_NAME', ['platforms', 'ninetyone', 'shopName']),
  integerEnv('NINETYONE_TIMESTAMP_TOLERANCE_SECONDS', ['platforms', 'ninetyone', 'timestampToleranceSeconds']),
  stringEnv('NINETYONE_CARDS_ENCODING', ['platforms', 'ninetyone', 'cardsEncoding']),
  stringEnv('CLOUDTENTACLES_BASE_URL', ['platforms', 'cloudtentacles', 'baseUrl']),
  integerEnv('CLOUDTENTACLES_TIMEOUT_MS', ['platforms', 'cloudtentacles', 'timeoutMs']),
  stringEnv('CLOUDTENTACLES_SEND_SMS_PATH', ['platforms', 'cloudtentacles', 'sendSmsPath']),
  stringEnv('CLOUDTENTACLES_LOGIN_PATH', ['platforms', 'cloudtentacles', 'loginPath']),
  stringEnv('CLOUDTENTACLES_USER_INFO_PATH', ['platforms', 'cloudtentacles', 'userInfoPath']),
  stringEnv('CLOUDTENTACLES_ASSET_PATH', ['platforms', 'cloudtentacles', 'assetPath']),
  stringEnv('CLOUDTENTACLES_PERMISSION_PATH', ['platforms', 'cloudtentacles', 'permissionPath']),
  stringEnv('CLOUDTENTACLES_CATEGORIES_PATH', ['platforms', 'cloudtentacles', 'categoriesPath']),
  stringEnv('CLOUDTENTACLES_SKU_LIST_PATH', ['platforms', 'cloudtentacles', 'skuListPath']),
  stringEnv('CLOUDTENTACLES_SKU_BUY_PATH', ['platforms', 'cloudtentacles', 'skuBuyPath']),
  stringEnv('CLOUDTENTACLES_KNAPSACK_PATH', ['platforms', 'cloudtentacles', 'knapsackPath']),
  stringEnv('CLOUDTENTACLES_VN_LIST_PATH', ['platforms', 'cloudtentacles', 'vnListPath']),
  stringEnv('CLOUDTENTACLES_VN_APPOINT_PATH', ['platforms', 'cloudtentacles', 'vnAppointPath']),
  stringEnv('CLOUDTENTACLES_VN_GENERATE_LOGIN_CODE_PATH', ['platforms', 'cloudtentacles', 'vnGenerateLoginCodePath']),
  stringEnv('CLOUDTENTACLES_VN_VERIF_CODE_PATH', ['platforms', 'cloudtentacles', 'vnVerifCodePath']),
  stringEnv('CLOUDTENTACLES_VN_VERIFY_LOGIN_CODE_PATH', ['platforms', 'cloudtentacles', 'vnVerifyLoginCodePath']),
  stringEnv('CLOUDTENTACLES_VN_BIND_URL_PATH', ['platforms', 'cloudtentacles', 'vnBindUrlPath']),
  stringEnv('CLOUDTENTACLES_VN_BIND_INFO_PATH', ['platforms', 'cloudtentacles', 'vnBindInfoPath']),
  stringEnv('CLOUDTENTACLES_VN_BACK_PATH', ['platforms', 'cloudtentacles', 'vnBackPath']),
  integerEnv('CLOUDTENTACLES_BIND_URL_TTL_SECONDS', ['platforms', 'cloudtentacles', 'bindUrlTtlSeconds']),
  integerEnv('CLOUDTENTACLES_BIND_URL_PROBE_INTERVAL_SECONDS', ['platforms', 'cloudtentacles', 'bindUrlProbeIntervalSeconds']),
  integerEnv('CLOUDTENTACLES_BIND_URL_PROBE_TIMEOUT_MS', ['platforms', 'cloudtentacles', 'bindUrlProbeTimeoutMs']),
  stringEnv('CLOUDTENTACLES_BIND_URL_PROBE_USER_AGENT', ['platforms', 'cloudtentacles', 'bindUrlProbeUserAgent']),
  stringEnv('CLOUDTENTACLES_BIND_URL_PROBE_ENDPOINT', ['platforms', 'cloudtentacles', 'bindUrlProbeEndpoint']),
  stringEnv('CLOUDTENTACLES_BIND_URL_PROBE_CHART_ID', ['platforms', 'cloudtentacles', 'bindUrlProbeChartId']),
  stringEnv('CLOUDTENTACLES_BIND_URL_PROBE_SUB_CHART_ID', ['platforms', 'cloudtentacles', 'bindUrlProbeSubChartId']),
  stringEnv('CLOUDTENTACLES_BIND_URL_PROBE_IDE_TOKEN', ['platforms', 'cloudtentacles', 'bindUrlProbeIdeToken']),
  stringEnv('CLOUDTENTACLES_BIND_URL_PROBE_ACTIVITY_URL', ['platforms', 'cloudtentacles', 'bindUrlProbeActivityUrl']),
  stringEnv('CLOUDTENTACLES_BIND_URL_PROBE_REFERER', ['platforms', 'cloudtentacles', 'bindUrlProbeReferer']),
  stringEnv('CLOUDTENTACLES_BIND_URL_PROBE_EXTRA_COOKIE', ['platforms', 'cloudtentacles', 'bindUrlProbeExtraCookie']),
  stringEnv('CLOUDTENTACLES_PUBLIC_KEY_PEM', ['platforms', 'cloudtentacles', 'publicKeyPem']),
  stringEnv('CLOUDTENTACLES_CLIENT_SOURCE', ['platforms', 'cloudtentacles', 'clientSource']),
  stringEnv('CLOUDTENTACLES_DEVICE_ID', ['platforms', 'cloudtentacles', 'deviceId']),
  integerEnv('CLOUDTENTACLES_DEVICE_TYPE', ['platforms', 'cloudtentacles', 'deviceType']),
  stringEnv('TENCENT_REDEEM_PROOF_MODE', ['redeem', 'proofMode']),
]

export function applyEnvOverrides(baseConfig: RuntimeConfig, env: RuntimeEnv = process.env): RuntimeConfig {
  const nextConfig = deepMerge(baseConfig, {})

  applyClaimBaseUrlOverride(nextConfig, env)

  for (const override of ENV_OVERRIDES) {
    const value = override.read(env[override.env])
    if (value !== null) {
      setConfigValue(nextConfig, override.path, value)
    }
  }

  return nextConfig
}

function applyClaimBaseUrlOverride(config: RuntimeConfig, env: RuntimeEnv): void {
  const claimBaseUrl = parseString(env.CLAIM_BASE_URL)
  if (claimBaseUrl !== null) {
    config.orders.claimBaseUrl = claimBaseUrl
    return
  }

  const appBaseUrl = parseString(env.APP_BASE_URL)
  if (appBaseUrl !== null) {
    config.orders.claimBaseUrl = `${appBaseUrl.replace(/\/+$/, '')}/#/claim`
  }
}

function stringEnv(
  env: string,
  configPath: RuntimeConfigPath,
  transform: (value: string) => string = (value) => value,
): EnvOverride {
  return {
    env,
    path: configPath,
    read(rawValue) {
      const value = parseString(rawValue)
      return value === null ? null : transform(value)
    },
  }
}

function integerEnv(env: string, configPath: RuntimeConfigPath): EnvOverride {
  return {
    env,
    path: configPath,
    read: parseInteger,
  }
}

function booleanEnv(env: string, configPath: RuntimeConfigPath): EnvOverride {
  return {
    env,
    path: configPath,
    read: parseBoolean,
  }
}

function adminUsersJsonEnv(env: string, configPath: RuntimeConfigPath): EnvOverride {
  return {
    env,
    path: configPath,
    read(rawValue) {
      return parseJsonArray<AdminDefaultUser>(rawValue)
    },
  }
}

function parseString(rawValue: unknown): string | null {
  const value = String(rawValue || '').trim()
  return value ? value : null
}

function setConfigValue(config: RuntimeConfig, configPath: RuntimeConfigPath, value: RuntimeConfigValue): void {
  let current: Record<string, unknown> = config as unknown as Record<string, unknown>

  for (const key of configPath.slice(0, -1)) {
    const nextValue = current[key]
    if (!nextValue || typeof nextValue !== 'object' || Array.isArray(nextValue)) {
      current[key] = {}
    }
    current = current[key] as Record<string, unknown>
  }

  current[configPath[configPath.length - 1]] = value
}
