import { classifyTaskError, type TaskErrorCategory } from "./translateTaskError";

export type GenerationLifecycleStatus =
  | "creating"
  | "queued"
  | "running"
  | "stopping"
  | "failed"
  | "completed"
  | "local_timeout";

export type TaskRefundStatus = "not_charged" | "pending_refund" | "refunded" | "manual_review" | "unknown";

export interface TaskTimeoutPolicy {
  submitTimeoutMs: number;
  noProgressTimeoutMs: number;
  maxRuntimeMs: number;
}

export interface TaskFailureInfo {
  category: TaskErrorCategory;
  message: string;
  actionLabel: string;
  retryable: boolean;
  refundStatus: TaskRefundStatus;
  refundHint: string;
}

export interface TextTokenUsage {
  promptTokens?: number;
  completionTokens?: number;
  totalTokens?: number;
}

export interface TextTokenCreditRate {
  inputCreditsPerMillion: number;
  outputCreditsPerMillion: number;
  source?: "server" | "fallback";
  modelKey?: string;
}

const CREDITS_PER_CNY = 100;

export const TEXT_INPUT_CREDITS_PER_MILLION = 2 * CREDITS_PER_CNY;
export const TEXT_OUTPUT_CREDITS_PER_MILLION = 5 * CREDITS_PER_CNY;

export const FALLBACK_TEXT_TOKEN_CREDIT_RATE: TextTokenCreditRate = {
  inputCreditsPerMillion: TEXT_INPUT_CREDITS_PER_MILLION,
  outputCreditsPerMillion: TEXT_OUTPUT_CREDITS_PER_MILLION,
  source: "fallback",
};

const IMAGE_TIMEOUT_POLICY: TaskTimeoutPolicy = {
  submitTimeoutMs: 90_000,
  noProgressTimeoutMs: 120_000,
  maxRuntimeMs: 10 * 60_000,
};

const VIDEO_TIMEOUT_POLICY: TaskTimeoutPolicy = {
  submitTimeoutMs: 120_000,
  noProgressTimeoutMs: 120_000,
  maxRuntimeMs: 20 * 60_000,
};

const VIDEO_LONG_TIMEOUT_POLICY: TaskTimeoutPolicy = {
  submitTimeoutMs: 120_000,
  noProgressTimeoutMs: 180_000,
  maxRuntimeMs: 30 * 60_000,
};

const VIDEO_SUPER_RESOLUTION_TIMEOUT_POLICY: TaskTimeoutPolicy = {
  submitTimeoutMs: 120_000,
  noProgressTimeoutMs: 180_000,
  maxRuntimeMs: 15 * 60_000,
};

const TEXT_TIMEOUT_POLICY: TaskTimeoutPolicy = {
  submitTimeoutMs: 30_000,
  noProgressTimeoutMs: 60_000,
  maxRuntimeMs: 5 * 60_000,
};

export function getTaskTimeoutPolicy(input: {
  kind?: "image" | "video" | "text";
  model?: string | null;
  operation?: string | null;
}): TaskTimeoutPolicy {
  if (input.operation === "video-super-resolution") return VIDEO_SUPER_RESOLUTION_TIMEOUT_POLICY;
  if (input.kind === "image") return IMAGE_TIMEOUT_POLICY;
  if (input.kind === "text") return TEXT_TIMEOUT_POLICY;
  const model = String(input.model || "").toLowerCase();
  if (/kling|wan|veo|sora|hailuo|vidu|pixverse|happyhorse/.test(model)) return VIDEO_LONG_TIMEOUT_POLICY;
  return VIDEO_TIMEOUT_POLICY;
}

export function isTaskLocallyTimedOut(input: {
  startedAt: number;
  lastProgressAt: number;
  now?: number;
  policy: TaskTimeoutPolicy;
  progress?: number;
}): "no_progress" | "max_runtime" | null {
  const now = input.now || Date.now();
  const progress = Number(input.progress || 0);
  if (now - input.startedAt >= input.policy.maxRuntimeMs) return "max_runtime";
  if (progress > 0 && progress < 100 && now - input.lastProgressAt >= input.policy.noProgressTimeoutMs) {
    return "no_progress";
  }
  if (progress <= 0 && now - input.startedAt >= input.policy.submitTimeoutMs) return "no_progress";
  return null;
}

