/**
 * Zod schemas for every structured LLM output the platform consumes.
 *
 * Field names mirror what callers already produce — do not rename them
 * without migrating the corresponding service module.
 */

import { z } from 'zod';

const topicTypeEnum = z.enum(['concept', 'role', 'process']);
const relationTypeStrict = z.enum(['related_to', 'depends_on', 'part_of', 'executed_by']);
const learningRelevanceEnum = z.enum(['core', 'standard', 'peripheral', 'exclude']);

const extractionTopicSchema = z.object({
  id: z.string().min(1),
  label: z.string().min(1),
  type: topicTypeEnum,
  description: z.string().min(1),
  learning_relevance: learningRelevanceEnum,
});

const extractionRelationSchema = z.object({
  source: z.string().min(1),
  target: z.string().min(1),
  type: relationTypeStrict,
});

export const extractionResultSchema = z.object({
  topics: z.array(extractionTopicSchema),
  relations: z.array(extractionRelationSchema),
});

const curriculumWeekSchema = z.object({
  week_number: z.number().int().min(1).max(26),
  theme: z.string().min(1),
  topic_ids: z.array(z.string().min(1)).min(1),
  estimated_duration: z.number().int().min(15).max(45),
  week_rationale: z.string().min(1),
});

export const curriculumScheduleSchema = z.object({
  weeks: z.array(curriculumWeekSchema).length(26),
});

const topicEnrichmentSchemaDef = z.object({
  id: z.string().min(1),
  theme: z.string().min(1),
  complexity_weight: z.number().int().min(1).max(5),
  difficulty: z.enum(['introductory', 'intermediate', 'advanced']),
});

export const topicEnrichmentSchema = z.object({
  topics: z.array(topicEnrichmentSchemaDef).min(1),
});


const articleSectionSchema = z.object({
  heading: z.string().min(1),
  body: z.string().min(1),
});

const articleBodySchema = z.object({
  title: z.string().min(1),
  intro: z.string().min(1),
  sections: z.array(articleSectionSchema).min(1),
  keyTakeaways: z.array(z.string().min(1)).min(1),
});

export const learningArticleSchema = z.object({
  article: articleBodySchema,
});

const slideSchema = z.object({
  title: z.string().min(1),
  bullets: z.array(z.string().min(1)).min(1),
  speakerNote: z.string().min(1),
});

export const learningSlidesSchema = z.object({
  slides: z.array(slideSchema).min(1),
});

const infographicStatSchema = z.object({
  value: z.string().min(1),
  label: z.string().min(1),
  icon: z.string().min(1),
});

const infographicStepSchema = z.object({
  number: z.number().int().min(1),
  title: z.string().min(1),
  description: z.string().min(1),
  icon: z.string().min(1),
});

const infographicBodySchema = z.object({
  headline: z.string().min(1),
  tagline: z.string().min(1),
  stats: z.array(infographicStatSchema).min(1),
  steps: z.array(infographicStepSchema).min(1),
  quote: z.string().min(1),
  colorTheme: z.string().min(1),
});

export const learningInfographicSchema = z.object({
  infographic: infographicBodySchema,
});

export const learningAllSchema = z.object({
  article: articleBodySchema,
  slides: z.array(slideSchema).min(1),
  infographic: infographicBodySchema,
});

const quizDifficultyEnum = z.enum(['easy', 'medium', 'hard']).catch('medium');

const quizQuestionSchema = z.object({
  id: z.string().min(1).catch(`gen-${Math.random().toString(36).slice(2, 8)}`),
  question: z.string().min(1),
  topicLabel: z.string().min(1).catch('General'),
  options: z.array(z.string().min(1)).length(4),
  correctIndex: z.preprocess(
    (v) => (typeof v === 'number' ? Math.round(v) : v),
    z.number().int().min(0).max(3),
  ),
  explanation: z.string().min(1).catch('See the correct answer above.'),
  difficulty: quizDifficultyEnum,
});

export const quizQuestionsSchema = z.object({
  questions: z.preprocess(
    (v) => {
      // If the model returned an array directly, wrap it
      if (Array.isArray(v)) return v;
      // If it returned a single object, wrap in array
      if (v && typeof v === 'object' && !Array.isArray(v)) return [v];
      return v;
    },
    z.array(quizQuestionSchema).min(1),
  ),
});

export const customTopicSchema = z.object({
  label: z.string().min(1),
  type: topicTypeEnum,
  description: z.string().min(1),
});

const mergeActionSchema = z.object({
  keepId: z.string().min(1),
  deleteId: z.string().min(1),
});

const newRelationSchema = z.object({
  source: z.string().min(1),
  target: z.string().min(1),
  type: relationTypeStrict,
});

