import type { VariableDefinition, FunnelAnswers, NavigationConditionDefinition } from "./types";

/**
 * Подставляет значения переменных в текст на основе ответов пользователя.
 * 
 * @param text - Текст с переменными в формате {{variableName}}
 * @param variables - Определения переменных с правилами подстановки
 * @param answers - Текущие ответы пользователя
 * @returns Текст с подставленными значениями
 * 
 * @example
 * const text = "По нашей статистике 43% {{gender}} {{zodiac}} выбирают разум";
 * const variables = [
 *   {
 *     name: "gender",
 *     mappings: [
 *       { conditions: [{ screenId: "gender", values: ["female"] }], value: "женщин" },
 *       { conditions: [{ screenId: "gender", values: ["male"] }], value: "мужчин" }
 *     ],
 *     fallback: "людей"
 *   },
 *   {
 *     name: "zodiac",
 *     mappings: [
 *       { conditions: [{ screenId: "birthdate", conditionType: "values", values: ["aries"] }], value: "Овнов" }
 *     ],
 *     fallback: ""
 *   }
 * ];
 * const answers = { gender: ["female"], birthdate: ["aries"] };
 * 
 * // Result: "По нашей статистике 43% женщин Овнов выбирают разум"
 */
export function substituteVariables(
  text: string | undefined,
  variables: VariableDefinition[] | undefined,
  answers: FunnelAnswers
): string {
  if (!text || !variables || variables.length === 0) {
    return text || "";
  }

  let result = text;

  // Проходим по всем переменным
  for (const variable of variables) {
    const placeholder = `{{${variable.name}}}`;
    
    // Если переменной нет в тексте, пропускаем
    if (!result.includes(placeholder)) {
      continue;
    }

    // Ищем подходящее значение на основе условий
    const value = resolveVariableValue(variable, answers);
    
    // Заменяем все вхождения переменной на значение
    result = result.replaceAll(placeholder, value);
  }

  return result;
}

/**
 * Находит значение переменной на основе условий и ответов пользователя.
 * 
 * @param variable - Определение переменной
 * @param answers - Текущие ответы пользователя
 * @returns Значение для подстановки
 */
function resolveVariableValue(
  variable: VariableDefinition,
  answers: FunnelAnswers
): string {
  // Проверяем каждое правило подстановки по порядку
  for (const mapping of variable.mappings) {
    // Проверяем все условия этого правила
    const allConditionsMet = mapping.conditions.every((condition) => 
      checkSingleCondition(condition, answers)
    );

    // Если все условия выполнены, возвращаем значение
    if (allConditionsMet) {
      return mapping.value;
    }
  }

  // Если ни одно условие не сработало, возвращаем fallback или пустую строку
  return variable.fallback ?? "";
}

/**
 * Проверяет одно условие навигации для системы переменных.
 * 
 * @param condition - Условие для проверки
 * @param answers - Текущие ответы пользователя
 * @returns true если условие выполнено
 */
function checkSingleCondition(
  condition: NavigationConditionDefinition,
  answers: FunnelAnswers
): boolean {
  // Проверяем что answers существует
  if (!answers) {
    return false;
  }

  const screenAnswers = answers[condition.screenId];
  
  // Если нет ответов для этого экрана, условие не выполнено
  if (!screenAnswers || screenAnswers.length === 0) {
    return false;
  }

  // Получаем значения для проверки (поддерживаем и values и optionIds для обратной совместимости)
  const targetValues = condition.values ?? condition.optionIds ?? [];
  
  if (targetValues.length === 0) {
    return false;
  }

  const operator = condition.operator ?? "includesAny";

  switch (operator) {
    case "includesAny":
      // Хотя бы одно значение из targetValues есть в ответах
      return targetValues.some((value) => screenAnswers.includes(value));

    case "includesAll":
      // Все значения из targetValues есть в ответах
      return targetValues.every((value) => screenAnswers.includes(value));

    case "includesExactly":
      // Ответы содержат ровно те значения что в targetValues
      return (
        screenAnswers.length === targetValues.length &&
        targetValues.every((value) => screenAnswers.includes(value))
      );

    case "equals":
      // Точное совпадение одного значения (для single selection)
      return screenAnswers.length === 1 && targetValues.includes(screenAnswers[0]);

    default:
      return false;
  }
}