export function buildLocalTimeoutMessage(kind: "image" | "video" | "text" = "video"): string {
  if (kind === "text") {
    return "本地等待已超时，已停止前端动画。若服务端稍后返回，请以会话记录和积分流水为准。";
  }
  const label = kind === "image" ? "图片" : "视频";
  return `${label}任务长时间没有进展，已停止本地等待并释放前端占用。服务端任务仍可能稍后完成，请到任务历史或资产页查看结果；如已扣费，系统会在失败结算后按积分流水退回。`;
}

export function buildTaskFailureInfo(
  error: string | undefined | null,
  options: { refundStatus?: TaskRefundStatus; charged?: boolean; submitted?: boolean } = {},
): TaskFailureInfo {
  const classified = classifyTaskError(error);
  const submitted = options.submitted !== false;
  const refundStatus: TaskRefundStatus =
    options.refundStatus ||
    (submitted
      ? classified.category === "insufficient_balance" || classified.category === "auth_failure"
        ? "not_charged"
        : "unknown"
      : "not_charged");

  const refundHint = getRefundHint(refundStatus);
  return {
    category: classified.category,
    message: `${classified.message}${refundHint ? `\n\n${refundHint}` : ""}`,
    actionLabel: classified.action,
    retryable: !["auth_failure", "insufficient_balance", "content_policy"].includes(classified.category),
    refundStatus,
    refundHint,
  };
}

export function getRefundHint(status: TaskRefundStatus): string {
  switch (status) {
    case "not_charged":
      return "提交未进入扣费结算，未产生积分消耗。";
    case "pending_refund":
      return "任务已失败，若已扣费，系统会自动退回，请以积分流水为准。";
    case "refunded":
      return "失败扣费已退回，请在积分流水中核对。";
    case "manual_review":
      return "退款状态需要人工核对，请联系管理员并提供任务 ID。";
    default:
      return "如已扣费，系统将在任务失败后自动退回；请以积分流水为准。";
  }
}

function sanitizeCreditRate(value: number): number {
  return Number.isFinite(value) && value >= 0 ? value : 0;
}

function formatCreditRate(value: number): string {
  const safeValue = sanitizeCreditRate(value);
  if (safeValue >= 100) return Math.round(safeValue).toLocaleString("zh-CN");
  return Number(safeValue.toFixed(4)).toString();
}

export function formatTextTokenCreditRule(
  rate: TextTokenCreditRate = FALLBACK_TEXT_TOKEN_CREDIT_RATE,
): string {
  return `输入 Token 每百万 ${formatCreditRate(rate.inputCreditsPerMillion)} 积分，输出 Token 每百万 ${formatCreditRate(rate.outputCreditsPerMillion)} 积分，实际以服务端结算为准。`;
}

export function estimateTextTokenCredits(
  usage: TextTokenUsage,
  rate: TextTokenCreditRate = FALLBACK_TEXT_TOKEN_CREDIT_RATE,
): number {
  const promptTokens = Math.max(0, Number(usage.promptTokens || 0));
  const completionTokens = Math.max(0, Number(usage.completionTokens || 0));
  return (
    (promptTokens / 1_000_000) * sanitizeCreditRate(rate.inputCreditsPerMillion) +
    (completionTokens / 1_000_000) * sanitizeCreditRate(rate.outputCreditsPerMillion)
  );
}

export function formatTextTokenUsage(
  usage?: TextTokenUsage | null,
  rate: TextTokenCreditRate = FALLBACK_TEXT_TOKEN_CREDIT_RATE,
): string {
  const rule = `文本计费规则：${formatTextTokenCreditRule(rate)}`;
  if (!usage) return rule;
  const promptTokens = Math.max(0, Number(usage.promptTokens || 0));
  const completionTokens = Math.max(0, Number(usage.completionTokens || 0));
  const estimatedCredits = estimateTextTokenCredits({ promptTokens, completionTokens }, rate);
  return `本次 Token：输入 ${promptTokens}，输出 ${completionTokens}，预估 ${estimatedCredits.toFixed(4)} 积分。\n${rule}`;
}
