import type { BuilderState } from "@/lib/admin/builder/context";
import type { BuilderScreen } from "@/lib/admin/builder/types";

export interface BuilderValidationIssue {
  severity: "error" | "warning";
  message: string;
  screenId?: string;
  optionId?: string;
}

export interface BuilderValidationResult {
  issues: BuilderValidationIssue[];
  errors: BuilderValidationIssue[];
  warnings: BuilderValidationIssue[];
}

function createIssue(
  severity: BuilderValidationIssue["severity"],
  message: string,
  context: Partial<Pick<BuilderValidationIssue, "screenId" | "optionId">> = {}
): BuilderValidationIssue {
  return { severity, message, ...context };
}

function collectDuplicateIds(values: string[]): string[] {
  const counts = new Map<string, number>();
  for (const value of values) {
    counts.set(value, (counts.get(value) ?? 0) + 1);
  }
  return Array.from(counts.entries())
    .filter(([, count]) => count > 1)
    .map(([value]) => value);
}

function validateScreenIds(state: BuilderState, issues: BuilderValidationIssue[]) {
  const duplicates = collectDuplicateIds(state.screens.map((screen) => screen.id));
  for (const duplicateId of duplicates) {
    issues.push(createIssue("error", `Дублирующийся идентификатор экрана \`${duplicateId}\``, { screenId: duplicateId }));
  }
}

function validateOptionIds(screen: BuilderScreen, issues: BuilderValidationIssue[]) {
  // Проверяем опции только для экранов типа 'list', у которых есть свойство list
  if (screen.template !== "list" || !("list" in screen)) {
    return;
  }

  const screenWithList = screen as { list: { options: { id: string }[] } };
  const duplicates = collectDuplicateIds(screenWithList.list.options.map((option) => option.id));
  for (const duplicateId of duplicates) {
    issues.push(
      createIssue(
        "error",
        `Экран \`${screen.id}\`: опция с идентификатором \`${duplicateId}\` повторяется несколько раз`,
        { screenId: screen.id, optionId: duplicateId }
      )
    );
  }
}

function validateNavigation(screen: BuilderScreen, state: BuilderState, issues: BuilderValidationIssue[]) {
  const screenIds = new Set(state.screens.map((candidate) => candidate.id));

  const navigation = screen.navigation;
  if (!navigation) {
    issues.push(
      createIssue(
        "warning",
        `Экран \`${screen.id}\` не имеет настроенной навигации (переход по умолчанию или правил)`,
        { screenId: screen.id }
      )
    );
    return;
  }

  // 🎯 ФИНАЛЬНЫЕ ЭКРАНЫ НЕ ТРЕБУЮТ НАВИГАЦИИ
  if (navigation.isEndScreen) {
    // Финальный экран - навигация не нужна
    return;
  }

  if (!navigation.defaultNextScreenId && (!navigation.rules || navigation.rules.length === 0)) {
    issues.push(
      createIssue(
        "warning",
        `Экран \`${screen.id}\` не ведёт на следующий экран. Добавьте переход по умолчанию, правило, или отметьте как финальный экран.`,
        { screenId: screen.id }
      )
    );
  }

  if (navigation.defaultNextScreenId && !screenIds.has(navigation.defaultNextScreenId)) {
    issues.push(
      createIssue(
        "error",
        `Экран \`${screen.id}\` ссылается на несуществующий default next экран \`${navigation.defaultNextScreenId}\``,
        { screenId: screen.id }
      )
    );
  }

  // Проверка onBackScreenId
  const onBackScreenId: string | undefined = navigation.onBackScreenId as string | undefined;
  if (onBackScreenId) {
    if (!screenIds.has(onBackScreenId)) {
      issues.push(
        createIssue(
          "error",
          `Экран \`${screen.id}\` ссылается на несуществующий back-экран \`${onBackScreenId}\``,
          { screenId: screen.id }
        )
      );
    }
    if (onBackScreenId === screen.id) {
      issues.push(
        createIssue(
          "warning",
          `Экран \`${screen.id}\`: выбран тот же экран для перехода назад (нет смысла)`,
          { screenId: screen.id }
        )
      );
    }
  }

  for (const [ruleIndex, rule] of (navigation.rules ?? []).entries()) {
    if (!screenIds.has(rule.nextScreenId)) {
      issues.push(
        createIssue(
          "error",
          `Экран \`${screen.id}\`, правило #${ruleIndex + 1}: следующий экран \`${rule.nextScreenId}\` не найден`,
          { screenId: screen.id }
        )
      );
    }

    for (const condition of rule.conditions) {
      if (!screenIds.has(condition.screenId)) {
        issues.push(
          createIssue(
            "error",
            `Экран \`${screen.id}\`, правило #${ruleIndex + 1}: условие указывает на отсутствующий экран \`${condition.screenId}\``,
            { screenId: screen.id }
          )
        );
        continue;
      }

      const referenceScreen = state.screens.find((candidate) => candidate.id === condition.screenId);
      if (!referenceScreen) {
        continue;
      }

      // Проверяем опции только для экранов типа 'list'
      if (referenceScreen.template !== "list" || !("list" in referenceScreen)) {
        // Если это не list экран, но правило ссылается на опции, это ошибка
        if (condition.optionIds && condition.optionIds.length > 0) {
          issues.push(
            createIssue(
              "error",
              `Экран \`${screen.id}\`, правило #${ruleIndex + 1}: экран \`${referenceScreen.id}\` типа "${referenceScreen.template}" не имеет опций`,
              { screenId: screen.id }
            )
          );
        }
        continue;
      }

      const referenceScreenWithList = referenceScreen as { list: { options: { id: string }[] } };
      const availableOptionIds = new Set(referenceScreenWithList.list.options.map((option) => option.id));
      const missingOptionIds = (condition.optionIds ?? []).filter((optionId) => !availableOptionIds.has(optionId));
      if (missingOptionIds.length > 0) {
        issues.push(
          createIssue(
            "warning",
            `Экран \`${screen.id}\`, правило #${ruleIndex + 1}: опции ${missingOptionIds
              .map((id) => `\`${id}\``)
              .join(", ")} не найдены на экране \`${referenceScreen.id}\``,
            { screenId: screen.id }
          )
        );
      }
    }
  }
}

export function validateBuilderState(state: BuilderState): BuilderValidationResult {
  const issues: BuilderValidationIssue[] = [];

  validateScreenIds(state, issues);

  for (const screen of state.screens) {
    validateOptionIds(screen, issues);
    validateNavigation(screen, state, issues);
  }

  const errors = issues.filter((issue) => issue.severity === "error");
  const warnings = issues.filter((issue) => issue.severity === "warning");

  return {
    issues,
    errors,
    warnings,
  };
}
