import type { WebGenerationPreviewTask } from "../types";
import { isRecord, serverRequest } from "./serverConnection";

type ServerTaskStatus = "pending" | "running" | "completed" | "failed" | "cancelled";

interface ServerProjectTask {
  id: string;
  projectId?: string | null;
  clientQueueId?: string | null;
  type: "image" | "video";
  status: ServerTaskStatus;
  params?: Record<string, unknown>;
  resultUrl?: string | null;
  progress?: number;
  error?: string | null;
  createdAt?: string;
  updatedAt?: string;
}

export interface ProjectTaskUpsertInput {
  clientQueueId: string;
  type: "image" | "video";
  status: ServerTaskStatus;
  params?: Record<string, unknown>;
  providerTaskId?: string | null;
  resultUrl?: string | null;
  progress?: number;
  error?: string | null;
  dedupeKey?: string | null;
  sourceDeviceId?: string | null;
  createdAt?: string | null;
  completedAt?: string | null;
}

function toStringValue(value: unknown, fallback = ""): string {
  if (typeof value === "string") return value.trim() || fallback;
  if (typeof value === "number" && Number.isFinite(value)) return String(value);
  return fallback;
}

function toNumber(value: unknown, fallback = 0): number {
  const numeric = Number(value);
  return Number.isFinite(numeric) ? numeric : fallback;
}

function normalizeStatus(value: unknown): WebGenerationPreviewTask["status"] {
  const status = toStringValue(value);
  if (status === "running" || status === "completed" || status === "failed") return status;
  if (status === "cancelled") return "failed";
  return "queued";
}

function normalizeTask(raw: unknown): ServerProjectTask | null {
  if (!isRecord(raw)) return null;
  const type = toStringValue(raw.type);
  if (type !== "image" && type !== "video") return null;

  return {
    id: toStringValue(raw.id),
    projectId: toStringValue(raw.projectId ?? raw.project_id) || null,
    clientQueueId: toStringValue(raw.clientQueueId ?? raw.client_queue_id) || null,
    type,
    status: toStringValue(raw.status, "pending") as ServerTaskStatus,
    params: isRecord(raw.params) ? raw.params : {},
    resultUrl: toStringValue(raw.resultUrl ?? raw.result_url) || null,
    progress: toNumber(raw.progress),
    error: toStringValue(raw.error) || null,
    createdAt: toStringValue(raw.createdAt ?? raw.created_at),
    updatedAt: toStringValue(raw.updatedAt ?? raw.updated_at),
  };
}

function extractTasks(payload: unknown): ServerProjectTask[] {
  const normalizeTasks = (rows: unknown[]): ServerProjectTask[] => {
    const tasks: ServerProjectTask[] = [];
    for (const row of rows) {
      const task = normalizeTask(row);
      if (task) tasks.push(task);
    }
    return tasks;
  };

  if (Array.isArray(payload)) return normalizeTasks(payload);
  if (!isRecord(payload)) return [];
  const rows = payload.tasks ?? payload.items;
  return Array.isArray(rows) ? normalizeTasks(rows) : [];
}

function taskTitle(task: ServerProjectTask): string {
  const prompt = toStringValue(task.params?.prompt);
  if (prompt) return prompt.length > 20 ? `${prompt.slice(0, 20)}...` : prompt;
  return task.type === "video" ? "视频生成任务" : "图像生成任务";
}

function toPreviewTask(task: ServerProjectTask): WebGenerationPreviewTask {
  return {
    id: task.clientQueueId || task.id,
    title: taskTitle(task),
    type: task.type,
    status: normalizeStatus(task.status),
    progress: Math.max(0, Math.min(100, Math.trunc(task.progress || 0))),
    prompt: toStringValue(task.params?.prompt, taskTitle(task)),
    createdAt: task.createdAt || task.updatedAt || "",
    projectId: task.projectId || undefined,
    outputUrl: task.resultUrl || undefined,
    source: "server",
    errorMessage: task.error || undefined,
  };
}

async function listProjectTasks(projectId: string): Promise<WebGenerationPreviewTask[]> {
  const payload = await serverRequest<unknown>(`projects/${encodeURIComponent(projectId)}/tasks`);
  return extractTasks(payload).map(toPreviewTask);
}

export const projectTaskClient = {
  async list(projectId: string): Promise<WebGenerationPreviewTask[]> {
    return listProjectTasks(projectId);
  },

  async listForProjects(projectIds: string[]): Promise<WebGenerationPreviewTask[]> {
    const uniqueIds = new Set<string>();
    for (const projectId of projectIds) {
      const id = projectId.trim();
      if (id) uniqueIds.add(id);
    }
    const results = await Promise.all(Array.from(uniqueIds, (id) => listProjectTasks(id)));
    return results.flat();
  },

  async upsert(projectId: string, input: ProjectTaskUpsertInput): Promise<WebGenerationPreviewTask> {
    const payload = await serverRequest<{ task: unknown }>(
      `projects/${encodeURIComponent(projectId)}/tasks/upsert`,
      {
        method: "POST",
        body: input,
      },
    );
    const task = normalizeTask(payload.task ?? payload);
    if (!task) throw new Error("Project task response did not include a task");
    return toPreviewTask(task);
  },

  async batchUpsert(projectId: string, tasks: ProjectTaskUpsertInput[]): Promise<WebGenerationPreviewTask[]> {
    const payload = await serverRequest<{ tasks: unknown }>(
      `projects/${encodeURIComponent(projectId)}/tasks/batch-upsert`,
      {
        method: "POST",
        body: { tasks },
      },
    );
    return extractTasks(payload).map(toPreviewTask);
  },
};
