/**
 * Typed API client for Chrysopedia public endpoints.
 *
 * Mirrors backend schemas: SearchResponse, TechniquePageDetail, TopicCategory, CreatorBrowseItem.
 * Uses the same request<T> pattern as client.ts.
 */

// ── Types ───────────────────────────────────────────────────────────────────

export interface SearchResultItem {
  title: string;
  slug: string;
  type: string;
  score: number;
  summary: string;
  creator_name: string;
  creator_slug: string;
  topic_category: string;
  topic_tags: string[];
  technique_page_slug?: string;
  match_context?: string;
  section_anchor?: string;
  section_heading?: string;
}

export interface SearchResponse {
  items: SearchResultItem[];
  partial_matches: SearchResultItem[];
  total: number;
  query: string;
  fallback_used: boolean;
}

export interface KeyMomentSummary {
  id: string;
  title: string;
  summary: string;
  start_time: number;
  end_time: number;
  content_type: string;
  plugins: string[] | null;
  source_video_id: string;
  video_filename: string;
}

export interface CreatorInfo {
  name: string;
  slug: string;
  genres: string[] | null;
}

export interface RelatedLinkItem {
  target_title: string;
  target_slug: string;
  relationship: string;
  creator_name: string;
  topic_category: string;
  reason: string;
}

export interface BodySubSectionV2 {
  heading: string;
  content: string;
}

export interface BodySectionV2 {
  heading: string;
  content: string;
  subsections: BodySubSectionV2[];
}

export interface SourceVideoSummary {
  id: string;
  filename: string;
  content_type: string;
  added_at: string | null;
}

export interface TechniquePageDetail {
  id: string;
  title: string;
  slug: string;
  topic_category: string;
  topic_tags: string[] | null;
  summary: string | null;
  body_sections: BodySectionV2[] | Record<string, unknown> | null;
  body_sections_format: string;
  signal_chains: unknown[] | null;
  plugins: string[] | null;
  creator_id: string;
  source_quality: string | null;
  view_count: number;
  created_at: string;
  updated_at: string;
  key_moments: KeyMomentSummary[];
  creator_info: CreatorInfo | null;
  related_links: RelatedLinkItem[];
  version_count: number;
  source_videos: SourceVideoSummary[];
}

export interface TechniquePageVersionSummary {
  version_number: number;
  created_at: string;
  pipeline_metadata: Record<string, unknown> | null;
}

export interface TechniquePageVersionListResponse {
  items: TechniquePageVersionSummary[];
  total: number;
}

export interface TechniquePageVersionDetail {
  version_number: number;
  content_snapshot: Record<string, unknown>;
  pipeline_metadata: Record<string, unknown> | null;
  created_at: string;
}

export interface TechniqueListItem {
  id: string;
  title: string;
  slug: string;
  topic_category: string;
  topic_tags: string[] | null;
  summary: string | null;
  creator_id: string;
  creator_name: string;
  creator_slug: string;
  source_quality: string | null;
  view_count: number;
  key_moment_count: number;
  created_at: string;
  updated_at: string;
}

export interface TechniqueListResponse {
  items: TechniqueListItem[];
  total: number;
  offset: number;
  limit: number;
}

export interface TopicSubTopic {
  name: string;
  technique_count: number;
  creator_count: number;
}

export interface TopicCategory {
  name: string;
  description: string;
  sub_topics: TopicSubTopic[];
}

export interface CreatorBrowseItem {
  id: string;
  name: string;
  slug: string;
  genres: string[] | null;
  folder_name: string;
  view_count: number;
  created_at: string;
  updated_at: string;
  technique_count: number;
  video_count: number;
  last_technique_at: string | null;
}

export interface CreatorBrowseResponse {
  items: CreatorBrowseItem[];
  total: number;
  offset: number;
  limit: number;
}

export interface CreatorDetailResponse {
  id: string;
  name: string;
  slug: string;
  genres: string[] | null;
  folder_name: string;
  view_count: number;
  created_at: string;
  updated_at: string;
  video_count: number;
}

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

const BASE = "/api/v1";

class ApiError extends Error {
  constructor(
    public status: number,
    public detail: string,
  ) {
    super(`API ${status}: ${detail}`);
    this.name = "ApiError";
  }
}

