// api.ts から分割（挙動不変）: ローカルタスク（CRUD・コメント・ツール要求・プロンプトコーチ・フィードバック）。
import { BASE } from './client';

export type PieceName = string;  // Dynamically loaded from API
export type ProfileName = 'auto' | 'fast' | 'quality';
export type OutputFormat = 'text' | 'markdown' | 'json';
export type AskPolicy = 'low' | 'high';
export type Priority = 'low' | 'medium' | 'high';
export type Visibility = 'private' | 'org' | 'public';

export interface SubtaskInfo {
  id: string;
  issueNumber: number;
  status: string;
  instruction: string;
  worktreePath: string | null;
  createdAt: string;
  updatedAt: string;
  children?: SubtaskInfo[];
  childCount?: number;
  childCompleted?: number;
}

export interface SubtaskActivity {
  jobId: string;
  issueNumber: number;
  status: string;
  currentMovement: string | null;
  currentActivity: string | null;
  activityLog: string;
}

export type TitleSource = 'auto' | 'agent' | 'user';

export interface LocalTask {
  id: number;
  title: string;
  /** Provenance of the title: 'auto' (creation fallback), 'agent' (derived from goal), 'user' (manual edit). */
  titleSource?: TitleSource;
  body: string;
  pieceName: string;
  profile: string;
  outputFormat: string;
  askPolicy: string;
  priority: string;
  state: string;
  workspacePath: string | null;
  ownerId?: string | null;
  ownerName?: string | null;
  visibility?: Visibility;
  visibilityScopeOrgId?: string | null;
  visibilityScopeOrgName?: string | null;
  /** Spaces foundation: which space this task belongs to (null = legacy/個人). */
  spaceId?: string | null;
  createdAt: string;
  updatedAt: string;
  latestJob?: {
    id: string;
    status: string;
    waitReason?: string | null;
    currentMovement?: string | null;
    currentActivity?: string | null;
    workerId?: string | null;
    /**
     * Physical backend id (e.g. LiteLLM deployment) for jobs run through
     * a proxy worker. NULL until the proxy has resolved a backend or for
     * direct workers entirely.
     * Phase A: docs/superpowers/specs/2026-05-18-multi-team-gpu-pool-and-node-status-design.md.
     */
    lastBackendId?: string | null;
    contextPromptTokens?: number | null;
    contextLimitTokens?: number | null;
    contextUpdatedAt?: string | null;
  } | null;
  subtasks?: SubtaskInfo[];
  subtaskCount?: number;
  subtaskCompleted?: number;
  feedbackRating?: 'good' | 'bad' | null;
  feedbackTags?: string[] | null;
  feedbackComment?: string | null;
  feedbackAt?: string | null;
  shareToken?: string | null;
  sharedAt?: string | null;
  missionBrief?: MissionBrief | null;
}

export interface MissionBrief {
  goal: string;
  done: string;
  open: string;
  clarifications: string;
  user_constraints?: string;
  decisions?: string;
  current_focus?: string;
}

export async function updateMissionBrief(
  taskId: number,
  patch: Partial<MissionBrief>,
): Promise<MissionBrief | null> {
  const res = await fetch(`${BASE}/local/tasks/${taskId}/mission`, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(patch),
  });
  if (!res.ok) {
    const err = await res.json().catch(() => ({ error: res.statusText }));
    throw new Error(err.error || res.statusText);
  }
  const data = await res.json();
  return data.missionBrief ?? null;
}

// ─── Tool-request mechanism ────────────────────────────────────────────────
export interface ToolRequest {
  id: string;
  taskId: string | null;
  jobId: string | null;
  spaceId: string | null;
  pieceName: string;
  movementName: string;
  toolName: string;
  reason: string | null;
  category: 'requested' | 'blocked' | 'unknown';
  status: 'pending' | 'approved' | 'denied' | 'auto_denied';
  grantScope: 'task' | 'piece' | null;
  decidedBy: string | null;
  createdAt: string;
  decidedAt: string | null;
}

