/**
 * 보조지표 설정 — 숫자 파라미터 허용 범위·드롭다운 목록
 */

export interface NumericParamSpec {
  min: number;
  max: number;
  step: number;
  allowNegative: boolean;
  decimal: boolean;
  /** 드롭다운에 표시할 허용 값 목록 */
  options: number[];
}

const COMMON_PERIODS = [
  1, 2, 3, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 20, 21, 24, 26, 28, 34,
  50, 52, 60, 72, 90, 100, 120, 200,
];

const DECIMAL_MULT = [0.1, 0.2, 0.25, 0.5, 0.75, 1, 1.5, 2, 2.5, 3, 4, 5];

function roundTo(v: number, decimals: number): number {
  const f = 10 ** decimals;
  return Math.round(v * f) / f;
}

function mergeOptions(
  min: number,
  max: number,
  step: number,
  decimal: boolean,
  presets: number[],
  maxCount = 56,
  include?: number,
): number[] {
  const set = new Set<number>();
  const dec = decimal ? (step < 1 ? 2 : 1) : 0;
  for (const p of presets) {
    if (p >= min && p <= max) set.add(roundTo(p, dec));
  }
  if (include != null && !isNaN(include) && include >= min && include <= max) {
    set.add(roundTo(include, dec));
  }
  for (let v = min; v <= max + step * 0.001 && set.size < maxCount; v += step) {
    set.add(roundTo(v, dec));
  }
  return Array.from(set).sort((a, b) => a - b);
}

function isPeriodKey(key: string): boolean {
  return /^(length|len|period\d*|kLength|dSmoothing|smooth|adxSmoothing|diLength|ccilength|turbolen|signalLength|fastLength|slowLength|longLength|shortLength|maLength|ultraLength|midLength|longLength|displacement|conversionPeriods|basePeriods|laggingSpan2Periods|length1|length2|length3|lengthRSI|lengthStoch|rsiLength|upDownLength|rocLength|stdevLength|atrLength|p|q|x)$/i.test(key);
}

function isMultKey(key: string): boolean {
  return /^(mult|multiplier|percent|bbMult)$/i.test(key);
}

function isOffsetKey(key: string): boolean {
  return /^offset$/i.test(key);
}

function isSigmaKey(key: string): boolean {
  return /^sigma$/i.test(key);
}

/** 지표 파라미터 키별 숫자 스펙 */
export function getNumericParamSpec(
  indicatorType: string,
  paramKey: string,
  currentValue?: number,
): NumericParamSpec {
  if (isPeriodKey(paramKey)) {
    const max = paramKey === 'rocLength' ? 200 : 500;
    return {
      min: 1,
      max,
      step: 1,
      allowNegative: false,
      decimal: false,
      options: mergeOptions(1, max, 1, false, COMMON_PERIODS, 56, currentValue),
    };
  }

  if (isMultKey(paramKey)) {
    const isPercent = paramKey === 'percent';
    return {
      min: isPercent ? 0.01 : 0.1,
      max: isPercent ? 1 : 10,
      step: isPercent ? 0.01 : 0.1,
      allowNegative: false,
      decimal: true,
      options: mergeOptions(
        isPercent ? 0.01 : 0.1,
        isPercent ? 1 : 10,
        isPercent ? 0.01 : 0.1,
        true,
        isPercent
          ? [0.05, 0.1, 0.15, 0.2, 0.25, 0.5, 0.75, 1]
          : DECIMAL_MULT,
        40,
        currentValue,
      ),
    };
  }

  if (isOffsetKey(paramKey)) {
    if (indicatorType === 'ALMA') {
      return {
        min: 0,
        max: 1,
        step: 0.05,
        allowNegative: false,
        decimal: true,
        options: mergeOptions(0, 1, 0.05, true, [0, 0.5, 0.85, 1], 24, currentValue),
      };
    }
    return {
      min: -500,
      max: 500,
      step: 1,
      allowNegative: true,
      decimal: false,
      options: mergeOptions(-500, 500, 1, false, [-26, -9, 0, 9, 26], 48, currentValue),
    };
  }

  if (isSigmaKey(paramKey)) {
    return {
      min: 1,
      max: 30,
      step: 1,
      allowNegative: false,
      decimal: false,
      options: mergeOptions(1, 30, 1, false, [4, 6, 8, 10, 14, 20], 30, currentValue),
    };
  }

  // SMI 등 소수 오실레이터 임계
  if (indicatorType === 'SMIErgodic' || indicatorType === 'SMIErgodicOscillator') {
    return {
      min: 1,
      max: 100,
      step: 1,
      allowNegative: false,
      decimal: false,
      options: mergeOptions(1, 100, 1, false, [5, 10, 20, 40], 40, currentValue),
    };
  }

  // 기본: 정수 1~100
  return {
    min: 1,
    max: 500,
    step: 1,
    allowNegative: false,
    decimal: false,
    options: mergeOptions(1, 500, 1, false, COMMON_PERIODS, 56, currentValue),
  };
}

/** 수평선(과열/침체) 기준값 */
export function getHlinePriceSpec(indicatorType: string, current: number): NumericParamSpec {
  const byType: Record<string, number[]> = {
    RSI:              [20, 30, 50, 70, 80],
    Stochastic:       [20, 50, 80],
    StochRSI:         [20, 50, 80],
    CCI:              [-200, -100, 0, 100, 200],
    WilliamsPercentRange: [-80, -50, -20],
    MACD:             [-50, 0, 50],
    ADX:              [20, 25, 40],
    DMI:              [10, 20, 30],
    TRIX:             [-0.05, -0.01, 0, 0.01, 0.05],
    MOM:              [-50, 0, 50],
    ROC:              [-20, 0, 20],
    CMO:              [-50, 0, 50],
    DPO:              [-100, 0, 100],
    FisherTransform:  [-2.5, 0, 2.5],
    SMIErgodic:       [-0.4, 0, 0.4],
    SMIErgodicOscillator: [-0.4, 0, 0.4],
    Disparity:        [90, 95, 100, 105, 110],
    VR:               [50, 100, 150, 200, 250],
    Psychological: [25, 50, 75],
    NewPsychological: [25, 50, 75],
    InvestPsychological: [25, 50, 75],
  };

  const presets = byType[indicatorType] ?? [-200, -100, 0, 100, 200];
  const allowNegative = presets.some(p => p < 0) || indicatorType !== 'RSI';
  const min = Math.min(-500, ...presets, current) - 50;
  const max = Math.max(500, ...presets, current) + 50;

  return {
    min,
    max,
    step: 1,
    allowNegative: true,
    decimal: true,
    options: mergeOptions(min, max, allowNegative ? 10 : 5, true, presets, 48, current),
  };
}

export function getLineWidthSpec(min = 1, max = 5): NumericParamSpec {
  const options = Array.from({ length: max - min + 1 }, (_, i) => min + i);
  return {
    min,
    max,
    step: 1,
    allowNegative: false,
    decimal: false,
    options,
  };
}

export function getOpacityPercentSpec(current?: number): NumericParamSpec {
  const options = Array.from({ length: 21 }, (_, i) => i * 5);
  return {
    min: 0,
    max: 100,
    step: 1,
    allowNegative: false,
    decimal: false,
    options: mergeOptions(0, 100, 5, false, options, 21, current),
  };
}