async function request<T>(url: string, init?: RequestInit): Promise<T> {
  const res = await fetch(url, {
    ...init,
    headers: {
      "Content-Type": "application/json",
      ...init?.headers,
    },
  });

  if (!res.ok) {
    let detail = res.statusText;
    try {
      const body: unknown = await res.json();
      if (typeof body === "object" && body !== null && "detail" in body) {
        const d = (body as { detail: unknown }).detail;
        detail = typeof d === "string" ? d : Array.isArray(d) ? d.map((e: any) => e.msg || JSON.stringify(e)).join("; ") : JSON.stringify(d);
      }
    } catch {
      // body not JSON — keep statusText
    }
    throw new ApiError(res.status, detail);
  }

  return res.json() as Promise<T>;
}

// ── Search ───────────────────────────────────────────────────────────────────

export interface SuggestionItem {
  text: string;
  type: "topic" | "technique" | "creator";
}

export interface SuggestionsResponse {
  suggestions: SuggestionItem[];
}

export async function fetchSuggestions(): Promise<SuggestionsResponse> {
  return request<SuggestionsResponse>(`${BASE}/search/suggestions`);
}

export async function searchApi(
  q: string,
  scope?: string,
  limit?: number,
  sort?: string,
): Promise<SearchResponse> {
  const qs = new URLSearchParams({ q });
  if (scope) qs.set("scope", scope);
  if (limit !== undefined) qs.set("limit", String(limit));
  if (sort) qs.set("sort", sort);
  return request<SearchResponse>(`${BASE}/search?${qs.toString()}`);
}

// ── Techniques ───────────────────────────────────────────────────────────────

export interface TechniqueListParams {
  limit?: number;
  offset?: number;
  category?: string;
  creator_slug?: string;
  sort?: string;
}

export async function fetchTechniques(
  params: TechniqueListParams = {},
): Promise<TechniqueListResponse> {
  const qs = new URLSearchParams();
  if (params.limit !== undefined) qs.set("limit", String(params.limit));
  if (params.offset !== undefined) qs.set("offset", String(params.offset));
  if (params.category) qs.set("category", params.category);
  if (params.creator_slug) qs.set("creator_slug", params.creator_slug);
  if (params.sort) qs.set("sort", params.sort);
  const query = qs.toString();
  return request<TechniqueListResponse>(
    `${BASE}/techniques${query ? `?${query}` : ""}`,
  );
}

export async function fetchTechnique(
  slug: string,
): Promise<TechniquePageDetail> {
  return request<TechniquePageDetail>(`${BASE}/techniques/${slug}`);
}

export async function fetchRandomTechnique(): Promise<{ slug: string }> {
  return request<{ slug: string }>(`${BASE}/techniques/random`);
}

export async function fetchTechniqueVersions(
  slug: string,
): Promise<TechniquePageVersionListResponse> {
  return request<TechniquePageVersionListResponse>(
    `${BASE}/techniques/${slug}/versions`,
  );
}

export async function fetchTechniqueVersion(
  slug: string,
  versionNumber: number,
): Promise<TechniquePageVersionDetail> {
  return request<TechniquePageVersionDetail>(
    `${BASE}/techniques/${slug}/versions/${versionNumber}`,
  );
}

// ── Stats ─────────────────────────────────────────────────────────────────────

export interface StatsResponse {
  technique_count: number;
  creator_count: number;
}

export async function fetchStats(): Promise<StatsResponse> {
  return request<StatsResponse>(`${BASE}/stats`);
}

// ── Popular Searches ─────────────────────────────────────────────────────────

export interface PopularSearchItem {
  query: string;
  count: number;
}

export interface PopularSearchesResponse {
  items: PopularSearchItem[];
  cached: boolean;
}

export async function fetchPopularSearches(): Promise<PopularSearchesResponse> {
  return request<PopularSearchesResponse>(`${BASE}/search/popular`);
}

// ── Topics ───────────────────────────────────────────────────────────────────

export async function fetchTopics(): Promise<TopicCategory[]> {
  return request<TopicCategory[]>(`${BASE}/topics`);
}

export async function fetchSubTopicTechniques(
  categorySlug: string,
  subtopicSlug: string,
  params: { limit?: number; offset?: number; sort?: string } = {},
): Promise<TechniqueListResponse> {
  const qs = new URLSearchParams();
  if (params.limit !== undefined) qs.set("limit", String(params.limit));
  if (params.offset !== undefined) qs.set("offset", String(params.offset));
  if (params.sort) qs.set("sort", params.sort);
  const query = qs.toString();
  return request<TechniqueListResponse>(
    `${BASE}/topics/${encodeURIComponent(categorySlug)}/${encodeURIComponent(subtopicSlug)}${query ? `?${query}` : ""}`,
  );
}

