/**
 * Built-in stage: section-split
 * Splits content into named sections based on content type.
 *
 * - Prose/Markdown → split on ## headers
 * - JSON array → split on array elements
 * - JSON object → split on top-level keys
 * - YAML → split on top-level keys
 * - Code → split on function/class boundaries
 *
 * Config:
 *   minSectionSize: number (don't split tiny sections, default 500)
 *   maxSectionSize: number (re-split large sections, default 15000)
 */
import type { StageHandler, Section } from '../types.js';
import { detectContentType } from '../content-type.js';

const handler: StageHandler = async (content, ctx) => {
  const minSize = (ctx.config.minSectionSize as number | undefined) ?? 500;
  const maxSize = (ctx.config.maxSectionSize as number | undefined) ?? 15000;

  // Don't split tiny content
  if (content.length < minSize * 2) {
    return { content };
  }

  const contentType = detectContentType(content);
  let sections: Section[];

  switch (contentType) {
    case 'json':
      sections = splitJson(content);
      break;
    case 'yaml':
      sections = splitYaml(content);
      break;
    case 'xml':
      sections = splitXml(content);
      break;
    case 'code':
      sections = splitCode(content);
      break;
    case 'prose':
    default:
      sections = splitProse(content);
      break;
  }

  // Filter out tiny sections (merge into previous)
  sections = mergeTinySections(sections, minSize);

  // Re-split oversized sections
  sections = splitOversized(sections, maxSize);

  if (sections.length <= 1) {
    return { content };
  }

  // Build table of contents
  const toc = sections.map((s) => {
    const sizeHint = s.content.length > 1000
      ? ` (${Math.round(s.content.length / 1000)}K chars)`
      : ` (${s.content.length} chars)`;
    return `[${s.id}] ${s.title}${sizeHint}`;
  }).join('\n');

  return {
    content: `${sections.length} sections (${contentType}):\n${toc}\n\nUse section parameter to read a specific section.`,
    sections,
  };
};

// ── JSON Splitting ──────────────────────────────────────────────────

function splitJson(content: string): Section[] {
  try {
    const parsed = JSON.parse(content) as unknown;

    if (Array.isArray(parsed)) {
      return splitJsonArray(parsed);
    }
    if (parsed !== null && typeof parsed === 'object') {
      return splitJsonObject(parsed as Record<string, unknown>);
    }
  } catch {
    // Invalid JSON — fall through to prose
  }
  return splitProse(content);
}

function splitJsonArray(arr: unknown[]): Section[] {
  return arr.map((item, i) => {
    const obj = item as Record<string, unknown> | undefined;
    const label = obj?.label ?? obj?.name ?? obj?.id ?? obj?.title ?? obj?.type;
    const id = String(label ?? `item-${i}`).toLowerCase().replace(/[^a-z0-9]+/g, '-');
    const title = label ? String(label) : `Item ${i + 1}`;
    return {
      id,
      title,
      content: JSON.stringify(item, null, 2),
    };
  });
}

function splitJsonObject(obj: Record<string, unknown>): Section[] {
  return Object.entries(obj).map(([key, value]) => ({
    id: key.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
    title: key,
    content: JSON.stringify(value, null, 2),
  }));
}

// ── YAML Splitting ──────────────────────────────────────────────────

function splitYaml(content: string): Section[] {
  const sections: Section[] = [];
  const lines = content.split('\n');
  let currentKey = '';
  let currentLines: string[] = [];

  for (const line of lines) {
    const match = /^([a-zA-Z_][a-zA-Z0-9_-]*):\s/.exec(line);
    if (match) {
      if (currentKey && currentLines.length > 0) {
        sections.push({
          id: currentKey.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
          title: currentKey,
          content: currentLines.join('\n'),
        });
      }
      currentKey = match[1]!;
      currentLines = [line];
    } else {
      currentLines.push(line);
    }
  }

  if (currentKey && currentLines.length > 0) {
    sections.push({
      id: currentKey.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
      title: currentKey,
      content: currentLines.join('\n'),
    });
  }

  return sections;
}

