import type { Post, SiteSettings } from './types';
import { buildCategoryUrl, buildTagUrl } from './utils';

export interface ArticleFaqItem {
  question: string;
  answer: string;
}

export interface DiscoveryFaqOptions {
  locale: string;
  pageTitle: string;
  summary: string;
  primaryUrl: string;
  primaryLabel: string;
  relatedLinks?: Array<{
    label: string;
    url: string;
  }>;
  signals?: string[];
}

export type JsonLdObject = Record<string, unknown>;

function normalizeWhitespace(value: string): string {
  return value.replace(/\s+/g, ' ').trim();
}

export function stripMarkdown(value: string): string {
  return normalizeWhitespace(
    value
      .replace(/^---[\s\S]*?---/, ' ')
      .replace(/!\[[^\]]*]\([^)]*\)/g, ' ')
      .replace(/\[([^\]]+)]\([^)]*\)/g, '$1')
      .replace(/```[\s\S]*?```/g, ' ')
      .replace(/`([^`\n]+)`/g, '$1')
      .replace(/^#{1,6}\s+/gm, '')
      .replace(/^\s*[-*+]\s+/gm, '')
      .replace(/^\s*\d+\.\s+/gm, '')
      .replace(/<[^>]+>/g, ' ')
      .replace(/[>*_~|]/g, ' ')
  );
}

function truncate(value: string, maxLength: number): string {
  if (value.length <= maxLength) {
    return value;
  }

  return `${value.slice(0, Math.max(0, maxLength - 1)).trimEnd()}…`;
}

function uniqueNonEmpty(values: string[]): string[] {
  const seen = new Set<string>();

  return values.filter((value) => {
    const normalized = normalizeWhitespace(value).toLowerCase();
    if (!normalized || seen.has(normalized)) {
      return false;
    }

    seen.add(normalized);
    return true;
  });
}

export function splitMarkdownParagraphs(value: string): string[] {
  return value
    .replace(/\r\n/g, '\n')
    .split(/\n{2,}/)
    .map((item) => stripMarkdown(item))
    .map((item) => item.replace(/^#\s+/, '').trim())
    .filter(Boolean);
}

function splitSentences(value: string): string[] {
  return value
    .split(/(?<=[。！？!?；;])\s*|(?<=\.)\s+/)
    .map((item) => normalizeWhitespace(item))
    .filter((item) => item.length >= 8);
}

export function buildArticleSynopsis(
  post: Pick<Post, 'title' | 'description' | 'content'>,
  maxLength = 220,
): string {
  const contentParagraphs = splitMarkdownParagraphs(post.content || '').filter(
    (item) => normalizeWhitespace(item) !== normalizeWhitespace(post.title),
  );
  const parts = uniqueNonEmpty([post.description, ...contentParagraphs].filter(Boolean));

  return truncate(parts.join(' '), maxLength);
}

export function buildArticleHighlights(
  post: Pick<Post, 'title' | 'description' | 'content'>,
  limit = 3,
): string[] {
  const paragraphs = splitMarkdownParagraphs(post.content || '').filter(
    (item) => normalizeWhitespace(item) !== normalizeWhitespace(post.title),
  );
  const sentences = uniqueNonEmpty(
    [post.description, ...paragraphs]
      .filter(Boolean)
      .flatMap((item) => splitSentences(item || '')),
  );

  return sentences.slice(0, limit).map((item) => truncate(item, 88));
}

export function buildArticlePreviewParagraphs(
  post: Pick<Post, 'title' | 'description' | 'content'>,
  maxParagraphs = 3,
  maxLength = 118,
): string[] {
  const descriptionText = normalizeWhitespace(post.description || '');
  const rawParagraphs = (post.content || '')
    .replace(/\r\n/g, '\n')
    .split(/\n{2,}/)
    .map((item) => item.trim())
    .filter(Boolean);

  const paragraphs = uniqueNonEmpty(
    rawParagraphs
      .filter((item) => !/^\s*#{1,6}\s+/.test(item))
      .filter((item) => !/^\s*(?:[-*+]|\d+\.)\s+/.test(item))
      .filter((item) => !/^\s*```/.test(item))
      .map((item) => stripMarkdown(item))
      .filter((item) => {
        const normalized = normalizeWhitespace(item);
        return (
          normalized &&
          normalized !== normalizeWhitespace(post.title) &&
          normalized !== descriptionText &&
          normalized.length >= 18
        );
      }),
  );

  const previewParagraphs = paragraphs.slice(0, maxParagraphs).map((item) => truncate(item, maxLength));

  if (previewParagraphs.length > 0) {
    return previewParagraphs;
  }

  return uniqueNonEmpty([post.description].filter(Boolean))
    .slice(0, 1)
    .map((item) => truncate(item, maxLength));
}

export function resolvePostUpdatedAt(post: Pick<Post, 'updatedAt' | 'publishAt' | 'createdAt' | 'date'>): string {
  return post.updatedAt || post.publishAt || post.createdAt || post.date;
}