// ── Creators ─────────────────────────────────────────────────────────────────

export interface CreatorListParams {
  sort?: string;
  genre?: string;
  limit?: number;
  offset?: number;
}

export async function fetchCreators(
  params: CreatorListParams = {},
): Promise<CreatorBrowseResponse> {
  const qs = new URLSearchParams();
  if (params.sort) qs.set("sort", params.sort);
  if (params.genre) qs.set("genre", params.genre);
  if (params.limit !== undefined) qs.set("limit", String(params.limit));
  if (params.offset !== undefined) qs.set("offset", String(params.offset));
  const query = qs.toString();
  return request<CreatorBrowseResponse>(
    `${BASE}/creators${query ? `?${query}` : ""}`,
  );
}

export async function fetchCreator(
  slug: string,
): Promise<CreatorDetailResponse> {
  return request<CreatorDetailResponse>(`${BASE}/creators/${slug}`);
}


// ── Content Reports ─────────────────────────────────────────────────────────

export interface ContentReportCreate {
  content_type: string;
  content_id?: string | null;
  content_title?: string | null;
  report_type: string;
  description: string;
  page_url?: string | null;
}

export interface ContentReport {
  id: string;
  content_type: string;
  content_id: string | null;
  content_title: string | null;
  report_type: string;
  description: string;
  status: string;
  admin_notes: string | null;
  page_url: string | null;
  created_at: string;
  resolved_at: string | null;
}

export interface ContentReportListResponse {
  items: ContentReport[];
  total: number;
  offset: number;
  limit: number;
}

export async function submitReport(
  body: ContentReportCreate,
): Promise<ContentReport> {
  return request<ContentReport>(`${BASE}/reports`, {
    method: "POST",
    body: JSON.stringify(body),
  });
}

export async function fetchReports(params: {
  status?: string;
  content_type?: string;
  offset?: number;
  limit?: number;
} = {}): Promise<ContentReportListResponse> {
  const qs = new URLSearchParams();
  if (params.status) qs.set("status", params.status);
  if (params.content_type) qs.set("content_type", params.content_type);
  if (params.offset !== undefined) qs.set("offset", String(params.offset));
  if (params.limit !== undefined) qs.set("limit", String(params.limit));
  const query = qs.toString();
  return request<ContentReportListResponse>(
    `${BASE}/admin/reports${query ? `?${query}` : ""}`,
  );
}

export async function updateReport(
  id: string,
  body: { status?: string; admin_notes?: string },
): Promise<ContentReport> {
  return request<ContentReport>(`${BASE}/admin/reports/${id}`, {
    method: "PATCH",
    body: JSON.stringify(body),
  });
}


// ── Pipeline Admin ──────────────────────────────────────────────────────────

export interface PipelineVideoItem {
  id: string;
  filename: string;
  processing_status: string;
  creator_name: string;
  created_at: string | null;
  updated_at: string | null;
  event_count: number;
  total_tokens_used: number;
  last_event_at: string | null;
  active_stage: string | null;
  active_stage_status: string | null;
  stage_started_at: string | null;
  latest_run: {
    id: string;
    run_number: number;
    trigger: string;
    status: string;
    started_at: string | null;
    finished_at: string | null;
    error_stage: string | null;
    total_tokens: number;
  } | null;
}

export interface PipelineVideoListResponse {
  items: PipelineVideoItem[];
  total: number;
}

export interface PipelineEvent {
  id: string;
  video_id: string;
  stage: string;
  event_type: string;
  prompt_tokens: number | null;
  completion_tokens: number | null;
  total_tokens: number | null;
  model: string | null;
  duration_ms: number | null;
  payload: Record<string, unknown> | null;
  system_prompt_text: string | null;
  user_prompt_text: string | null;
  response_text: string | null;
  created_at: string | null;
}

export interface PipelineEventListResponse {
  items: PipelineEvent[];
  total: number;
  offset: number;
  limit: number;
}

export interface WorkerTask {
  id: string;
  name: string;
  args: unknown[];
  time_start: number | null;
}

