import { api } from "@/lib/api-client";
import type {
  CoursePlan,
  CoursePlanAssignment,
  CoursePlanDeliverables,
  CoursePlanGenerateBrief,
  CoursePlanMaterial,
  CoursePlanMedia,
  CoursePlanSource,
  CoursePlanSourceKind,
  WorkbookAttempt,
  WorkbookExtractedFrom,
  WorkbookGroundedSource,
} from "@/types";

/**
 * REST helpers for the AI course plan generator.
 *
 * The backend routes are mounted under `/api/ai/course-plan`; see
 * `backend/custom_addons/encoach_ai_course/controllers/course_plan.py`.
 */
export const coursePlanService = {
  // ---------------------------------------------------------------------
  // Plan lifecycle
  // ---------------------------------------------------------------------
  async list(params?: {
    page?: number;
    size?: number;
    search?: string;
  }): Promise<{ items: CoursePlan[]; page: { page: number; size: number; total: number } }> {
    return api.get("/ai/course-plan", params);
  },

  async get(planId: number): Promise<{ data: CoursePlan }> {
    return api.get(`/ai/course-plan/${planId}`);
  },

  async generate(brief: CoursePlanGenerateBrief): Promise<{ data: CoursePlan }> {
    return api.post("/ai/course-plan", brief);
  },

  async remove(planId: number): Promise<{ success: boolean }> {
    return api.delete(`/ai/course-plan/${planId}`);
  },

  async generateWeekMaterials(
    planId: number,
    weekNumber: number,
  ): Promise<{ items: CoursePlanMaterial[]; count: number }> {
    return api.post(`/ai/course-plan/${planId}/weeks/${weekNumber}/materials`);
  },

  async listWeekMaterials(
    planId: number,
    weekNumber: number,
  ): Promise<{ items: CoursePlanMaterial[]; count: number }> {
    return api.get(`/ai/course-plan/${planId}/weeks/${weekNumber}/materials`);
  },

  /**
   * RAG-grounded richer-schema generator. Requires at least one
   * indexed source on the plan; backend returns 409 otherwise so the
   * UI can prompt for upload.
   */
  async generateWeekMaterialsV2(
    planId: number,
    weekNumber: number,
  ): Promise<{
    items: CoursePlanMaterial[];
    count: number;
    rag: { enabled: boolean; sources_used: number[] };
  }> {
    return api.post(
      `/ai/course-plan/${planId}/weeks/${weekNumber}/materials/v2`,
    );
  },

  /** Mine indexed PDFs for original printed exercises. */
  async extractWorkbooks(
    planId: number,
    payload?: { max_batches?: number },
  ): Promise<{
    data: {
      materials_created: number;
      exercises_total: number;
      batches_run: number;
      skipped: number;
      reason?: string;
    };
    plan_id: number;
  }> {
    return api.post(
      `/ai/course-plan/${planId}/extract-workbooks`,
      payload ?? {},
    );
  },

  /** Source citations for one material — shown as the
   *  "Grounded on N references" badge popover. */
  async materialGrounding(materialId: number): Promise<{
    material_id: number;
    grounded_on: WorkbookGroundedSource[];
    extracted_from: WorkbookExtractedFrom | null;
    sources: CoursePlanSource[];
  }> {
    return api.get(`/ai/course-plan/material/${materialId}/grounding`);
  },

  async updateMaterial(
    materialId: number,
    payload: {
      title?: string;
      summary?: string;
      body?: Record<string, unknown>;
      body_text?: string;
      share_date?: string | null;
      is_static?: boolean;
    },
  ): Promise<{ data: CoursePlanMaterial }> {
    return api.patch(`/ai/course-plan/material/${materialId}`, payload);
  },

  // ---------------------------------------------------------------------
  // Phase A — Sources
  // ---------------------------------------------------------------------
  async listSources(planId: number): Promise<{ items: CoursePlanSource[]; count: number }> {
    return api.get(`/ai/course-plan/${planId}/sources`);
  },

  async createSource(
    planId: number,
    payload:
      | { kind: "url"; url: string; name?: string; auto_index?: boolean }
      | { kind: "text"; inline_text: string; name?: string; auto_index?: boolean },
  ): Promise<{ data: CoursePlanSource }> {
    return api.post(`/ai/course-plan/${planId}/sources`, payload);
  },

  async uploadSource(
    planId: number,
    file: File,
    opts?: { name?: string; auto_index?: boolean },
  ): Promise<{ data: CoursePlanSource }> {
    const fd = new FormData();
    fd.append("file", file);
    fd.append("kind", "file" satisfies CoursePlanSourceKind);
    if (opts?.name) fd.append("name", opts.name);
    if (opts?.auto_index !== undefined) {
      fd.append("auto_index", opts.auto_index ? "1" : "0");
    }
    return api.upload(`/ai/course-plan/${planId}/sources`, fd);
  },

  async reindexSource(
    planId: number,
    sourceId: number,
  ): Promise<{ data: CoursePlanSource }> {
    return api.post(
      `/ai/course-plan/${planId}/sources/${sourceId}/index`,
    );
  },

  async deleteSource(
    planId: number,
    sourceId: number,
  ): Promise<{ success: boolean }> {
    return api.delete(`/ai/course-plan/${planId}/sources/${sourceId}`);
  },

  /**
   * Attach existing items from /admin/resources to a course plan as RAG
   * sources. Returns the rows that were newly attached plus the ids
   * that were skipped (already linked) or missing (deleted from the
   * library) so the caller can show a clear toast — e.g. "Attached 2,
   * skipped 1 already-linked".
   */
  async attachResources(
    planId: number,
    resourceIds: number[],
  ): Promise<{
    attached: CoursePlanSource[];
    skipped_existing: number[];
    missing: number[];
    count: number;
  }> {
    return api.post(
      `/ai/course-plan/${planId}/sources/from-resources`,
      { resource_ids: resourceIds },
    );
  },

  // ---------------------------------------------------------------------
  // Phase B — Deliverables / progress
  // ---------------------------------------------------------------------
  async deliverables(planId: number): Promise<CoursePlanDeliverables> {
    return api.get(`/ai/course-plan/${planId}/deliverables`);
  },

  // ---------------------------------------------------------------------
  // Phase C — Multimedia
  // ---------------------------------------------------------------------
  async listMaterialMedia(
    materialId: number,
  ): Promise<{ items: CoursePlanMedia[]; count: number }> {
    return api.get(`/ai/course-plan/material/${materialId}/media`);
  },

  async generateAudio(
    materialId: number,
    payload?: {
      voice?: string;
      language?: string;
      gender?: "male" | "female";
      provider?: "polly" | "elevenlabs";
    },
  ): Promise<{ data: CoursePlanMedia }> {
    return api.post(
      `/ai/course-plan/material/${materialId}/media/audio`,
      payload ?? {},
    );
  },

  async generateImage(
    materialId: number,
    payload?: {
      prompt?: string;
      size?: "1024x1024" | "1792x1024" | "1024x1792";
      style?: "natural" | "vivid";
      quality?: "standard" | "hd";
    },
  ): Promise<{ data: CoursePlanMedia }> {
    return api.post(
      `/ai/course-plan/material/${materialId}/media/image`,
      payload ?? {},
    );
  },

  async composeVideo(materialId: number): Promise<{ data: CoursePlanMedia }> {
    return api.post(
      `/ai/course-plan/material/${materialId}/media/video`,
    );
  },

  async deleteMedia(mediaId: number): Promise<{ success: boolean }> {
    return api.delete(`/ai/course-plan/media/${mediaId}`);
  },

  /**
   * Attach an admin-supplied media file (audio / image / video) to a
   * course-plan material. The backend tags the resulting row with
   * `provider='manual'` so the drawer can distinguish hand-curated
   * clips from AI-generated ones — and admins can swap a robotic
   * Polly recording for a real human voiceover whenever they want.
   */
  async uploadMedia(
    materialId: number,
    kind: "audio" | "image" | "video",
    file: File,
    title?: string,
  ): Promise<{ data: CoursePlanMedia }> {
    const fd = new FormData();
    fd.append("kind", kind);
    fd.append("file", file);
    if (title) fd.append("title", title);
    return api.upload(
      `/ai/course-plan/material/${materialId}/media/upload`,
      fd,
    );
  },

  async generateWeekMedia(
    planId: number,
    weekNumber: number,
    payload?: { kinds?: Array<"audio" | "image" | "video"> },
  ): Promise<{ items: Array<CoursePlanMedia | { error: string; material_id: number }>; count: number }> {
    return api.post(
      `/ai/course-plan/${planId}/weeks/${weekNumber}/media`,
      payload ?? {},
    );
  },

  // ---------------------------------------------------------------------
  // Phase D — Assignments
  // ---------------------------------------------------------------------
  async listAssignments(
    planId: number,
  ): Promise<{ items: CoursePlanAssignment[]; count: number }> {
    return api.get(`/ai/course-plan/${planId}/assignments`);
  },

  async createAssignment(
    planId: number,
    payload:
      | {
          mode: "batch";
          batch_id: number;
          due_date?: string | null;
          message?: string;
        }
      | {
          mode: "students";
          student_user_ids: number[];
          due_date?: string | null;
          message?: string;
        }
      | {
          mode: "entities";
          entity_ids: number[];
          due_date?: string | null;
          message?: string;
        },
  ): Promise<{ data: CoursePlanAssignment }> {
    return api.post(`/ai/course-plan/${planId}/assignments`, payload);
  },

  async deleteAssignment(
    planId: number,
    assignmentId: number,
  ): Promise<{ success: boolean }> {
    return api.delete(
      `/ai/course-plan/${planId}/assignments/${assignmentId}`,
    );
  },

  // ---------------------------------------------------------------------
  // Phase E — Student-side
  // ---------------------------------------------------------------------
  async studentList(): Promise<{ items: CoursePlan[]; count: number }> {
    return api.get("/student/course-plans");
  },

  async studentGet(planId: number): Promise<{ data: CoursePlan }> {
    return api.get(`/student/course-plans/${planId}`);
  },

  // ---------------------------------------------------------------------
  // Phase F — Interactive workbook attempts
  // ---------------------------------------------------------------------
  /** Save (or finalize) the current student's answers and return the
   *  freshly-graded score. Used by InteractiveWorkbook.tsx on every
   *  "Check answers" click (debounced) and on "Submit final". */
  async saveWorkbookAttempt(
    planId: number,
    materialId: number,
    payload: { answers: Record<string, unknown>; finalize?: boolean },
  ): Promise<{ data: WorkbookAttempt }> {
    return api.post(
      `/student/course-plans/${planId}/materials/${materialId}/attempts`,
      payload,
    );
  },

  /** Latest attempt the current user owns on this material — null when
   *  the student hasn't started yet. */
  async myWorkbookAttempt(
    planId: number,
    materialId: number,
  ): Promise<{ data: WorkbookAttempt | null }> {
    return api.get(
      `/student/course-plans/${planId}/materials/${materialId}/attempts/me`,
    );
  },

  /** Teacher-side roster of latest attempts per student. */
  async listMaterialAttempts(
    planId: number,
    materialId: number,
  ): Promise<{ items: WorkbookAttempt[]; count: number }> {
    return api.get(
      `/ai/course-plan/${planId}/materials/${materialId}/attempts`,
    );
  },
};
