import * as dotenv from 'dotenv';
import logger from '../utils/logger.js';
import { listDynamicConfigEntries } from '../services/dynamicConfigRepository.js';

dotenv.config({ override: true });

export const config = {
    PRODUCT_ID: process.env.PRODUCT_ID || 'invttrdg',
    PLATFORM_API_URL: process.env.PLATFORM_API_URL || 'http://localhost:4003/api',
    PLATFORM_AUTH_ENABLED: process.env.PLATFORM_AUTH_ENABLED !== 'false',
    PLATFORM_JWT_ISSUER: process.env.PLATFORM_JWT_ISSUER || 'bytelyst-platform',
    PLATFORM_JWT_PUBLIC_KEY: process.env.PLATFORM_JWT_PUBLIC_KEY || '',
    PLATFORM_JWT_JWKS_URL: process.env.PLATFORM_JWT_JWKS_URL || '',
    JWT_SECRET: process.env.JWT_SECRET || '',
    COSMOS_ENDPOINT: process.env.COSMOS_ENDPOINT || '',
    COSMOS_KEY: process.env.COSMOS_KEY || '',
    COSMOS_DATABASE: process.env.COSMOS_DATABASE || 'invttrdg',

    // Plug-and-Play Provider Selection
    PROVIDER: process.env.PROVIDER || 'alpaca',
    DATA_PROVIDER: process.env.DATA_PROVIDER || process.env.PROVIDER || 'alpaca',
    EXECUTION_PROVIDER: process.env.EXECUTION_PROVIDER || process.env.PROVIDER || 'alpaca',

    // Asset Details
    SYMBOL: process.env.SYMBOL || 'BTC/USD', // Legacy single symbol
    SYMBOLS: (process.env.SYMBOLS || process.env.SYMBOL || 'BTC/USD').split(',').map(s => s.trim()), // Multi-asset support
    TIMEFRAME: process.env.TIMEFRAME || '1Min',
    POLLING_INTERVAL: parseInt(process.env.POLLING_INTERVAL || '60000', 10),

    // Alpaca Specific
    ALPACA_API_KEY: (process.env.ALPACA_API_KEY === 'your_key' ? '' : process.env.ALPACA_API_KEY) || '',
    ALPACA_API_SECRET: (process.env.ALPACA_API_SECRET === 'your_secret' ? '' : process.env.ALPACA_API_SECRET) || '',
    PAPER_TRADING: process.env.PAPER_TRADING === 'true',
    ASSET_CLASS: (process.env.ASSET_CLASS || 'crypto') as 'crypto' | 'us_equity',

    // CCXT Specific
    EXCHANGE: process.env.EXCHANGE || 'binance',
    CCXT_API_KEY: (process.env.CCXT_API_KEY === 'your_key' ? '' : process.env.CCXT_API_KEY) || '',
    CCXT_API_SECRET: (process.env.CCXT_API_SECRET === 'your_secret' ? '' : process.env.CCXT_API_SECRET) || '',

    // Notifications
    WEBHOOK_URL: process.env.WEBHOOK_URL || '',
    NOTIFICATION_PHONE_NUMBERS: (process.env.NOTIFICATION_PHONE_NUMBERS || '').split(',').map(s => s.trim()).filter(Boolean),
    NOTIFICATION_API_HOST: process.env.NOTIFICATION_API_HOST || 'www.zenhustles.com',
    NOTIFICATION_API_PATH: process.env.NOTIFICATION_API_PATH || '/api/whatsapp/send',

    // Server
    API_PORT: parseInt(process.env.PORT || process.env.API_PORT || '4018', 10),
    ALLOWED_ORIGINS: (process.env.CORS_ALLOWED_ORIGINS
        || process.env.ALLOWED_ORIGINS
        || 'http://localhost:3048,http://localhost:5173,http://localhost:8081')
        .split(',')
        .map(s => s.trim())
        .filter(Boolean),

    // Supabase
    SUPABASE_URL: process.env.SUPABASE_URL || '',
    SUPABASE_KEY: process.env.SUPABASE_KEY || process.env.SUPABASE_ANON_KEY || '',
    SUPABASE_JWT_ISSUER: process.env.SUPABASE_JWT_ISSUER || '',
    SUPABASE_JWT_AUDIENCE: process.env.SUPABASE_JWT_AUDIENCE || '',
    SNAPSHOT_USER_ID: process.env.SNAPSHOT_USER_ID || '',

    // AI Configuration (Phase 2.3)
    AI: {
        PROVIDER: process.env.AI_PROVIDER || 'openai', // Default/Primary provider
        OPENAI_API_KEY: process.env.OPENAI_API_KEY || process.env.AI_API_KEY || '',
        GEMINI_API_KEY: process.env.GEMINI_API_KEY || process.env.AI_API_KEY || '',
        PERPLEXITY_API_KEY: process.env.PERPLEXITY_API_KEY || '',
        MODEL: process.env.AI_MODEL || 'gpt-4o', // Default model for primary provider
        CONFIDENCE_THRESHOLD: parseInt(process.env.AI_CONFIDENCE_THRESHOLD || '70', 10),
        FALLBACK_LIST: (process.env.AI_FALLBACK_LIST || 'openai,perplexity,gemini').split(',').map(s => s.trim().toLowerCase()),
        CACHE_HOURS: parseInt(process.env.AI_CACHE_HOURS || '4', 10),
        FAIL_OPEN: process.env.AI_FAIL_OPEN !== 'false',
    },

    // Low-Stress Mode (Toggleable Feature)
    LOW_STRESS_MODE: process.env.LOW_STRESS_MODE === 'true',

    // Alert Toggles
    ENABLE_TREND_ALERTS: process.env.ENABLE_TREND_ALERTS !== 'false', // Default to true
    ENABLE_PULSE_ALERTS: process.env.ENABLE_PULSE_ALERTS !== 'false', // Default to true

    // Execution / Trading (Phase 5)
    ENABLE_TRADING: process.env.ENABLE_TRADING === 'true',
    TOTAL_CAPITAL: parseFloat(process.env.TOTAL_CAPITAL || '1000'),
    MAX_OPEN_TRADES: parseInt(process.env.MAX_OPEN_TRADES || '3', 10),
    MAX_OPEN_TRADES_PER_ACCOUNT: parseInt(process.env.MAX_OPEN_TRADES_PER_ACCOUNT || '6', 10),
    COOLDOWN_MS: parseInt(process.env.COOLDOWN_MS || '3600000', 10),  // Default 1 hour
    PROFIT_EXIT_PERCENT: parseFloat(process.env.PROFIT_EXIT_PERCENT || '1.0'),  // Default 1%
    TRAILING_STOP_PERCENT: parseFloat(process.env.TRAILING_STOP_PERCENT || '0.001'),  // Default 0.1%
    PROFILE_SYNC_INTERVAL_MS: parseInt(process.env.PROFILE_SYNC_INTERVAL_MS || '60000', 10),  // Default 1 min
    MONITOR_INTERVAL_MS: parseInt(process.env.MONITOR_INTERVAL_MS || '60000', 10),  // Default 1 min
    ORDER_SYNC_INTERVAL_MS: parseInt(process.env.ORDER_SYNC_INTERVAL_MS || '60000', 10),  // Default 1 min
    STALE_ORDER_THRESHOLD_MINUTES: parseInt(process.env.STALE_ORDER_THRESHOLD_MINUTES || '2', 10),  // Default 2 min
    ORDER_SYNC_MISSING_GRACE_MINUTES: parseInt(process.env.ORDER_SYNC_MISSING_GRACE_MINUTES || '5', 10),
    ORDER_SYNC_MISSING_CONFIRMATION_COUNT: parseInt(process.env.ORDER_SYNC_MISSING_CONFIRMATION_COUNT || '2', 10),
    ORDER_SYNC_RECENT_CLOSED_LOOKBACK_MINUTES: parseInt(process.env.ORDER_SYNC_RECENT_CLOSED_LOOKBACK_MINUTES || '30', 10),
    SYMBOL_DELAY_MS: parseInt(process.env.SYMBOL_DELAY_MS || '2000', 10),  // Delay between symbols
    ENABLE_BACKTEST: process.env.ENABLE_BACKTEST === 'true',
    BACKTEST_CUSTOMER_ENABLED: process.env.BACKTEST_CUSTOMER_ENABLED === 'true',
    BACKTEST_MAX_CSV_BYTES: parseInt(process.env.BACKTEST_MAX_CSV_BYTES || '5242880', 10), // 5MB
    BACKTEST_MAX_ROWS: parseInt(process.env.BACKTEST_MAX_ROWS || '200000', 10),
    CAPITAL_WATCHDOG_INTERVAL_MS: parseInt(process.env.CAPITAL_WATCHDOG_INTERVAL_MS || '60000', 10),  // Default 1 min
    DB_SNAPSHOT_INTERVAL_MS: parseInt(process.env.DB_SNAPSHOT_INTERVAL_MS || '300000', 10), // Default 5 min
    ENABLE_DB_SNAPSHOTS: process.env.ENABLE_DB_SNAPSHOTS !== 'false', // Default true
    ACCOUNT_SNAPSHOT_INTERVAL_MS: parseInt(process.env.ACCOUNT_SNAPSHOT_INTERVAL_MS || '60000', 10), // Default 1 min
    DYNAMIC_CONFIG_REFRESH_MS: parseInt(process.env.DYNAMIC_CONFIG_REFRESH_MS || '60000', 10), // Default 1 min
    EXCHANGE_STATE_MISMATCH_THROTTLE_MS: parseInt(process.env.EXCHANGE_STATE_MISMATCH_THROTTLE_MS || '300000', 10), // 5 min
    REQUIRE_EXCHANGE_FILL_EVIDENCE_FOR_AUTO_CLOSE: process.env.REQUIRE_EXCHANGE_FILL_EVIDENCE_FOR_AUTO_CLOSE !== 'false',

    // Order Execution Safety
    ORDER_POLL_INTERVAL_MS: parseInt(process.env.ORDER_POLL_INTERVAL_MS || '3000', 10),  // Poll every 3s
    ORDER_POLL_MAX_ATTEMPTS: parseInt(process.env.ORDER_POLL_MAX_ATTEMPTS || '10', 10),  // Max 10 polls (30s)
    LIMIT_ORDER_TIMEOUT_MS: parseInt(process.env.LIMIT_ORDER_TIMEOUT_MS || '300000', 10),  // 5 min timeout
    MAX_SLIPPAGE_PERCENT: parseFloat(process.env.MAX_SLIPPAGE_PERCENT || '1.0'),  // 1% max slippage
    ENABLE_AUTO_PAUSE_ON_SLIPPAGE_BREACH: process.env.ENABLE_AUTO_PAUSE_ON_SLIPPAGE_BREACH !== 'false',
    MIN_POSITION_QTY: parseFloat(process.env.MIN_POSITION_QTY || '0.0001'),
    MAX_POSITION_QTY: parseFloat(process.env.MAX_POSITION_QTY || '1000000'),
    QUANTITY_PRECISION: parseInt(process.env.QUANTITY_PRECISION || '6', 10),
    MIN_NOTIONAL_USD: parseFloat(process.env.MIN_NOTIONAL_USD || '10'),
    MAX_NOTIONAL_USD: parseFloat(process.env.MAX_NOTIONAL_USD || '100000'),
    CAPITAL_RESERVE_PERCENT: parseFloat(process.env.CAPITAL_RESERVE_PERCENT || '0'),
    ENTRY_CAPITAL_BUFFER_PCT: parseFloat(process.env.ENTRY_CAPITAL_BUFFER_PCT || '0.25'),
    ENABLE_STRICT_CAPITAL_GUARD: process.env.ENABLE_STRICT_CAPITAL_GUARD !== 'false',
    STRICT_CAPITAL_SLIPPAGE_BUFFER_PCT: parseFloat(process.env.STRICT_CAPITAL_SLIPPAGE_BUFFER_PCT || process.env.MAX_SLIPPAGE_PERCENT || '1.0'),
    STRICT_CAPITAL_FEE_BUFFER_PCT: parseFloat(process.env.STRICT_CAPITAL_FEE_BUFFER_PCT || '0.15'),
    STRICT_CAPITAL_MIN_RESERVE_USD: parseFloat(process.env.STRICT_CAPITAL_MIN_RESERVE_USD || '0'),
    ENTRY_AUTO_REDUCE_ALERT_MIN_PCT: parseFloat(process.env.ENTRY_AUTO_REDUCE_ALERT_MIN_PCT || '0.05'),
    ENTRY_AUTO_REDUCE_ALERT_MIN_USD: parseFloat(process.env.ENTRY_AUTO_REDUCE_ALERT_MIN_USD || '25'),
    ENTRY_AUTO_REDUCE_ALERT_THROTTLE_MS: parseInt(process.env.ENTRY_AUTO_REDUCE_ALERT_THROTTLE_MS || '1800000', 10),
    CAPITAL_INVARIANT_EPSILON_USD: parseFloat(process.env.CAPITAL_INVARIANT_EPSILON_USD || '2'),
    CAPITAL_INVARIANT_EPSILON_PCT: parseFloat(process.env.CAPITAL_INVARIANT_EPSILON_PCT || '0.00005'),
    CAPITAL_INVARIANT_ALERT_THROTTLE_MS: parseInt(process.env.CAPITAL_INVARIANT_ALERT_THROTTLE_MS || '600000', 10),
    CAPITAL_LEDGER_DRIFT_ALERT_PCT: parseFloat(process.env.CAPITAL_LEDGER_DRIFT_ALERT_PCT || '10'),
    CAPITAL_LEDGER_DRIFT_MIN_USD: parseFloat(process.env.CAPITAL_LEDGER_DRIFT_MIN_USD || '10'),
    CAPITAL_LEDGER_DRIFT_SCOPE: String(process.env.CAPITAL_LEDGER_DRIFT_SCOPE || 'auto').trim().toLowerCase(),
    OPERATIONAL_EVENTS_MAX_BUFFER: parseInt(process.env.OPERATIONAL_EVENTS_MAX_BUFFER || '2000', 10),

    // Alpaca omnibus sub-tagging
    ENABLE_ALPACA_SUBTAG: process.env.ENABLE_ALPACA_SUBTAG !== 'false',
    SUBTAG_OMNIBUS_ONLY: process.env.SUBTAG_OMNIBUS_ONLY === 'true',
    ALPACA_SUBTAG_ENV: process.env.ALPACA_SUBTAG_ENV || '',
    ALPACA_SUBTAG_MAX_LENGTH: parseInt(process.env.ALPACA_SUBTAG_MAX_LENGTH || '48', 10),
    ALPACA_SUBTAG_DISABLE_FOR_EXCHANGE: (process.env.ALPACA_SUBTAG_DISABLE_FOR_EXCHANGE || '')
        .split(',')
        .map(s => s.trim())
        .filter(Boolean),
    ALPACA_OMNIBUS_PROFILE_ALLOWLIST: (process.env.ALPACA_OMNIBUS_PROFILE_ALLOWLIST || '')
        .split(',')
        .map(s => s.trim())
        .filter(Boolean),

    // Reconciliation EXIT Backfill (Data-only safety path)
    ENABLE_RECON_EXIT_BACKFILL: process.env.ENABLE_RECON_EXIT_BACKFILL !== 'false',
    RECON_EXIT_BACKFILL_DRY_RUN: process.env.RECON_EXIT_BACKFILL_DRY_RUN === 'true',
    RECON_EXIT_BACKFILL_REQUIRE_PAUSE: process.env.RECON_EXIT_BACKFILL_REQUIRE_PAUSE !== 'false',
    RECON_EXIT_BACKFILL_DUST_ABS_QTY: parseFloat(process.env.RECON_EXIT_BACKFILL_DUST_ABS_QTY || '0.001'),
    RECON_EXIT_BACKFILL_DUST_REL_PCT: parseFloat(process.env.RECON_EXIT_BACKFILL_DUST_REL_PCT || '0.002'),
    RECON_EXIT_BACKFILL_LOOKBACK_HOURS: parseInt(process.env.RECON_EXIT_BACKFILL_LOOKBACK_HOURS || '72', 10),
    RECON_EXIT_BACKFILL_REQUIRE_STRONG_ATTRIBUTION: process.env.RECON_EXIT_BACKFILL_REQUIRE_STRONG_ATTRIBUTION !== 'false',
    RECON_EXIT_BACKFILL_ALLOW_HEURISTIC_MATCH: process.env.RECON_EXIT_BACKFILL_ALLOW_HEURISTIC_MATCH === 'true',
    RECON_EXIT_BACKFILL_FILL_AFTER_TRADE_GRACE_MINUTES: parseInt(process.env.RECON_EXIT_BACKFILL_FILL_AFTER_TRADE_GRACE_MINUTES || '5', 10),
    RECON_EXIT_BACKFILL_PROFILE_ALLOWLIST: (process.env.RECON_EXIT_BACKFILL_PROFILE_ALLOWLIST || '')
        .split(',')
        .map(s => s.trim())
        .filter(Boolean),

    // Reconciliation missing-order coverage sync (data-only safety path)
    ENABLE_RECON_ORDER_COVERAGE_SYNC: process.env.ENABLE_RECON_ORDER_COVERAGE_SYNC !== 'false',
    RECON_ORDER_COVERAGE_DRY_RUN: process.env.RECON_ORDER_COVERAGE_DRY_RUN === 'true',
    RECON_ORDER_COVERAGE_REQUIRE_PAUSE: process.env.RECON_ORDER_COVERAGE_REQUIRE_PAUSE === 'true',
    RECON_ORDER_COVERAGE_LOOKBACK_HOURS: parseInt(process.env.RECON_ORDER_COVERAGE_LOOKBACK_HOURS || '72', 10),
    RECON_ORDER_COVERAGE_FETCH_LIMIT_PER_PAGE: parseInt(process.env.RECON_ORDER_COVERAGE_FETCH_LIMIT_PER_PAGE || '500', 10),
    RECON_ORDER_COVERAGE_MAX_FETCH_PAGES: parseInt(process.env.RECON_ORDER_COVERAGE_MAX_FETCH_PAGES || '25', 10),
    RECON_ORDER_COVERAGE_MAX_INSERTS_PER_PROFILE: parseInt(process.env.RECON_ORDER_COVERAGE_MAX_INSERTS_PER_PROFILE || '200', 10),
    RECON_ORDER_COVERAGE_TRADE_ID_LOOKBACK_ROWS: parseInt(process.env.RECON_ORDER_COVERAGE_TRADE_ID_LOOKBACK_ROWS || '2000', 10),
    RECON_ORDER_COVERAGE_REQUIRE_SUBTAG_ATTRIBUTION: process.env.RECON_ORDER_COVERAGE_REQUIRE_SUBTAG_ATTRIBUTION !== 'false',
    RECON_ORDER_COVERAGE_AUTO_PAUSE_ON_UNATTRIBUTED_FILLS: process.env.RECON_ORDER_COVERAGE_AUTO_PAUSE_ON_UNATTRIBUTED_FILLS !== 'false',
    RECON_ORDER_COVERAGE_UNATTRIBUTED_PAUSE_MIN_COUNT: parseInt(process.env.RECON_ORDER_COVERAGE_UNATTRIBUTED_PAUSE_MIN_COUNT || '1', 10),
    RECON_ORDER_COVERAGE_UNATTRIBUTED_BASELINE_MS: parseInt(process.env.RECON_ORDER_COVERAGE_UNATTRIBUTED_BASELINE_MS || '0', 10),

    // Reconciliation sub-tag repair (data-only; fills missing legacy sub_tag for traceability)
    ENABLE_RECON_SUBTAG_REPAIR: process.env.ENABLE_RECON_SUBTAG_REPAIR !== 'false',
    RECON_SUBTAG_REPAIR_DRY_RUN: process.env.RECON_SUBTAG_REPAIR_DRY_RUN === 'true',
    RECON_SUBTAG_REPAIR_LOOKBACK_HOURS: parseInt(process.env.RECON_SUBTAG_REPAIR_LOOKBACK_HOURS || '720', 10),
    RECON_SUBTAG_REPAIR_MAX_UPDATES_PER_PROFILE: parseInt(process.env.RECON_SUBTAG_REPAIR_MAX_UPDATES_PER_PROFILE || '500', 10),

    // Canonical lifecycle API sizing and truncation visibility
    CANONICAL_LIFECYCLE_MAX_ROWS: parseInt(process.env.CANONICAL_LIFECYCLE_MAX_ROWS || '200000', 10),
    CANONICAL_LIFECYCLE_TRUNCATION_ALERT_MS: parseInt(process.env.CANONICAL_LIFECYCLE_TRUNCATION_ALERT_MS || '600000', 10),

    // Reconciliation SLO alerts
    RECONCILIATION_SLO_ALERT_STREAK: parseInt(process.env.RECONCILIATION_SLO_ALERT_STREAK || '2', 10),
    RECONCILIATION_SLO_ALERT_THROTTLE_MS: parseInt(process.env.RECONCILIATION_SLO_ALERT_THROTTLE_MS || '600000', 10),
    RECONCILIATION_SLO_MISMATCH_THRESHOLD: parseInt(process.env.RECONCILIATION_SLO_MISMATCH_THRESHOLD || '1', 10),
    RECONCILIATION_SLO_MISSING_EXCHANGE_THRESHOLD: parseInt(process.env.RECONCILIATION_SLO_MISSING_EXCHANGE_THRESHOLD || '1', 10),
    RECONCILIATION_SLO_MISSING_DB_THRESHOLD: parseInt(process.env.RECONCILIATION_SLO_MISSING_DB_THRESHOLD || '1', 10),

    // Reconciliation integrity watchdog (hard alerts for lifecycle drift risks)
    ENABLE_RECON_INTEGRITY_WATCHDOG: process.env.ENABLE_RECON_INTEGRITY_WATCHDOG !== 'false',
    RECON_INTEGRITY_WATCHDOG_THROTTLE_MS: parseInt(process.env.RECON_INTEGRITY_WATCHDOG_THROTTLE_MS || '600000', 10),
    RECON_INTEGRITY_WATCHDOG_MISSING_DB_THRESHOLD: parseInt(process.env.RECON_INTEGRITY_WATCHDOG_MISSING_DB_THRESHOLD || '1', 10),
    RECON_INTEGRITY_WATCHDOG_NO_GO_THRESHOLD: parseInt(process.env.RECON_INTEGRITY_WATCHDOG_NO_GO_THRESHOLD || '1', 10),
    ENABLE_RECON_WATCHDOG_AUTO_RESUME: process.env.ENABLE_RECON_WATCHDOG_AUTO_RESUME !== 'false',
    RECON_WATCHDOG_AUTO_RESUME_MIN_PAUSE_MS: parseInt(process.env.RECON_WATCHDOG_AUTO_RESUME_MIN_PAUSE_MS || '900000', 10),
    RECON_WATCHDOG_AUTO_RESUME_CLEAN_CYCLES: parseInt(process.env.RECON_WATCHDOG_AUTO_RESUME_CLEAN_CYCLES || '2', 10),
    RECON_WATCHDOG_AUTO_RESUME_COOLDOWN_MS: parseInt(process.env.RECON_WATCHDOG_AUTO_RESUME_COOLDOWN_MS || '1800000', 10),

    // Reconciliation position-parity heartbeat (automated ghost self-healing path)
    ENABLE_RECON_POSITION_PARITY_HEARTBEAT: process.env.ENABLE_RECON_POSITION_PARITY_HEARTBEAT !== 'false',
    RECON_POSITION_PARITY_DRY_RUN: process.env.RECON_POSITION_PARITY_DRY_RUN === 'true',
    RECON_POSITION_PARITY_CONFIRMATIONS: parseInt(process.env.RECON_POSITION_PARITY_CONFIRMATIONS || '3', 10),
    RECON_POSITION_PARITY_DUST_ABS_QTY: parseFloat(process.env.RECON_POSITION_PARITY_DUST_ABS_QTY || '0.0001'),
    RECON_POSITION_PARITY_MAX_NOTIONAL_PCT: parseFloat(process.env.RECON_POSITION_PARITY_MAX_NOTIONAL_PCT || '0.5'),
    RECON_POSITION_PARITY_REQUIRE_SUBTAG_ATTRIBUTION: process.env.RECON_POSITION_PARITY_REQUIRE_SUBTAG_ATTRIBUTION !== 'false',
    RECON_POSITION_PARITY_ALLOW_LEGACY_ENTRY_ATTRIBUTION: process.env.RECON_POSITION_PARITY_ALLOW_LEGACY_ENTRY_ATTRIBUTION !== 'false',

    // Pro Strategy Configuration (Modular Rules)
    PRO_STRATEGY: {
        ENABLED_RULES: (process.env.ENABLED_RULES || 'trend_bias,momentum,zone,session,entry_trigger,ai_analysis,risk_management').split(',').map(s => s.trim()),
        PARAMETERS: {
            // Trend Bias (4H)
            TREND_TIMEFRAME: process.env.R_TREND_TIMEFRAME || '4h',
            // Execution (15m) -- Phase 2 Upgrade
            EXECUTION_TIMEFRAME: process.env.R_EXECUTION_TIMEFRAME || (process.env.LOW_STRESS_MODE === 'true' ? '1h' : '15m'),

            TREND_EMA_FAST: parseInt(process.env.R_TREND_EMA_FAST || '50', 10),
            TREND_EMA_SLOW: parseInt(process.env.R_TREND_EMA_SLOW || '200', 10),

            // Momentum (15m)
            MOMENTUM_TIMEFRAME: process.env.R_MOMENTUM_TIMEFRAME || '15m',
            RSI_PERIOD: parseInt(process.env.R_RSI_PERIOD || '14', 10),
            RSI_OVERBOUGHT: parseInt(process.env.R_RSI_OVERBOUGHT || '70', 10),
            RSI_OVERSOLD: parseInt(process.env.R_RSI_OVERSOLD || '30', 10),

            // Zone / Location
            ZONE_EMA_PERIOD: parseInt(process.env.R_ZONE_EMA_PERIOD || '20', 10),

            // Session (UTC Hours) — JSON-configurable
            SESSION_WINDOWS: JSON.parse(process.env.R_SESSION_WINDOWS || JSON.stringify([
                { start: 0, end: 9 },   // Tokyo (TOK)
                { start: 22, end: 7 },  // Sydney (SYD)
                { start: 7, end: 16 },  // London (LDN)
                { start: 13, end: 22 }  // New York (NY)
            ])),

            // Risk
            ATR_PERIOD: parseInt(process.env.R_ATR_PERIOD || '14', 10),
            RISK_PER_TRADE: parseFloat(process.env.R_RISK_PER_TRADE || '0.01'),
            RISK_REWARD_RATIO: parseFloat(process.env.R_RISK_REWARD_RATIO || '1.5'),
            SL_MULTIPLIER: parseFloat(process.env.R_SL_MULTIPLIER || '1.5'),
            MAX_TP_CAP: parseFloat(process.env.R_MAX_TP_CAP || '0.01'),
        }
    },
};