export async function fetchToolRequests(taskId: number): Promise<ToolRequest[]> {
  const res = await fetch(`${BASE}/local/tasks/${taskId}/tool-requests`);
  if (!res.ok) throw new Error('failed to fetch tool requests');
  const data = await res.json();
  return (data.toolRequests ?? []) as ToolRequest[];
}

export async function decideToolRequest(
  taskId: number,
  reqId: string,
  decision: 'approve' | 'deny',
): Promise<void> {
  const res = await fetch(`${BASE}/local/tasks/${taskId}/tool-requests/${reqId}/decide`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ decision }),
  });
  if (!res.ok) {
    const err = await res.json().catch(() => ({ error: res.statusText }));
    throw new Error(err.error || res.statusText);
  }
}

// Package-request mechanism (PR3): agent-declared Python package requests.
export interface PackageRequest {
  id: string;
  taskId: string | null;
  jobId: string | null;
  spaceId: string | null;
  pieceName: string | null;
  movementName: string | null;
  spec: string;
  normalizedName: string;
  reason: string | null;
  status: 'pending' | 'approved' | 'denied' | 'auto_denied';
  decidedBy: string | null;
  createdAt: string;
  decidedAt: string | null;
}

export async function fetchPackageRequests(taskId: number): Promise<PackageRequest[]> {
  const res = await fetch(`${BASE}/local/tasks/${taskId}/package-requests`);
  if (!res.ok) throw new Error('failed to fetch package requests');
  const data = await res.json();
  return (data.packageRequests ?? []) as PackageRequest[];
}

export async function decidePackageRequest(
  taskId: number,
  reqId: string,
  decision: 'approve' | 'deny',
): Promise<void> {
  const res = await fetch(`${BASE}/local/tasks/${taskId}/package-requests/${reqId}/decide`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ decision }),
  });
  if (!res.ok) {
    const err = await res.json().catch(() => ({ error: res.statusText }));
    throw new Error(err.error || res.statusText);
  }
}

export type CommentKind = 'request' | 'comment' | 'result' | 'ask' | 'progress' | 'handoff' | 'interjection';

export interface LocalTaskComment {
  id: number;
  taskId: number;
  author: string;
  kind: CommentKind;
  body: string;
  /** Filenames attached to this comment, saved under the task's input/ dir. */
  attachments?: string[];
  createdAt: string;
  injectedAt: string | null;
}

export interface CreateLocalTaskInput {
  title?: string;
  body: string;
  piece: PieceName;
  profile: ProfileName;
  outputFormat: OutputFormat;
  askPolicy: AskPolicy;
  priority: Priority;
  attachments?: Array<{ name: string; contentBase64: string }>;
  visibility?: Visibility;
  visibilityScopeOrgId?: string | null;
  browserSessionProfileId?: number | null;
  /**
   * Spaces foundation: 'persistent'（既定）はスペースのワークスペースに蓄積、
   * 'ephemeral' は使い捨て。未指定時はバックエンドが 'persistent' に解決する。
   */
  workspaceMode?: 'persistent' | 'ephemeral';
  /** 紐付けるスペース。未指定なら owner の個人スペースに解決される。 */
  spaceId?: string;
  options?: {
    mcpDisabled?: boolean;
    skillsDisabled?: boolean;
  };
}

export async function fetchLocalTasks(): Promise<LocalTask[]> {
  const res = await fetch(`${BASE}/local/tasks`);
  const data = await res.json();
  if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch local tasks');
  return data.tasks ?? [];
}

export async function createLocalTask(input: CreateLocalTaskInput): Promise<{ task: LocalTask; jobId: string }> {
  const res = await fetch(`${BASE}/local/tasks`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(input),
  });
  const data = await res.json();
  if (!res.ok) throw new Error(data?.error ?? 'Failed to create local task');
  return data;
}