// ── XML Splitting ───────────────────────────────────────────────────

function splitXml(content: string): Section[] {
  // Simple regex-based splitting on top-level elements
  const sections: Section[] = [];
  const tagRegex = /<([a-zA-Z][a-zA-Z0-9]*)[^>]*>[\s\S]*?<\/\1>/g;
  let match: RegExpExecArray | null;

  while ((match = tagRegex.exec(content)) !== null) {
    const tagName = match[1]!;
    sections.push({
      id: tagName.toLowerCase(),
      title: tagName,
      content: match[0],
    });
  }

  if (sections.length === 0) {
    return splitProse(content);
  }

  return sections;
}

// ── Code Splitting ──────────────────────────────────────────────────

function splitCode(content: string): Section[] {
  const sections: Section[] = [];
  // Split on function/class/export boundaries
  const boundaries = /^(?:export\s+)?(?:async\s+)?(?:function|class|const|let|var|def|module)\s+(\w+)/gm;
  let lastIndex = 0;
  let lastName = 'preamble';
  let match: RegExpExecArray | null;

  while ((match = boundaries.exec(content)) !== null) {
    if (match.index > lastIndex) {
      const block = content.slice(lastIndex, match.index).trim();
      if (block) {
        sections.push({
          id: lastName.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
          title: lastName,
          content: block,
        });
      }
    }
    lastName = match[1]!;
    lastIndex = match.index;
  }

  // Remaining content
  if (lastIndex < content.length) {
    const block = content.slice(lastIndex).trim();
    if (block) {
      sections.push({
        id: lastName.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
        title: lastName,
        content: block,
      });
    }
  }

  return sections;
}

// ── Prose/Markdown Splitting ────────────────────────────────────────

function splitProse(content: string): Section[] {
  const sections: Section[] = [];
  // Split on markdown ## headers (any level)
  const parts = content.split(/^(#{1,4}\s+.+)$/m);

  let currentTitle = 'Introduction';
  let currentContent = '';

  for (const part of parts) {
    const headerMatch = /^#{1,4}\s+(.+)$/.exec(part);
    if (headerMatch) {
      if (currentContent.trim()) {
        sections.push({
          id: currentTitle.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
          title: currentTitle,
          content: currentContent.trim(),
        });
      }
      currentTitle = headerMatch[1]!.trim();
      currentContent = '';
    } else {
      currentContent += part;
    }
  }

  if (currentContent.trim()) {
    sections.push({
      id: currentTitle.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
      title: currentTitle,
      content: currentContent.trim(),
    });
  }

  return sections;
}

// ── Helpers ─────────────────────────────────────────────────────────

function mergeTinySections(sections: Section[], minSize: number): Section[] {
  if (sections.length <= 1) return sections;

  const merged: Section[] = [];
  for (const section of sections) {
    const prev = merged[merged.length - 1];
    if (prev && section.content.length < minSize) {
      prev.content += '\n\n' + section.content;
      prev.title += ' + ' + section.title;
    } else {
      merged.push({ ...section });
    }
  }
  return merged;
}

function splitOversized(sections: Section[], maxSize: number): Section[] {
  const result: Section[] = [];
  for (const section of sections) {
    if (section.content.length <= maxSize) {
      result.push(section);
      continue;
    }
    // Split oversized section by paragraph breaks
    const paragraphs = section.content.split(/\n\n+/);
    let chunk = '';
    let partNum = 1;

    for (const para of paragraphs) {
      if (chunk.length + para.length > maxSize && chunk) {
        result.push({
          id: `${section.id}-part${partNum}`,
          title: `${section.title} (part ${partNum})`,
          content: chunk.trim(),
        });
        partNum++;
        chunk = '';
      }
      chunk += (chunk ? '\n\n' : '') + para;
    }
    if (chunk.trim()) {
      result.push({
        id: partNum > 1 ? `${section.id}-part${partNum}` : section.id,
        title: partNum > 1 ? `${section.title} (part ${partNum})` : section.title,
        content: chunk.trim(),
      });
    }
  }
  return result;
}

export default handler;