const toNumber = (value: unknown, fallback: number): number => {
    if (typeof value === 'number' && Number.isFinite(value)) return value;
    if (typeof value === 'string') {
        const parsed = Number(value);
        if (Number.isFinite(parsed)) return parsed;
    }
    return fallback;
};

const toBoolean = (value: unknown, fallback: boolean): boolean => {
    if (typeof value === 'boolean') return value;
    if (typeof value === 'string') {
        const normalized = value.trim().toLowerCase();
        if (['true', '1', 'yes', 'on'].includes(normalized)) return true;
        if (['false', '0', 'no', 'off'].includes(normalized)) return false;
    }
    return fallback;
};

const toCsvArray = (value: unknown, fallback: string[]): string[] => {
    if (Array.isArray(value)) {
        return value.map(v => String(v).trim()).filter(Boolean);
    }
    if (typeof value === 'string') {
        return value.split(',').map(s => s.trim()).filter(Boolean);
    }
    return fallback;
};

const dynamicConfigParsers: Record<string, (value: unknown) => unknown> = {
    SYMBOLS: (value) => toCsvArray(value, config.SYMBOLS),
    POLLING_INTERVAL: (value) => toNumber(value, config.POLLING_INTERVAL),
    ENABLE_TRADING: (value) => toBoolean(value, config.ENABLE_TRADING),
    TOTAL_CAPITAL: (value) => toNumber(value, config.TOTAL_CAPITAL),
    MAX_OPEN_TRADES: (value) => toNumber(value, config.MAX_OPEN_TRADES),
    MAX_OPEN_TRADES_PER_ACCOUNT: (value) => toNumber(value, config.MAX_OPEN_TRADES_PER_ACCOUNT),
    COOLDOWN_MS: (value) => toNumber(value, config.COOLDOWN_MS),
    PROFIT_EXIT_PERCENT: (value) => toNumber(value, config.PROFIT_EXIT_PERCENT),
    TRAILING_STOP_PERCENT: (value) => toNumber(value, config.TRAILING_STOP_PERCENT),
    PROFILE_SYNC_INTERVAL_MS: (value) => toNumber(value, config.PROFILE_SYNC_INTERVAL_MS),
    MONITOR_INTERVAL_MS: (value) => toNumber(value, config.MONITOR_INTERVAL_MS),
    ORDER_SYNC_INTERVAL_MS: (value) => toNumber(value, config.ORDER_SYNC_INTERVAL_MS),
    STALE_ORDER_THRESHOLD_MINUTES: (value) => toNumber(value, config.STALE_ORDER_THRESHOLD_MINUTES),
    ORDER_SYNC_MISSING_GRACE_MINUTES: (value) => toNumber(value, config.ORDER_SYNC_MISSING_GRACE_MINUTES),
    ORDER_SYNC_MISSING_CONFIRMATION_COUNT: (value) => toNumber(value, config.ORDER_SYNC_MISSING_CONFIRMATION_COUNT),
    ORDER_SYNC_RECENT_CLOSED_LOOKBACK_MINUTES: (value) => toNumber(value, config.ORDER_SYNC_RECENT_CLOSED_LOOKBACK_MINUTES),
    SYMBOL_DELAY_MS: (value) => toNumber(value, config.SYMBOL_DELAY_MS),
    ENABLE_BACKTEST: (value) => toBoolean(value, config.ENABLE_BACKTEST),
    BACKTEST_CUSTOMER_ENABLED: (value) => toBoolean(value, config.BACKTEST_CUSTOMER_ENABLED),
    BACKTEST_MAX_CSV_BYTES: (value) => toNumber(value, config.BACKTEST_MAX_CSV_BYTES),
    BACKTEST_MAX_ROWS: (value) => toNumber(value, config.BACKTEST_MAX_ROWS),
    ORDER_POLL_INTERVAL_MS: (value) => toNumber(value, config.ORDER_POLL_INTERVAL_MS),
    ORDER_POLL_MAX_ATTEMPTS: (value) => toNumber(value, config.ORDER_POLL_MAX_ATTEMPTS),
    LIMIT_ORDER_TIMEOUT_MS: (value) => toNumber(value, config.LIMIT_ORDER_TIMEOUT_MS),
    MAX_SLIPPAGE_PERCENT: (value) => toNumber(value, config.MAX_SLIPPAGE_PERCENT),
    ENABLE_AUTO_PAUSE_ON_SLIPPAGE_BREACH: (value) => toBoolean(value, config.ENABLE_AUTO_PAUSE_ON_SLIPPAGE_BREACH),
    MIN_POSITION_QTY: (value) => toNumber(value, config.MIN_POSITION_QTY),
    MAX_POSITION_QTY: (value) => toNumber(value, config.MAX_POSITION_QTY),
    QUANTITY_PRECISION: (value) => toNumber(value, config.QUANTITY_PRECISION),
    MIN_NOTIONAL_USD: (value) => toNumber(value, config.MIN_NOTIONAL_USD),
    MAX_NOTIONAL_USD: (value) => toNumber(value, config.MAX_NOTIONAL_USD),
    CAPITAL_RESERVE_PERCENT: (value) => toNumber(value, config.CAPITAL_RESERVE_PERCENT),
    ENTRY_CAPITAL_BUFFER_PCT: (value) => toNumber(value, config.ENTRY_CAPITAL_BUFFER_PCT),
    ENABLE_STRICT_CAPITAL_GUARD: (value) => toBoolean(value, config.ENABLE_STRICT_CAPITAL_GUARD),
    STRICT_CAPITAL_SLIPPAGE_BUFFER_PCT: (value) => toNumber(value, config.STRICT_CAPITAL_SLIPPAGE_BUFFER_PCT),
    STRICT_CAPITAL_FEE_BUFFER_PCT: (value) => toNumber(value, config.STRICT_CAPITAL_FEE_BUFFER_PCT),
    STRICT_CAPITAL_MIN_RESERVE_USD: (value) => toNumber(value, config.STRICT_CAPITAL_MIN_RESERVE_USD),
    ENTRY_AUTO_REDUCE_ALERT_MIN_PCT: (value) => toNumber(value, config.ENTRY_AUTO_REDUCE_ALERT_MIN_PCT),
    ENTRY_AUTO_REDUCE_ALERT_MIN_USD: (value) => toNumber(value, config.ENTRY_AUTO_REDUCE_ALERT_MIN_USD),
    ENTRY_AUTO_REDUCE_ALERT_THROTTLE_MS: (value) => toNumber(value, config.ENTRY_AUTO_REDUCE_ALERT_THROTTLE_MS),
    CAPITAL_INVARIANT_EPSILON_USD: (value) => toNumber(value, config.CAPITAL_INVARIANT_EPSILON_USD),
    CAPITAL_INVARIANT_EPSILON_PCT: (value) => toNumber(value, config.CAPITAL_INVARIANT_EPSILON_PCT),
    CAPITAL_INVARIANT_ALERT_THROTTLE_MS: (value) => toNumber(value, config.CAPITAL_INVARIANT_ALERT_THROTTLE_MS),
    CAPITAL_LEDGER_DRIFT_ALERT_PCT: (value) => toNumber(value, config.CAPITAL_LEDGER_DRIFT_ALERT_PCT),
    CAPITAL_LEDGER_DRIFT_MIN_USD: (value) => toNumber(value, config.CAPITAL_LEDGER_DRIFT_MIN_USD),
    CAPITAL_LEDGER_DRIFT_SCOPE: (value) => String(value ?? config.CAPITAL_LEDGER_DRIFT_SCOPE).trim().toLowerCase(),
    OPERATIONAL_EVENTS_MAX_BUFFER: (value) => toNumber(value, config.OPERATIONAL_EVENTS_MAX_BUFFER),
    ENABLE_ALPACA_SUBTAG: (value) => toBoolean(value, config.ENABLE_ALPACA_SUBTAG),
    SUBTAG_OMNIBUS_ONLY: (value) => toBoolean(value, config.SUBTAG_OMNIBUS_ONLY),
    ALPACA_SUBTAG_ENV: (value) => String(value ?? config.ALPACA_SUBTAG_ENV),
    ALPACA_SUBTAG_MAX_LENGTH: (value) => toNumber(value, config.ALPACA_SUBTAG_MAX_LENGTH),
    ALPACA_SUBTAG_DISABLE_FOR_EXCHANGE: (value) => toCsvArray(value, config.ALPACA_SUBTAG_DISABLE_FOR_EXCHANGE),
    ALPACA_OMNIBUS_PROFILE_ALLOWLIST: (value) => toCsvArray(value, config.ALPACA_OMNIBUS_PROFILE_ALLOWLIST),
    ALLOWED_ORIGINS: (value) => toCsvArray(value, config.ALLOWED_ORIGINS),
    DB_SNAPSHOT_INTERVAL_MS: (value) => toNumber(value, config.DB_SNAPSHOT_INTERVAL_MS),
    ENABLE_DB_SNAPSHOTS: (value) => toBoolean(value, config.ENABLE_DB_SNAPSHOTS),
    ACCOUNT_SNAPSHOT_INTERVAL_MS: (value) => toNumber(value, config.ACCOUNT_SNAPSHOT_INTERVAL_MS),
    DYNAMIC_CONFIG_REFRESH_MS: (value) => toNumber(value, config.DYNAMIC_CONFIG_REFRESH_MS),
    EXCHANGE_STATE_MISMATCH_THROTTLE_MS: (value) => toNumber(value, config.EXCHANGE_STATE_MISMATCH_THROTTLE_MS),
    REQUIRE_EXCHANGE_FILL_EVIDENCE_FOR_AUTO_CLOSE: (value) => toBoolean(value, config.REQUIRE_EXCHANGE_FILL_EVIDENCE_FOR_AUTO_CLOSE),
    ENABLE_RECON_EXIT_BACKFILL: (value) => toBoolean(value, config.ENABLE_RECON_EXIT_BACKFILL),
    RECON_EXIT_BACKFILL_DRY_RUN: (value) => toBoolean(value, config.RECON_EXIT_BACKFILL_DRY_RUN),
    RECON_EXIT_BACKFILL_REQUIRE_PAUSE: (value) => toBoolean(value, config.RECON_EXIT_BACKFILL_REQUIRE_PAUSE),
    RECON_EXIT_BACKFILL_DUST_ABS_QTY: (value) => toNumber(value, config.RECON_EXIT_BACKFILL_DUST_ABS_QTY),
    RECON_EXIT_BACKFILL_DUST_REL_PCT: (value) => toNumber(value, config.RECON_EXIT_BACKFILL_DUST_REL_PCT),
    RECON_EXIT_BACKFILL_LOOKBACK_HOURS: (value) => toNumber(value, config.RECON_EXIT_BACKFILL_LOOKBACK_HOURS),
    RECON_EXIT_BACKFILL_REQUIRE_STRONG_ATTRIBUTION: (value) => toBoolean(value, config.RECON_EXIT_BACKFILL_REQUIRE_STRONG_ATTRIBUTION),
    RECON_EXIT_BACKFILL_ALLOW_HEURISTIC_MATCH: (value) => toBoolean(value, config.RECON_EXIT_BACKFILL_ALLOW_HEURISTIC_MATCH),
    RECON_EXIT_BACKFILL_FILL_AFTER_TRADE_GRACE_MINUTES: (value) => toNumber(value, config.RECON_EXIT_BACKFILL_FILL_AFTER_TRADE_GRACE_MINUTES),
    RECON_EXIT_BACKFILL_PROFILE_ALLOWLIST: (value) => toCsvArray(value, config.RECON_EXIT_BACKFILL_PROFILE_ALLOWLIST),
    ENABLE_RECON_ORDER_COVERAGE_SYNC: (value) => toBoolean(value, config.ENABLE_RECON_ORDER_COVERAGE_SYNC),
    RECON_ORDER_COVERAGE_DRY_RUN: (value) => toBoolean(value, config.RECON_ORDER_COVERAGE_DRY_RUN),
    RECON_ORDER_COVERAGE_REQUIRE_PAUSE: (value) => toBoolean(value, config.RECON_ORDER_COVERAGE_REQUIRE_PAUSE),
    RECON_ORDER_COVERAGE_LOOKBACK_HOURS: (value) => toNumber(value, config.RECON_ORDER_COVERAGE_LOOKBACK_HOURS),
    RECON_ORDER_COVERAGE_FETCH_LIMIT_PER_PAGE: (value) => toNumber(value, config.RECON_ORDER_COVERAGE_FETCH_LIMIT_PER_PAGE),
    RECON_ORDER_COVERAGE_MAX_FETCH_PAGES: (value) => toNumber(value, config.RECON_ORDER_COVERAGE_MAX_FETCH_PAGES),
    RECON_ORDER_COVERAGE_MAX_INSERTS_PER_PROFILE: (value) => toNumber(value, config.RECON_ORDER_COVERAGE_MAX_INSERTS_PER_PROFILE),
    RECON_ORDER_COVERAGE_TRADE_ID_LOOKBACK_ROWS: (value) => toNumber(value, config.RECON_ORDER_COVERAGE_TRADE_ID_LOOKBACK_ROWS),
    RECON_ORDER_COVERAGE_REQUIRE_SUBTAG_ATTRIBUTION: (value) => toBoolean(value, config.RECON_ORDER_COVERAGE_REQUIRE_SUBTAG_ATTRIBUTION),
    RECON_ORDER_COVERAGE_AUTO_PAUSE_ON_UNATTRIBUTED_FILLS: (value) => toBoolean(value, config.RECON_ORDER_COVERAGE_AUTO_PAUSE_ON_UNATTRIBUTED_FILLS),
    RECON_ORDER_COVERAGE_UNATTRIBUTED_PAUSE_MIN_COUNT: (value) => toNumber(value, config.RECON_ORDER_COVERAGE_UNATTRIBUTED_PAUSE_MIN_COUNT),
    RECON_ORDER_COVERAGE_UNATTRIBUTED_BASELINE_MS: (value) => toNumber(value, config.RECON_ORDER_COVERAGE_UNATTRIBUTED_BASELINE_MS),
    ENABLE_RECON_SUBTAG_REPAIR: (value) => toBoolean(value, config.ENABLE_RECON_SUBTAG_REPAIR),
    RECON_SUBTAG_REPAIR_DRY_RUN: (value) => toBoolean(value, config.RECON_SUBTAG_REPAIR_DRY_RUN),
    RECON_SUBTAG_REPAIR_LOOKBACK_HOURS: (value) => toNumber(value, config.RECON_SUBTAG_REPAIR_LOOKBACK_HOURS),
    RECON_SUBTAG_REPAIR_MAX_UPDATES_PER_PROFILE: (value) => toNumber(value, config.RECON_SUBTAG_REPAIR_MAX_UPDATES_PER_PROFILE),
    CANONICAL_LIFECYCLE_MAX_ROWS: (value) => toNumber(value, config.CANONICAL_LIFECYCLE_MAX_ROWS),
    CANONICAL_LIFECYCLE_TRUNCATION_ALERT_MS: (value) => toNumber(value, config.CANONICAL_LIFECYCLE_TRUNCATION_ALERT_MS),
    RECONCILIATION_SLO_ALERT_STREAK: (value) => toNumber(value, config.RECONCILIATION_SLO_ALERT_STREAK),
    RECONCILIATION_SLO_ALERT_THROTTLE_MS: (value) => toNumber(value, config.RECONCILIATION_SLO_ALERT_THROTTLE_MS),
    RECONCILIATION_SLO_MISMATCH_THRESHOLD: (value) => toNumber(value, config.RECONCILIATION_SLO_MISMATCH_THRESHOLD),
    RECONCILIATION_SLO_MISSING_EXCHANGE_THRESHOLD: (value) => toNumber(value, config.RECONCILIATION_SLO_MISSING_EXCHANGE_THRESHOLD),
    RECONCILIATION_SLO_MISSING_DB_THRESHOLD: (value) => toNumber(value, config.RECONCILIATION_SLO_MISSING_DB_THRESHOLD),
    ENABLE_RECON_INTEGRITY_WATCHDOG: (value) => toBoolean(value, config.ENABLE_RECON_INTEGRITY_WATCHDOG),
    RECON_INTEGRITY_WATCHDOG_THROTTLE_MS: (value) => toNumber(value, config.RECON_INTEGRITY_WATCHDOG_THROTTLE_MS),
    RECON_INTEGRITY_WATCHDOG_MISSING_DB_THRESHOLD: (value) => toNumber(value, config.RECON_INTEGRITY_WATCHDOG_MISSING_DB_THRESHOLD),
    RECON_INTEGRITY_WATCHDOG_NO_GO_THRESHOLD: (value) => toNumber(value, config.RECON_INTEGRITY_WATCHDOG_NO_GO_THRESHOLD),
    ENABLE_RECON_WATCHDOG_AUTO_RESUME: (value) => toBoolean(value, config.ENABLE_RECON_WATCHDOG_AUTO_RESUME),
    RECON_WATCHDOG_AUTO_RESUME_MIN_PAUSE_MS: (value) => toNumber(value, config.RECON_WATCHDOG_AUTO_RESUME_MIN_PAUSE_MS),
    RECON_WATCHDOG_AUTO_RESUME_CLEAN_CYCLES: (value) => toNumber(value, config.RECON_WATCHDOG_AUTO_RESUME_CLEAN_CYCLES),
    RECON_WATCHDOG_AUTO_RESUME_COOLDOWN_MS: (value) => toNumber(value, config.RECON_WATCHDOG_AUTO_RESUME_COOLDOWN_MS),
    ENABLE_RECON_POSITION_PARITY_HEARTBEAT: (value) => toBoolean(value, config.ENABLE_RECON_POSITION_PARITY_HEARTBEAT),
    RECON_POSITION_PARITY_DRY_RUN: (value) => toBoolean(value, config.RECON_POSITION_PARITY_DRY_RUN),
    RECON_POSITION_PARITY_CONFIRMATIONS: (value) => toNumber(value, config.RECON_POSITION_PARITY_CONFIRMATIONS),
    RECON_POSITION_PARITY_DUST_ABS_QTY: (value) => toNumber(value, config.RECON_POSITION_PARITY_DUST_ABS_QTY),
    RECON_POSITION_PARITY_MAX_NOTIONAL_PCT: (value) => toNumber(value, config.RECON_POSITION_PARITY_MAX_NOTIONAL_PCT),
    RECON_POSITION_PARITY_REQUIRE_SUBTAG_ATTRIBUTION: (value) => toBoolean(value, config.RECON_POSITION_PARITY_REQUIRE_SUBTAG_ATTRIBUTION),
    RECON_POSITION_PARITY_ALLOW_LEGACY_ENTRY_ATTRIBUTION: (value) => toBoolean(value, config.RECON_POSITION_PARITY_ALLOW_LEGACY_ENTRY_ATTRIBUTION),
};

