import { marked } from "marked";
import hljs from "highlight.js";
// Импортируем стили
import "highlight.js/styles/github-dark.css";
import "highlight.js/styles/github.css";

// Расширение для спойлеров
const spoilerExtension = {
  name: "spoiler",
  level: "inline" as const,
  start(src: string) {
    return src.match(/\|\|/)?.index;
  },
  tokenizer(src: string) {
    const rule = /^\|\|(.*?)\|\|/;
    const match = rule.exec(src);
    if (match) {
      return {
        type: "spoiler",
        raw: match[0],
        text: match[1].trim(),
      };
    }
  },
  renderer(token: any) {
    return `<span class="spoiler" title="Нажмите, чтобы показать">${token.text}</span>`;
  },
};

// Функция для рендеринга вложенных токенов
function renderTokens(tokens: any[], renderer: any): string {
  return tokens
    .map((token) => {
      // Используем кастомный renderer если он есть и является функцией
      if (renderer[token.type] && typeof renderer[token.type] === 'function') {
        try {
          return renderer[token.type](token);
        } catch (e) {
          // Если метод не может обработать токен, используем fallback
        }
      }
      // Fallback для встроенных типов токенов
      if (token.type === "text") {
        return token.text || "";
      }
      if (token.type === "strong") {
        return `<strong>${renderTokens(token.tokens || [], renderer)}</strong>`;
      }
      if (token.type === "em") {
        return `<em>${renderTokens(token.tokens || [], renderer)}</em>`;
      }
      if (token.type === "codespan") {
        return `<code>${token.text || ""}</code>`;
      }
      if (token.type === "del") {
        return `<del>${renderTokens(token.tokens || [], renderer)}</del>`;
      }
      if (token.type === "link") {
        // Для ссылок используем кастомный renderer если доступен
        if (renderer.link && typeof renderer.link === 'function') {
          try {
            return renderer.link(token);
          } catch (e) {
            // Если метод не может обработать токен, используем fallback
          }
        }
        // Fallback для встроенных ссылок
        const href = token.href || "";
        const title = token.title ? ` title="${token.title}"` : "";
        const text =
          token.tokens && token.tokens.length > 0
            ? renderTokens(token.tokens, renderer)
            : token.text || "";
        return `<a href="${href}"${title}>${text}</a>`;
      }
      if (token.type === "spoiler") {
        // Для спойлеров используем кастомный renderer если доступен
        if (renderer.spoiler && typeof renderer.spoiler === 'function') {
          try {
            return renderer.spoiler(token);
          } catch (e) {
            // Если метод не может обработать токен, используем fallback
          }
        }
        return `<span class="spoiler" title="Нажмите, чтобы показать">${
          token.text || ""
        }</span>`;
      }
      return token.text || "";
    })
    .join("");
}

// Функция для экранирования HTML
const escapeHtml = (text: string): string => {
  const div = document.createElement("div");
  div.textContent = text;
  return div.innerHTML;
};

// Маппинг алиасов языков
const languageAliases: Record<string, string> = {
  js: "javascript",
  ts: "typescript",
  py: "python",
  sh: "bash",
  shell: "bash",
  zsh: "bash",
  yml: "yaml",
  md: "markdown",
  html: "markup",
  xml: "markup",
  svg: "markup",
  mathml: "markup",
  ssml: "markup",
  atom: "markup",
  rss: "markup",
};

// Функция для нормализации названия языка
const normalizeLanguage = (lang: string): string => {
  const normalized = lang.toLowerCase().trim();
  // Проверяем алиасы
  return languageAliases[normalized] || normalized;
};

// Функция для подсветки синтаксиса кода
const highlightCode = (code: string, lang?: string): string => {
  if (!lang) {
    // Если язык не указан, просто экранируем HTML
    return escapeHtml(code);
  }

  // Нормализуем название языка
  const normalizedLang = normalizeLanguage(lang);

  try {
    // Используем highlight.js для подсветки синтаксиса
    const highlighted = hljs.highlight(code, { language: normalizedLang });
    return highlighted.value;
  } catch (e: any) {
    // Если произошла ошибка, пробуем автоопределение
    try {
      const autoHighlighted = hljs.highlightAuto(code);
      return autoHighlighted.value;
    } catch (autoError: any) {
      // Если автоопределение тоже не сработало, логируем и экранируем
      console.warn(
        "Highlight.js error:",
        e?.message || e,
        "Language:",
        normalizedLang
      );
      return escapeHtml(code);
    }
  }
};