export interface WorkerInfo {
  name: string;
  active_tasks: WorkerTask[];
  reserved_tasks: number;
  total_completed: number;
  uptime: string | null;
  pool_size: number | null;
}

export interface WorkerStatusResponse {
  online: boolean;
  workers: WorkerInfo[];
  error?: string;
}

export interface TriggerResponse {
  status: string;
  video_id: string;
  current_processing_status?: string;
}

export interface RevokeResponse {
  status: string;
  video_id: string;
  tasks_revoked: number;
}

export async function fetchPipelineVideos(): Promise<PipelineVideoListResponse> {
  return request<PipelineVideoListResponse>(`${BASE}/admin/pipeline/videos`);
}

export interface RecentActivityItem {
  id: string;
  video_id: string;
  filename: string;
  creator_name: string;
  stage: string;
  event_type: string;
  total_tokens: number | null;
  duration_ms: number | null;
  created_at: string | null;
}

export interface RecentActivityResponse {
  items: RecentActivityItem[];
}

export async function fetchRecentActivity(limit = 10): Promise<RecentActivityResponse> {
  return request<RecentActivityResponse>(`${BASE}/admin/pipeline/recent-activity?limit=${limit}`);
}

export interface PipelineRunItem {
  id: string;
  run_number: number;
  trigger: string;
  status: string;
  started_at: string | null;
  finished_at: string | null;
  error_stage: string | null;
  total_tokens: number;
  event_count: number;
}

export interface PipelineRunsResponse {
  items: PipelineRunItem[];
  legacy_event_count: number;
}

export async function fetchPipelineRuns(videoId: string): Promise<PipelineRunsResponse> {
  return request<PipelineRunsResponse>(`${BASE}/admin/pipeline/runs/${videoId}`);
}

export async function fetchPipelineEvents(
  videoId: string,
  params: { offset?: number; limit?: number; stage?: string; event_type?: string; run_id?: string; order?: "asc" | "desc" } = {},
): Promise<PipelineEventListResponse> {
  const qs = new URLSearchParams();
  if (params.offset !== undefined) qs.set("offset", String(params.offset));
  if (params.limit !== undefined) qs.set("limit", String(params.limit));
  if (params.stage) qs.set("stage", params.stage);
  if (params.event_type) qs.set("event_type", params.event_type);
  if (params.run_id) qs.set("run_id", params.run_id);
  if (params.order) qs.set("order", params.order);
  const query = qs.toString();
  return request<PipelineEventListResponse>(
    `${BASE}/admin/pipeline/events/${videoId}${query ? `?${query}` : ""}`,
  );
}

export async function fetchWorkerStatus(): Promise<WorkerStatusResponse> {
  return request<WorkerStatusResponse>(`${BASE}/admin/pipeline/worker-status`);
}

export async function triggerPipeline(videoId: string): Promise<TriggerResponse> {
  return request<TriggerResponse>(`${BASE}/admin/pipeline/trigger/${videoId}`, {
    method: "POST",
  });
}

export async function revokePipeline(videoId: string): Promise<RevokeResponse> {
  return request<RevokeResponse>(`${BASE}/admin/pipeline/revoke/${videoId}`, {
    method: "POST",
  });
}

export interface CleanRetriggerResponse {
  status: string;
  video_id: string;
  cleaned: Record<string, string>;
}

export async function cleanRetriggerPipeline(videoId: string): Promise<CleanRetriggerResponse> {
  return request<CleanRetriggerResponse>(`${BASE}/admin/pipeline/clean-retrigger/${videoId}`, {
    method: "POST",
  });
}

// ── Chunking Inspector ─────────────────────────────────────────────────────

export interface ChunkingTopicBoundary {
  topic_label: string;
  segment_count: number;
  start_time: number;
  end_time: number;
  start_index: number;
  end_index: number;
}

export interface ChunkingSynthesisGroup {
  category: string;
  moment_count: number;
  exceeds_chunk_threshold: boolean;
  chunks_needed: number;
}

export interface ChunkingDataResponse {
  video_id: string;
  total_segments: number;
  total_moments: number;
  classification_source: string;
  synthesis_chunk_size: number;
  topic_boundaries: ChunkingTopicBoundary[];
  key_moments: Array<{
    id: string;
    title: string;
    content_type: string;
    start_time: number;
    end_time: number;
    plugins: string[];
    technique_page_id: string | null;
  }>;
  classification: Array<Record<string, unknown>>;
  synthesis_groups: ChunkingSynthesisGroup[];
}