const aiConfigParsers: Record<string, (value: unknown) => unknown> = {
    CONFIDENCE_THRESHOLD: (value) => toNumber(value, config.AI.CONFIDENCE_THRESHOLD),
    CACHE_HOURS: (value) => toNumber(value, config.AI.CACHE_HOURS),
    FALLBACK_LIST: (value) => toCsvArray(value, config.AI.FALLBACK_LIST),
    FAIL_OPEN: (value) => toBoolean(value, config.AI.FAIL_OPEN),
};

export function applyDynamicConfigEntries(data: Array<{ key: string; value: unknown }>) {
    if (!Array.isArray(data) || data.length === 0) {
        return [] as string[];
    }

    const loadedKeys: string[] = [];
    data.forEach((item: any) => {
        const { key, value } = item;
        if (key in config) {
            const parser = dynamicConfigParsers[key];
            (config as any)[key] = parser ? parser(value) : value;
            loadedKeys.push(key);
        } else if (key in config.AI) {
            const parser = aiConfigParsers[key];
            (config.AI as any)[key] = parser ? parser(value) : value;
            loadedKeys.push(`AI.${key}`);
        }
    });

    return loadedKeys;
}

/**
 * Loads global configuration from Cosmos-first dynamic config storage to override .env defaults.
 * Falls back to the legacy Supabase table during migration.
 */