export interface PromptCoachAxis {
  name: string;
  score: number;
  comment: string;
}
export interface PromptCoachResult {
  overall: number;
  axes: PromptCoachAxis[];
  rewrite: string;
  predicted_piece: { name: string; reason: string } | null;
  maestro_tips: Array<{ feature: string; suggestion: string }>;
  personalized: string[];
}

/**
 * On-demand prompt coach: evaluates a draft task prompt before it is submitted.
 * Stateless — nothing is persisted. Returns 503 when the coach is unconfigured.
 */
export async function evaluatePrompt(input: { instruction: string; piece?: string }): Promise<PromptCoachResult> {
  const res = await fetch(`${BASE}/local/tasks/evaluate-prompt`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(input),
  });
  const data = await res.json();
  if (!res.ok) throw new Error(data?.error ?? 'Failed to evaluate prompt');
  return data as PromptCoachResult;
}

export async function fetchLocalTask(taskId: number): Promise<LocalTask> {
  const res = await fetch(`${BASE}/local/tasks/${taskId}`);
  const data = await res.json();
  if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch local task');
  return data.task;
}

export async function fetchLocalTaskComments(taskId: number): Promise<LocalTaskComment[]> {
  const res = await fetch(`${BASE}/local/tasks/${taskId}/comments`);
  const data = await res.json();
  if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch local task comments');
  return data.comments ?? [];
}

export async function postLocalTaskComment(taskId: number, body: string, author: string = 'user', attachments?: Array<{ name: string; contentBase64: string }>): Promise<void> {
  const payload: Record<string, unknown> = { body, author };
  if (attachments && attachments.length > 0) payload.attachments = attachments;
  const res = await fetch(`${BASE}/local/tasks/${taskId}/comments`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
  });
  const data = await res.json();
  if (!res.ok) throw new Error(data?.error ?? 'Failed to post local task comment');
}

export async function updateLocalTask(
  taskId: number,
  updates: { title?: string; visibility?: Visibility; visibilityScopeOrgId?: string | null },
): Promise<LocalTask> {
  const res = await fetch(`${BASE}/local/tasks/${taskId}`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(updates),
  });
  const data = await res.json();
  if (!res.ok) throw new Error(data?.error ?? 'Failed to update local task');
  return data.task;
}

/** Trigger on-demand AI title regeneration. Owner/admin only. Returns the new title. */
export async function regenerateTaskTitle(taskId: number): Promise<string> {
  const res = await fetch(`${BASE}/local/tasks/${taskId}/regenerate-title`, { method: 'POST' });
  const data = await res.json();
  if (!res.ok) throw new Error(data?.error ?? 'Failed to regenerate title');
  return data.title as string;
}

export async function continueTaskWithPiece(
  taskId: number,
  body: { piece: string; instruction: string },
): Promise<{ jobId: string }> {
  const res = await fetch(`${BASE}/local/tasks/${taskId}/continue`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  });
  const data = await res.json();
  if (!res.ok) throw new Error(data?.error ?? 'Failed to continue task');
  return data;
}

export async function deleteLocalTask(taskId: number): Promise<void> {
  const res = await fetch(`${BASE}/local/tasks/${taskId}`, { method: 'DELETE' });
  const data = await res.json();
  if (!res.ok) throw new Error(data?.error ?? 'Failed to delete local task');
}

export async function cancelLocalTask(taskId: number): Promise<void> {
  const res = await fetch(`${BASE}/local/tasks/${taskId}/cancel`, {
    method: 'POST',
  });
  const data = await res.json();
  if (!res.ok) throw new Error(data?.error ?? 'Failed to cancel task');
}

export async function putFeedback(
  taskId: number,
  feedback: { rating: 'good' | 'bad'; tags: string[]; comment?: string },
): Promise<LocalTask> {
  const res = await fetch(`${BASE}/local/tasks/${taskId}/feedback`, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(feedback),
  });
  const data = await res.json();
  if (!res.ok) throw new Error(data?.error ?? 'Failed to update feedback');
  return data.task;
}