const relevanceUpdateSchema = z.object({
  id: z.string().min(1),
  learning_relevance: learningRelevanceEnum,
});

export const graphActionsSchema = z.object({
  merges: z.array(mergeActionSchema).optional().default([]),
  deletions: z.array(z.string().min(1)).optional().default([]),
  newRelations: z.array(newRelationSchema).optional().default([]),
  relevanceUpdates: z.array(relevanceUpdateSchema).optional().default([]),
});

const deltaTopicSchema = z.object({
  id: z.string().min(1),
  label: z.string().min(1),
  type: topicTypeEnum,
  description: z.string().min(1),
});

const deltaRelationSchema = z.object({
  source: z.string().min(1),
  target: z.string().min(1),
  type: relationTypeStrict,
});

export const proposeGraphDeltaSchema = z.object({
  reason: z.string().min(1),
  topics: z.array(deltaTopicSchema).max(3).optional(),
  relations: z.array(deltaRelationSchema).max(5).optional(),
});

// ── Article patch operation schemas (Phase 2.4) ──────────────────────────────

export const setIntroPatchSchema = z.object({
  intro: z.string().min(1),
});

export const setSectionPatchSchema = z.object({
  heading: z.string().min(1),
  body: z.string().min(1),
});

export const addSectionPatchSchema = z.object({
  heading: z.string().min(1),
  body: z.string().min(1),
  position: z.enum(['start', 'end']),
});

export const removeSectionPatchSchema = z.object({
  heading: z.string().min(1),
});

export const replaceTakeawaysPatchSchema = z.object({
  items: z.array(z.string().min(1)).min(1),
});

// ── Theme session schema ─────────────────────────────────────────────────────

const themeSessionTopicSection = z.object({
  topic_id: z.string().min(1),
  label: z.string().min(1),
  summary: z.string().min(1),
});

export const themeSessionSchema = z.object({
  title: z.string().min(1),
  intro: z.string().min(1),
  topicSections: z.array(themeSessionTopicSection).min(1),
  connections: z.string().min(1),
  keyTakeaways: z.array(z.string().min(1)).min(3),
});

// Fast-tier models occasionally emit array tool fields as a JSON-encoded
// string ("[\"a\",\"b\"]") instead of a real array (issue #32). Parse those
// back to arrays before validating; leave anything else untouched so real
// type errors still fail validation.
function coerceStringifiedArray(v) {
  if (typeof v === 'string') {
    const s = v.trim();
    if (s.startsWith('[')) {
      try { return JSON.parse(s); } catch { /* keep original, let Zod report */ }
    }
  }
  return v;
}

const onboardingKeyPoints = z.preprocess((v) => {
  let out = coerceStringifiedArray(v);
  // Fallback: a bullet/newline list as one string → split into points.
  if (typeof out === 'string' && out.includes('\n')) {
    out = out
      .split('\n')
      .map((line) => line.replace(/^\s*[-•*\d.]+\s*/, '').trim())
      .filter(Boolean);
  }
  // Overage is trivial — keep the first 5 rather than failing the user.
  if (Array.isArray(out) && out.length > 5) out = out.slice(0, 5);
  return out;
}, z.array(z.string().min(1)).min(3).max(5));

export const onboardingOverviewSchema = z.object({
  title: z.string().min(1),
  what_it_is: z.string().min(1),
  why_it_matters: z.string().min(1),
  key_points: onboardingKeyPoints,
  topics_covered: z.preprocess(
    coerceStringifiedArray,
    z.array(z.object({ topic_id: z.string().min(1), label: z.string().min(1) })).min(1),
  ),
});

/**
 * Registry mapping known tool names to their input schemas. `callLLM`
 * consults this when the caller does not pass an explicit `toolSchemas`
 * override.
 */
export const toolSchemaRegistry = {
  emit_knowledge_graph: extractionResultSchema,
  emit_curriculum_schedule: curriculumScheduleSchema,
  emit_topic_enrichment: topicEnrichmentSchema,
  emit_learning_article: learningArticleSchema,
  emit_learning_slides: learningSlidesSchema,
  emit_learning_infographic: learningInfographicSchema,
  emit_learning_all: learningAllSchema,
  emit_quiz_questions: quizQuestionsSchema,
  emit_custom_topic: customTopicSchema,
  emit_graph_actions: graphActionsSchema,
  propose_graph_delta: proposeGraphDeltaSchema,
  set_intro: setIntroPatchSchema,
  set_section: setSectionPatchSchema,
  add_section: addSectionPatchSchema,
  remove_section: removeSectionPatchSchema,
  replace_takeaways: replaceTakeawaysPatchSchema,
  emit_theme_session: themeSessionSchema,
  emit_onboarding_overview: onboardingOverviewSchema,
};