export const loadDynamicConfig = async (supabase?: any) => {
    try {
        logger.info('--- Loading Dynamic Global Config from control-plane storage ---');
        const data = await listDynamicConfigEntries(supabase);

        if (data && data.length > 0) {
            const loadedKeys = applyDynamicConfigEntries(data);
            logger.info(`✅ Dynamic Config Loaded: ${loadedKeys.join(', ')}`);
        } else {
            logger.info('ℹ️  No dynamic config overrides found in control-plane storage. Using .env defaults.');
        }
    } catch (err: any) {
        logger.error(`[Config] Unexpected error loading dynamic config: ${err.message}`);
    }
};

export const validateConfig = () => {
    // Treat "your_key" as empty
    const isPlaceholder = (val: string) => !val || val === 'your_key' || val === 'your_secret';

    if (config.PROVIDER === 'alpaca') {
        if (isPlaceholder(config.ALPACA_API_KEY) || isPlaceholder(config.ALPACA_API_SECRET)) {
            // Only log warning, don't exit if Supabase is available
            if (config.SUPABASE_URL && config.SUPABASE_KEY) {
                logger.warn('⚠️ Alpaca keys in .env are placeholders/missing. Bot will attempt to use keys from Supabase users.');
            } else {
                logger.error('❌ Missing Alpaca API credentials and no Supabase configured!');
                process.exit(1);
            }
        }
    } else if (config.PROVIDER === 'ccxt') {
        if (!config.EXCHANGE || config.EXCHANGE === 'binance') { // binance is default but check for keys
            if (isPlaceholder(config.CCXT_API_KEY) && !(config.SUPABASE_URL)) {
                logger.warn('⚠️ CCXT keys are placeholders/missing.');
            }
        }
    }
};