export async function fetchChunkingData(videoId: string): Promise<ChunkingDataResponse> {
  return request<ChunkingDataResponse>(`${BASE}/admin/pipeline/chunking/${videoId}`);
}

// ── Single-Stage Re-Run ────────────────────────────────────────────────────

export interface RerunStageResponse {
  status: string;
  video_id: string;
  stage: string;
  prompt_override: boolean;
}

export async function rerunStage(
  videoId: string,
  stageName: string,
  promptOverride?: string,
): Promise<RerunStageResponse> {
  const body: Record<string, string | undefined> = {};
  if (promptOverride) {
    body.prompt_override = promptOverride;
  }
  return request<RerunStageResponse>(
    `${BASE}/admin/pipeline/rerun-stage/${videoId}/${stageName}`,
    {
      method: "POST",
      body: Object.keys(body).length > 0 ? JSON.stringify(body) : undefined,
    },
  );
}

// ── Stale Pages & Bulk Re-Synthesize ───────────────────────────────────────

export interface StalePageCreator {
  creator: string;
  stale_count: number;
  page_slugs: string[];
}

export interface StalePagesResponse {
  current_prompt_hash: string;
  total_pages: number;
  stale_pages: number;
  fresh_pages: number;
  stale_by_creator: StalePageCreator[];
}

export async function fetchStalePages(): Promise<StalePagesResponse> {
  return request<StalePagesResponse>(`${BASE}/admin/pipeline/stale-pages`);
}

export interface BulkResynthResponse {
  status: string;
  stage: string;
  total: number;
  dispatched: number;
  skipped: Array<{ video_id: string; reason: string }> | null;
}

export async function bulkResynthesize(
  videoIds?: string[],
  stage = "stage5_synthesis",
): Promise<BulkResynthResponse> {
  return request<BulkResynthResponse>(`${BASE}/admin/pipeline/bulk-resynthesize`, {
    method: "POST",
    body: JSON.stringify({ video_ids: videoIds ?? null, stage }),
  });
}

// ── Wipe All Output ────────────────────────────────────────────────────────

export interface WipeAllResponse {
  status: string;
  deleted: Record<string, string | number>;
}

export async function wipeAllOutput(): Promise<WipeAllResponse> {
  return request<WipeAllResponse>(`${BASE}/admin/pipeline/wipe-all-output`, {
    method: "POST",
  });
}

// ── Debug Mode ──────────────────────────────────────────────────────────────

export interface DebugModeResponse {
  debug_mode: boolean;
}

export async function fetchDebugMode(): Promise<DebugModeResponse> {
  return request<DebugModeResponse>(`${BASE}/admin/pipeline/debug-mode`);
}

export async function setDebugMode(enabled: boolean): Promise<DebugModeResponse> {
  return request<DebugModeResponse>(`${BASE}/admin/pipeline/debug-mode`, {
    method: "PUT",
    body: JSON.stringify({ debug_mode: enabled }),
  });
}

// ── Admin: Technique Pages ─────────────────────────────────────────────────

export interface AdminTechniquePageItem {
  id: string;
  title: string;
  slug: string;
  creator_name: string;
  creator_slug: string;
  topic_category: string;
  body_sections_format: string;
  source_video_count: number;
  version_count: number;
  created_at: string;
  updated_at: string;
}

export interface AdminTechniquePageListResponse {
  items: AdminTechniquePageItem[];
  total: number;
  offset: number;
  limit: number;
}

export async function fetchAdminTechniquePages(
  params: {
    multi_source_only?: boolean;
    creator?: string;
    sort?: string;
    offset?: number;
    limit?: number;
  } = {},
): Promise<AdminTechniquePageListResponse> {
  const qs = new URLSearchParams();
  if (params.multi_source_only) qs.set("multi_source_only", "true");
  if (params.creator) qs.set("creator", params.creator);
  if (params.sort) qs.set("sort", params.sort);
  if (params.offset !== undefined) qs.set("offset", String(params.offset));
  if (params.limit !== undefined) qs.set("limit", String(params.limit));
  const query = qs.toString();
  return request<AdminTechniquePageListResponse>(
    `${BASE}/admin/pipeline/technique-pages${query ? `?${query}` : ""}`,
  );
}