// Кастомный renderer для внешних ссылок, чекбоксов и блоков кода
const createRenderer = (isReadOnly: boolean = false): any => {
  // Создаем новый renderer, расширяя стандартный
  const renderer = new marked.Renderer();
  
  // Переопределяем метод link для обработки внешних ссылок
  renderer.link = function(token: any) {
    const href = token.href;
    const title = token.title;

    // Используем стандартный метод для рендеринга текста ссылки
    let text = "";
    if (token.tokens && token.tokens.length > 0) {
      // Используем стандартные методы marked для рендеринга вложенных токенов
      text = renderTokens(token.tokens, this);
    } else if (token.text) {
      text = token.text;
    }

    try {
      const url = new URL(href, window.location.href);
      const isExternal = url.origin !== window.location.origin;

      if (isExternal) {
        return `<a href="${href}" title="${
          title || ""
        }" target="_blank" rel="noopener noreferrer" class="external-link">${text}</a>`;
      }
    } catch {}

    return `<a href="${href}"${title ? ` title="${title}"` : ""}>${text}</a>`;
  };

  // Переопределяем метод listitem для обработки чекбоксов
  const originalListitem = renderer.listitem.bind(renderer);
  renderer.listitem = function(token: any) {
    const task = token.task;
    const checked = token.checked;

    // Правильно обрабатываем вложенные токены для форматирования
    let content = "";
    if (token.tokens && token.tokens.length > 0) {
      // Используем стандартные методы marked для рендеринга вложенных токенов
      content = renderTokens(token.tokens, this);
    } else if (token.text) {
      // Если токенов нет, используем текст (для обратной совместимости)
      content = token.text;
    }

    if (task) {
      const disabledAttr = isReadOnly ? " disabled" : "";
      const checkbox = `<input type="checkbox" ${checked ? "checked" : ""}${disabledAttr} />`;
      return `<li class="task-list-item">${checkbox} ${content}</li>\n`;
    }
    // Для обычных элементов списка используем стандартный метод
    return originalListitem(token);
  };

  // Переопределяем метод code для подсветки синтаксиса
  renderer.code = function(token: any) {
    const code = token.text || "";
    // В marked токен блока кода имеет поле lang
    const lang = (token.lang || token.language || "").trim();

    const highlightedCode = highlightCode(code, lang);

    // Если язык указан, добавляем класс для стилизации (highlight.js использует класс hljs)
    const langClass = lang ? `language-${lang}` : "";
    const langLabel = lang ? `<span class="code-language">${lang}</span>` : "";

    return `<pre class="code-block">${langLabel}<code class="hljs ${langClass}">${highlightedCode}</code></pre>`;
  };

  return renderer;
};

// Настройка marked (базовая конфигурация)
marked.use({
  extensions: [spoilerExtension],
  gfm: true,
  breaks: true,
});

export const parseMarkdown = (text: string, isReadOnly: boolean = false): string => {
  const renderer = createRenderer(isReadOnly);
  // Используем marked.parse с renderer
  const result = marked.parse(text, { renderer });
  return result as string;
};

// Функция для извлечения тегов из текста
export const extractTags = (content: string): string[] => {
  const tagRegex = /#([а-яё\w]+)/gi;
  const tags: string[] = [];
  let match;

  while ((match = tagRegex.exec(content)) !== null) {
    const matchIndex = match.index;
    const beforeContext = content.substring(
      Math.max(0, matchIndex - 100),
      matchIndex
    );
    const afterContext = content.substring(
      matchIndex + match[0].length,
      Math.min(content.length, matchIndex + match[0].length + 100)
    );

    const lastOpenTag = beforeContext.lastIndexOf("<");
    const lastCloseTag = beforeContext.lastIndexOf(">");

    if (lastOpenTag > lastCloseTag) {
      continue;
    }

    const lastQuote = Math.max(
      beforeContext.lastIndexOf('"'),
      beforeContext.lastIndexOf("'")
    );
    const lastEquals = beforeContext.lastIndexOf("=");

    if (lastEquals > -1 && lastQuote > lastEquals) {
      const nextQuote = Math.min(
        afterContext.indexOf('"') !== -1 ? afterContext.indexOf('"') : Infinity,
        afterContext.indexOf("'") !== -1 ? afterContext.indexOf("'") : Infinity
      );
      if (nextQuote !== Infinity) {
        continue;
      }
    }

    const tag = match[1];
    // Проверяем, есть ли уже тег с таким же именем (регистронезависимо)
    if (!tags.some((t) => t.toLowerCase() === tag.toLowerCase())) {
      tags.push(tag);
    }
  }

  return tags;
};

// Функция для преобразования тегов в кликабельные элементы
export const makeTagsClickable = (content: string): string => {
  const tagRegex = /#([а-яё\w]+)/gi;
  const matches: Array<{ fullMatch: string; tag: string; index: number }> = [];
  let match;

  while ((match = tagRegex.exec(content)) !== null) {
    matches.push({
      fullMatch: match[0],
      tag: match[1],
      index: match.index,
    });
  }

  let result = content;
  for (let i = matches.length - 1; i >= 0; i--) {
    const match = matches[i];
    const beforeTag = result.substring(0, match.index);
    const afterTag = result.substring(match.index + match.fullMatch.length);

    const lastOpenTag = beforeTag.lastIndexOf("<");
    const lastCloseTag = beforeTag.lastIndexOf(">");

    if (lastOpenTag > lastCloseTag) {
      continue;
    }

    const beforeContext = beforeTag.substring(Math.max(0, match.index - 100));
    const lastQuote = Math.max(
      beforeContext.lastIndexOf('"'),
      beforeContext.lastIndexOf("'")
    );
    const lastEquals = beforeContext.lastIndexOf("=");

    if (lastEquals > -1 && lastQuote > lastEquals) {
      const afterContext = afterTag.substring(
        0,
        Math.min(100, afterTag.length)
      );
      const nextQuote = Math.min(
        afterContext.indexOf('"') !== -1 ? afterContext.indexOf('"') : Infinity,
        afterContext.indexOf("'") !== -1 ? afterContext.indexOf("'") : Infinity
      );
      if (nextQuote !== Infinity) {
        continue;
      }
    }

    const replacement = `<span class="tag-in-note" data-tag="${match.tag}">${match.fullMatch}</span>`;
    result = beforeTag + replacement + afterTag;
  }

  return result;
};

// Функция для подсветки найденного текста
export const highlightSearchText = (content: string, query: string): string => {
  if (!query.trim()) return content;

  const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  const regex = new RegExp(`(${escapedQuery})`, "gi");
  return content.replace(regex, '<mark class="search-highlight">$1</mark>');
};