export function buildArticleFaqs(
  post: Pick<Post, 'title' | 'category' | 'tags'>,
  options: {
    locale: string;
    summary: string;
    readTimeMinutes: number;
  },
): ArticleFaqItem[] {
  const isEnglish = options.locale.startsWith('en');
  const tags = post.tags.slice(0, 5);
  const keywordList = tags.length ? tags.join(isEnglish ? ', ' : '、') : post.category;
  const categoryUrl = buildCategoryUrl(post.category);
  const tagUrls = tags.slice(0, 2).map((tag) => buildTagUrl(tag));

  const items = isEnglish
    ? [
        {
          question: `What is "${post.title}" mainly about?`,
          answer: options.summary,
        },
        {
          question: 'What keywords or topics appear in this page?',
          answer: `This page belongs to ${post.category} and highlights ${keywordList}. Estimated reading time is about ${Math.max(
            options.readTimeMinutes,
            1,
          )} minute(s).`,
        },
        {
          question: 'Where should I continue if I want related content?',
          answer: `Start with the category page ${categoryUrl} and then continue with related tags ${tagUrls.join(
            ', ',
          ) || buildTagUrl('')}.`,
        },
      ]
    : [
        {
          question: `《${post.title}》主要讲什么？`,
          answer: options.summary,
        },
        {
          question: '这页内容涉及哪些关键词？',
          answer: `这篇内容归档在「${post.category}」，重点关键词包括 ${keywordList}，预计阅读时间约 ${Math.max(
            options.readTimeMinutes,
            1,
          )} 分钟。`,
        },
        {
          question: '如果想继续阅读相关内容，应该从哪里开始？',
          answer: `建议先看分类页 ${categoryUrl}，再继续浏览相关标签 ${tagUrls.join('、') || buildTagUrl('')}。`,
        },
      ];

  return items.map((item) => ({
    question: truncate(item.question, 120),
    answer: truncate(item.answer, 320),
  }));
}

export function buildDiscoveryHighlights(values: string[], limit = 3, maxLength = 96): string[] {
  return uniqueNonEmpty(values.map((item) => normalizeWhitespace(item)).filter(Boolean))
    .slice(0, limit)
    .map((item) => truncate(item, maxLength));
}

export function buildPageFaqs(options: DiscoveryFaqOptions): ArticleFaqItem[] {
  const isEnglish = options.locale.startsWith('en');
  const relatedLinks = (options.relatedLinks || []).slice(0, 3);
  const relatedText = relatedLinks
    .map((item) => `${item.label}: ${item.url}`)
    .join(isEnglish ? '; ' : '；');
  const signalText = uniqueNonEmpty(options.signals || []).join(isEnglish ? ', ' : '、');

  const items = isEnglish
    ? [
        {
          question: `What is the main purpose of "${options.pageTitle}"?`,
          answer: options.summary,
        },
        {
          question: 'Which URL should be cited as the canonical source?',
          answer: `Use ${options.primaryLabel} as the canonical page: ${options.primaryUrl}.`,
        },
        {
          question: 'Where should I continue for related information?',
          answer: relatedText || signalText
            ? `Continue with ${relatedText || signalText}.`
            : `Continue from the canonical page ${options.primaryUrl}.`,
        },
      ]
    : [
        {
          question: `「${options.pageTitle}」这一页的核心作用是什么？`,
          answer: options.summary,
        },
        {
          question: '这一页应该引用哪个规范地址？',
          answer: `优先引用 ${options.primaryLabel} 的规范地址：${options.primaryUrl}。`,
        },
        {
          question: '如果要继续浏览相关内容，应该看哪里？',
          answer: relatedText || signalText
            ? `建议继续查看：${relatedText || signalText}。`
            : `建议从这个规范页继续展开：${options.primaryUrl}。`,
        },
      ];

  return items.map((item) => ({
    question: truncate(item.question, 120),
    answer: truncate(item.answer, 320),
  }));
}

export function buildFaqJsonLd(faqs: ArticleFaqItem[]) {
  if (!faqs.length) {
    return undefined;
  }

  return {
    '@context': 'https://schema.org',
    '@type': 'FAQPage',
    mainEntity: faqs.map((item) => ({
      '@type': 'Question',
      name: item.question,
      acceptedAnswer: {
        '@type': 'Answer',
        text: item.answer,
      },
    })),
  };
}

export function compactJsonLd<T extends JsonLdObject>(
  items: Array<T | null | undefined | false>,
): T[] {
  return items.filter((item): item is T => Boolean(item));
}

export function buildPostItemList(posts: Post[], siteUrl: string) {
  return posts.map((post, index) => ({
    '@type': 'ListItem',
    position: index + 1,
    url: new URL(`/articles/${post.slug}`, siteUrl).toString(),
    name: post.title,
    description: post.description,
  }));
}

export function buildPageTrackerLabel(referrer: string | null | undefined): string {
  const source = normalizeWhitespace(referrer || '').toLowerCase();
  if (!source) {
    return 'direct';
  }

  if (source.includes('chatgpt') || source.includes('openai')) return 'chatgpt-search';
  if (source.includes('perplexity')) return 'perplexity';
  if (source.includes('copilot') || source.includes('bing')) return 'copilot-bing';
  if (source.includes('gemini')) return 'gemini';
  if (source.includes('google')) return 'google';
  if (source.includes('claude')) return 'claude';
  if (source.includes('duckduckgo')) return 'duckduckgo';
  if (source.includes('kagi')) return 'kagi';

  return source;
}

export function buildSiteTopicSummary(siteSettings: SiteSettings): string[] {
  return uniqueNonEmpty([
    siteSettings.siteDescription,
    siteSettings.heroSubtitle,
    siteSettings.ownerBio,
    ...siteSettings.techStack.slice(0, 6).map((item) => `${siteSettings.siteName} covers ${item}`),
  ]).slice(0, 4);
}
