import { createTaskEvent } from "../../repositories/task-event-repo.js";
import { updateTask } from "../../repositories/task-repo.js";
import { buildClaimUrl, createTaskClaimToken } from "../claim/claim-service.js";
import {
  buyCloudtentaclesSku,
  getCloudtentaclesAsset,
  listCloudtentaclesSku,
} from "../platforms/cloudtentacles/catalog-service.js";
import { getCloudtentaclesKnapsack } from "../platforms/cloudtentacles/knapsack-service.js";
import {
  backCloudtentaclesVirtualNumber,
  getCloudtentaclesBindInfo,
  probeCloudtentaclesBindUrl,
} from "../platforms/cloudtentacles/virtual-number-service.js";
import { resolveCloudtentaclesConfig } from "../platforms/cloudtentacles/shared.js";
import {
  notifyKuaishouCloudAssetNotEnough,
  notifyKuaishouCloudBindUrlRefreshFailed,
} from "../notification/domain-notifications.js";
import { createHttpError } from "../../utils/http.js";
import { nowIso } from "../../utils/time.js";
import {
  KUAISHOU_CLOUD_FIXED_VN_KEY,
  isKuaishouCloudBindUrlFresh,
  isKuaishouCloudTask,
  maskPhone,
  normalizeKuaishouCloudFlow,
  normalizeKuaishouCloudRoleInfo,
  resolveKuaishouCloudBindUrlExpiresAt,
  type JsonObject,
} from "./kuaishou-cloud/domain.js";
import {
  prepareKuaishouCloudBindResourceWithFallback,
  resolveKuaishouCloudBindingResources,
  resolveKuaishouCloudVnKeyCandidates,
} from "./kuaishou-cloud/binding-resources.js";
import {
  resolvePersistedCloudtentaclesContextWithFallback,
} from "./kuaishou-cloud/cloudtentacles-context.js";
import {
  getTaskClaimExpiresAt,
  isClaimExpired,
  normalizeActor,
  parseTaskContext,
} from "./kuaishou-cloud/task-context.js";

export {
  isKuaishouCloudBindUrlFresh,
  isKuaishouCloudTask,
  maskCode,
  maskPhone,
  normalizeKuaishouCloudFlow,
  normalizeKuaishouCloudRoleInfo,
  resolveKuaishouCloudBindUrlExpiresAt,
} from "./kuaishou-cloud/domain.js";
export {
  resolvePersistedCloudtentaclesContext,
  resolvePersistedCloudtentaclesContextWithFallback,
} from "./kuaishou-cloud/cloudtentacles-context.js";

export async function ensureTaskClaimLink(task) {
  const tokenStatus = String(task?.primary_claim_token_status || "").trim();
  const token = String(
    task?.primary_claim_token || task?.claim_token || ""
  ).trim();
  const expiredAt = getTaskClaimExpiresAt(task);

  if (tokenStatus === "active" && token && !isClaimExpired(expiredAt)) {
    return {
      token,
      expiredAt,
      claimUrl: buildClaimUrl(token),
    };
  }

  const claimToken = await createTaskClaimToken(task.id);
  return {
    token: claimToken.token,
    expiredAt: claimToken.expired_at,
    claimUrl: claimToken.claimUrl,
  };
}

export async function prepareKuaishouCloudFulfillmentTask(
  task,
  options: JsonObject = {}
) {
  if (!isKuaishouCloudTask(task)) {
    throw createHttpError("当前任务不是快手 Cloud 履约任务", {
      statusCode: 409,
      errorCode: "kuaishou_cloud_task_invalid",
    });
  }

  const now = nowIso();
  const actor = normalizeActor(options.actor);
  const force = options.force === true;
  const taskContext = parseTaskContext(task);
  const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment);
  const claimLinkState = await ensureTaskClaimLink(task);

  if (
    !force &&
    flow.binding.prepareStatus === "ready" &&
    flow.binding.vnId > 0 &&
    flow.binding.vnPhone &&
    flow.binding.bindUrl
  ) {
    if (!isKuaishouCloudBindUrlFresh(flow)) {
      return refreshKuaishouCloudTaskBindUrl(task, {
        source: options.source || "system_refresh_expired_bind_url",
        actor,
        claimLinkState,
      });
    }

    let readyTask = task;

    if (String(task.task_status || "").trim() !== "waiting_binding") {
      readyTask = await updateTask(task.id, {
        task_status: "waiting_binding",
        claim_token: claimLinkState.token || task.claim_token || "",
        claim_expires_at:
          claimLinkState.expiredAt || getTaskClaimExpiresAt(task),
        updated_at: now,
      });
    }

    return {
      task: readyTask,
      claimUrl: claimLinkState.claimUrl,
      token: claimLinkState.token,
      flow,
    };
  }

  const cloudContext = resolvePersistedCloudtentaclesContextWithFallback(
    flow.binding.cloudSourceKey || "default",
    flow.binding.cloudSourceKeyFallbacks || []
  );
  const [knapsack, skuList] = await Promise.all([
    getCloudtentaclesKnapsack(cloudContext),
    listCloudtentaclesSku(cloudContext),
  ]);
  const resolvedBinding = resolveKuaishouCloudBindingResources(flow, {
    skuItems: Array.isArray(skuList.items) ? skuList.items : [],
    knapsackItems: Array.isArray(knapsack.items) ? knapsack.items : [],
  });
  const vnKeyCandidates = resolveKuaishouCloudVnKeyCandidates({
    flow,
    binding: resolvedBinding,
  });

  if (!resolvedBinding.skuId || vnKeyCandidates.length === 0) {
    throw createHttpError("当前任务缺少可用 cloud 资源，无法自动准备绑定", {
      statusCode: 409,
      errorCode: "kuaishou_cloud_missing_binding_config",
    });
  }

  const flowWithResolvedBinding = {
    ...flow,
    binding: {
      ...flow.binding,
      skuId: resolvedBinding.skuId,
      skuName: resolvedBinding.skuName,
      vnKey: KUAISHOU_CLOUD_FIXED_VN_KEY,
    },
  };

  const knapsackItem = resolvedBinding.knapsackItem;
  let usedKnapsack = Number(knapsackItem?.count || 0) > 0;
  let purchaseTriggered = false;
  let assetBefore = 0;
  let assetAfter = 0;

  if (!usedKnapsack) {
    if (!flowWithResolvedBinding.purchase.autoBuyEnabled) {
      throw createHttpError("背包中没有现成库存，且当前配置未开启自动购买", {
        statusCode: 409,
        errorCode: "kuaishou_cloud_auto_buy_disabled",
      });
    }

    const asset = await getCloudtentaclesAsset(cloudContext);
    const targetSku = resolvedBinding.skuItem;

    if (!targetSku) {
      throw createHttpError(
        `cloudtentacles 未找到 SKU ${flowWithResolvedBinding.binding.skuId}`,
        {
          statusCode: 404,
          errorCode: "kuaishou_cloud_sku_not_found",
        }
      );
    }

    assetBefore = Number(asset.asset || 0) || 0;
    const targetPrice = Number(targetSku.price || 0) || 0;
    const requiredAsset =
      targetPrice + flowWithResolvedBinding.purchase.minAssetReserve;
    if (assetBefore < requiredAsset) {
      await notifyKuaishouCloudAssetNotEnough({
        task,
        flow: flowWithResolvedBinding,
        assetBefore,
        requiredAsset,
        skuName: targetSku.name,
      });
      throw createHttpError(
        `余额不足，当前 ${assetBefore}，至少需要 ${requiredAsset}`,
        {
          statusCode: 409,
          errorCode: "kuaishou_cloud_asset_not_enough",
        }
      );
    }

    await buyCloudtentaclesSku({
      ...cloudContext,
      id: flowWithResolvedBinding.binding.skuId,
      count: 1,
    });
    purchaseTriggered = true;

    const assetResult = await getCloudtentaclesAsset(cloudContext);
    assetAfter = Number(assetResult.asset || 0) || 0;
  }

  const preparedBinding = await prepareKuaishouCloudBindResourceWithFallback({
    cloudContext,
    vnKeyCandidates,
  });

  const nextContext = {
    ...taskContext,
    kuaishouCloudFulfillment: {
      ...flowWithResolvedBinding,
      binding: {
        ...flowWithResolvedBinding.binding,
        resolvedSourceKey: cloudContext.resolvedSourceKey,
        vnKey: preparedBinding.vnKey,
        prepareStatus: "ready",
        vnId: preparedBinding.vnId,
        vnPhone: preparedBinding.vnPhone,
        bindUrl: preparedBinding.bindUrl,
        bindPreparedAt: now,
        bindExpiresAt: resolveKuaishouCloudBindUrlExpiresAt(now),
        bindProbeAt: null,
        bindProbeStatus: "pending",
        bindProbeMessage: "",
        roleName: "",
        roleId: "",
      },
      role: {
        status: "pending",
        name: "",
        rid: "",
        refreshedAt: null,
        errorMessage: "",
        rawInfo: null,
      },
      purchase: {
        ...flowWithResolvedBinding.purchase,
        usedKnapsack,
        purchaseTriggered,
        assetBefore,
        assetAfter,
        purchaseAt: purchaseTriggered
          ? now
          : flowWithResolvedBinding.purchase.purchaseAt,
      },
    },
  };

  const updatedTask = await updateTask(task.id, {
    task_status: "waiting_binding",
    inventory_status: "not_required",
    user_action_status: "pending_claim",
    claim_token: claimLinkState.token || task.claim_token || "",
    claim_expires_at: claimLinkState.expiredAt || getTaskClaimExpiresAt(task),
    role_id: "",
    role_name: "",
    last_error: "",
    context_json: JSON.stringify(nextContext),
    updated_at: now,
  });

  await createTaskEvent(
    task.id,
    "kuaishou_cloud_binding_prepared",
    {
      source: String(options.source || "system").trim() || "system",
      skuId: flowWithResolvedBinding.binding.skuId,
      skuName: flowWithResolvedBinding.binding.skuName,
      vnKey: preparedBinding.vnKey,
      vnId: preparedBinding.vnId,
      vnPhoneMasked: maskPhone(preparedBinding.vnPhone),
      purchaseTriggered,
      usedKnapsack,
      resolvedByName: resolvedBinding.resolvedByName,
      actor,
    },
    now
  );

  return {
    task: updatedTask,
    claimUrl: claimLinkState.claimUrl,
    token: claimLinkState.token,
    flow: normalizeKuaishouCloudFlow(nextContext.kuaishouCloudFulfillment),
  };
}

export async function refreshKuaishouCloudTaskBindUrl(
  task,
  options: JsonObject = {}
) {
  if (!isKuaishouCloudTask(task)) {
    throw createHttpError("当前任务不是快手 Cloud 履约任务", {
      statusCode: 409,
      errorCode: "kuaishou_cloud_task_invalid",
    });
  }

  const now = nowIso();
  const actor = normalizeActor(options.actor);
  const taskContext = parseTaskContext(task);
  const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment);

  if (!flow.binding.vnId || !flow.binding.vnKey) {
    throw createHttpError("当前任务缺少可刷新绑定链接的虚拟号信息", {
      statusCode: 409,
      errorCode: "kuaishou_cloud_missing_bind_url_context",
    });
  }

  const effectiveSourceKey =
    flow.binding.resolvedSourceKey || flow.binding.cloudSourceKey || "default";
  const cloudContext = resolvePersistedCloudtentaclesContextWithFallback(
    effectiveSourceKey,
    flow.binding.cloudSourceKeyFallbacks || []
  );
  const oldVnKey = flow.binding.vnKey;
  const oldVnId = flow.binding.vnId;
  const oldVnPhone = flow.binding.vnPhone;

  await backCloudtentaclesVirtualNumber({
    ...cloudContext,
    key: oldVnKey,
    id: oldVnId,
  });

  await createTaskEvent(
    task.id,
    "kuaishou_cloud_expired_bind_number_returned",
    {
      source: String(options.source || "system").trim() || "system",
      vnKey: oldVnKey,
      vnId: oldVnId,
      vnPhoneMasked: maskPhone(oldVnPhone),
      actor,
    },
    now
  );

  let preparedBinding;
  try {
    preparedBinding = await prepareKuaishouCloudBindResourceWithFallback({
      cloudContext,
      vnKeyCandidates: resolveKuaishouCloudVnKeyCandidates({
        flow,
        binding: flow.binding,
      }),
    });

    if (!String(preparedBinding.bindUrl || "").trim()) {
      throw createHttpError("cloudtentacles 未返回新的绑定链接", {
        statusCode: 502,
        errorCode: "kuaishou_cloud_empty_bind_url",
      });
    }
  } catch (error) {
    const failedTask = await markKuaishouCloudBindUrlRefreshFailed(task, {
      taskContext,
      flow,
      now,
      actor,
      error,
    });

    return {
      task: failedTask,
      claimUrl: buildClaimUrl(
        String(task.primary_claim_token || task.claim_token || "")
      ),
      token: String(task.primary_claim_token || task.claim_token || ""),
      flow: normalizeKuaishouCloudFlow(
        parseTaskContext(failedTask).kuaishouCloudFulfillment
      ),
    };
  }

  const nextContext = {
    ...taskContext,
    kuaishouCloudFulfillment: {
      ...flow,
      binding: {
        ...flow.binding,
        prepareStatus: "ready",
        vnKey: preparedBinding.vnKey,
        vnId: preparedBinding.vnId,
        vnPhone: preparedBinding.vnPhone,
        bindUrl: preparedBinding.bindUrl,
        bindPreparedAt: now,
        bindExpiresAt: resolveKuaishouCloudBindUrlExpiresAt(now),
        bindProbeAt: null,
        bindProbeStatus: "pending",
        bindProbeMessage: "",
        roleName:
          String(task.task_status || "").trim() === "role_confirmed"
            ? flow.binding.roleName
            : "",
        roleId:
          String(task.task_status || "").trim() === "role_confirmed"
            ? flow.binding.roleId
            : "",
      },
      role:
        String(task.task_status || "").trim() === "role_confirmed"
          ? flow.role
          : {
              status: "pending",
              name: "",
              rid: "",
              refreshedAt: null,
              errorMessage: "",
              rawInfo: null,
            },
    },
  };

  const claimLinkState =
    options.claimLinkState || (await ensureTaskClaimLink(task));
  const updatedTask = await updateTask(task.id, {
    task_status:
      String(task.task_status || "").trim() === "role_confirmed"
        ? task.task_status
        : "waiting_binding",
    claim_token: claimLinkState.token || task.claim_token || "",
    claim_expires_at: claimLinkState.expiredAt || getTaskClaimExpiresAt(task),
    role_id:
      String(task.task_status || "").trim() === "role_confirmed"
        ? task.role_id
        : "",
    role_name:
      String(task.task_status || "").trim() === "role_confirmed"
        ? task.role_name
        : "",
    last_error: "",
    context_json: JSON.stringify(nextContext),
    updated_at: now,
  });

  await createTaskEvent(
    task.id,
    "kuaishou_cloud_bind_url_refreshed",
    {
      source: String(options.source || "system").trim() || "system",
      oldVnId,
      oldVnPhoneMasked: maskPhone(oldVnPhone),
      vnKey: preparedBinding.vnKey,
      vnId: preparedBinding.vnId,
      vnPhoneMasked: maskPhone(preparedBinding.vnPhone),
      actor,
    },
    now
  );

  return {
    task: updatedTask,
    claimUrl: claimLinkState.claimUrl,
    token: claimLinkState.token,
    flow: normalizeKuaishouCloudFlow(nextContext.kuaishouCloudFulfillment),
  };
}

export async function probeKuaishouCloudTaskBindUrl(
  task,
  options: JsonObject = {}
) {
  if (!isKuaishouCloudTask(task)) {
    throw createHttpError("当前任务不是快手 Cloud 履约任务", {
      statusCode: 409,
      errorCode: "kuaishou_cloud_task_invalid",
    });
  }

  const now = nowIso();
  const taskContext = parseTaskContext(task);
  const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment);

  if (!flow.binding.bindUrl) {
    return {
      task,
      flow,
      probe: null,
    };
  }

  const probeIntervalMs =
    Number(resolveCloudtentaclesConfig().bindUrlProbeIntervalSeconds || 30) *
    1000;
  if (
    !options.force &&
    flow.binding.bindProbeAt &&
    Date.now() - Date.parse(flow.binding.bindProbeAt) < probeIntervalMs
  ) {
    return {
      task,
      flow,
      probe: null,
    };
  }

  const probe = await probeCloudtentaclesBindUrl({
    bindUrl: flow.binding.bindUrl,
  });

  if (probe.expired || !isKuaishouCloudBindUrlFresh(flow)) {
    const refreshed = await refreshKuaishouCloudTaskBindUrl(task, {
      source: options.source || "claim_page_bind_url_expired",
      actor: options.actor || { source: "system" },
    });
    return {
      ...refreshed,
      probe,
    };
  }

  const probeRoleInfo = normalizeKuaishouCloudRoleInfo(probe.roleInfo);
  const hasRoleInfo = Boolean(probeRoleInfo.name || probeRoleInfo.rid);
  const nextContext = {
    ...taskContext,
    kuaishouCloudFulfillment: {
      ...flow,
      binding: {
        ...flow.binding,
        bindProbeAt: now,
        bindProbeStatus: probe.valid ? "valid" : "invalid",
        bindProbeMessage: String(probe.message || probe.reason || "").trim(),
        roleName: hasRoleInfo ? probeRoleInfo.name : flow.binding.roleName,
        roleId: hasRoleInfo ? probeRoleInfo.rid : flow.binding.roleId,
      },
      role: {
        ...flow.role,
        status: hasRoleInfo ? "ready" : flow.role.status,
        name: hasRoleInfo ? probeRoleInfo.name : flow.role.name,
        rid: hasRoleInfo ? probeRoleInfo.rid : flow.role.rid,
        refreshedAt: hasRoleInfo ? now : flow.role.refreshedAt,
        errorMessage: hasRoleInfo ? "" : flow.role.errorMessage,
        rawInfo: hasRoleInfo ? probeRoleInfo.rawInfo : flow.role.rawInfo,
      },
    },
  };

  const updatedTask = await updateTask(task.id, {
    role_id: hasRoleInfo ? probeRoleInfo.rid : task.role_id,
    role_name: hasRoleInfo ? probeRoleInfo.name : task.role_name,
    context_json: JSON.stringify(nextContext),
    updated_at: now,
  });

  return {
    task: updatedTask,
    flow: normalizeKuaishouCloudFlow(nextContext.kuaishouCloudFulfillment),
    probe,
  };
}

/**
 * @param {any} task
 * @param {{ taskContext?: any, flow?: any, now?: string, actor?: any, error?: unknown }} [input]
 */
async function markKuaishouCloudBindUrlRefreshFailed(
  task,
  { taskContext, flow, now, actor, error }: JsonObject = {}
) {
  const errorMessage =
    error instanceof Error
      ? error.message
      : String(error || "新绑定链接准备失败");
  const nextContext = {
    ...taskContext,
    kuaishouCloudFulfillment: {
      ...flow,
      binding: {
        ...flow.binding,
        prepareStatus: "pending",
        vnId: 0,
        vnPhone: "",
        bindUrl: "",
        bindPreparedAt: null,
        bindExpiresAt: null,
        bindProbeAt: now,
        bindProbeStatus: "refresh_failed",
        bindProbeMessage: errorMessage,
        roleName: "",
        roleId: "",
      },
      role: {
        status: "pending",
        name: "",
        rid: "",
        refreshedAt: now,
        errorMessage:
          "绑定链接已过期，旧号码已退还，新链接准备失败，请稍后刷新或联系客服处理",
        rawInfo: null,
      },
    },
  };

  const updatedTask = await updateTask(task.id, {
    task_status: "pending_binding_prepare",
    role_id: "",
    role_name: "",
    last_error: `绑定链接过期，旧号码已退还，新链接准备失败：${errorMessage}`,
    context_json: JSON.stringify(nextContext),
    updated_at: now,
  });

  await createTaskEvent(
    task.id,
    "kuaishou_cloud_bind_url_refresh_failed",
    {
      source: "system_refresh_expired_bind_url",
      errorMessage,
      actor,
    },
    now
  );

  await notifyKuaishouCloudBindUrlRefreshFailed({
    task: updatedTask,
    errorMessage,
  });

  return updatedTask;
}

export async function refreshKuaishouCloudTaskRoleInfo(
  task,
  options: JsonObject = {}
) {
  if (!isKuaishouCloudTask(task)) {
    throw createHttpError("当前任务不是快手 Cloud 履约任务", {
      statusCode: 409,
      errorCode: "kuaishou_cloud_task_invalid",
    });
  }

  const now = nowIso();
  const actor = normalizeActor(options.actor);
  const taskContext = parseTaskContext(task);
  const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment);

  if (!flow.binding.vnId || !flow.binding.vnKey) {
    throw createHttpError(
      "当前任务还没有可查询的绑定角色信息，请先准备绑定资源",
      {
        statusCode: 409,
        errorCode: "kuaishou_cloud_missing_bind_info_context",
      }
    );
  }

  if (flow.binding.bindUrl) {
    const probed = await probeKuaishouCloudTaskBindUrl(task, {
      source: options.source || "system_role_refresh_bind_url_probe",
      actor,
      force: options.forceProbe === true,
    });
    const probedFlow = normalizeKuaishouCloudFlow(
      parseTaskContext(probed.task).kuaishouCloudFulfillment
    );
    if (probed.probe?.expired) {
      return {
        task: probed.task,
        roleInfo: normalizeKuaishouCloudRoleInfo(null),
        flow: probedFlow,
      };
    }

    if (
      (probed.probe === null || probed.probe?.valid) &&
      (probedFlow.binding.roleName || probedFlow.binding.roleId)
    ) {
      return {
        task: probed.task,
        roleInfo: {
          name: probedFlow.binding.roleName,
          rid: probedFlow.binding.roleId,
          rawInfo: probedFlow.role.rawInfo,
        },
        flow: probedFlow,
      };
    }
  }

  const effectiveSourceKey =
    flow.binding.resolvedSourceKey || flow.binding.cloudSourceKey || "default";
  const cloudContext = resolvePersistedCloudtentaclesContextWithFallback(
    effectiveSourceKey,
    flow.binding.cloudSourceKeyFallbacks || []
  );
  const bindInfoResult = await getCloudtentaclesBindInfo({
    ...cloudContext,
    key: flow.binding.vnKey,
    id: flow.binding.vnId,
  });

  const bindInfo = normalizeKuaishouCloudRoleInfo(bindInfoResult.bindInfo);
  const hasRoleInfo = Boolean(bindInfo.name || bindInfo.rid);
  const nextContext = {
    ...taskContext,
    kuaishouCloudFulfillment: {
      ...flow,
      binding: {
        ...flow.binding,
        roleName: hasRoleInfo ? bindInfo.name : "",
        roleId: hasRoleInfo ? bindInfo.rid : "",
      },
      role: {
        status: hasRoleInfo ? "ready" : "pending",
        name: hasRoleInfo ? bindInfo.name : "",
        rid: hasRoleInfo ? bindInfo.rid : "",
        refreshedAt: now,
        errorMessage: hasRoleInfo
          ? ""
          : "当前还没有查询到角色信息，请完成绑定后稍等片刻再试",
        rawInfo: bindInfo.rawInfo,
      },
    },
  };

  const updatedTask = await updateTask(task.id, {
    role_id: hasRoleInfo ? bindInfo.rid : "",
    role_name: hasRoleInfo ? bindInfo.name : "",
    context_json: JSON.stringify(nextContext),
    updated_at: now,
  });

  if (options.recordEvent !== false) {
    await createTaskEvent(
      task.id,
      "kuaishou_cloud_role_info_refreshed",
      {
        source: String(options.source || "system").trim() || "system",
        roleName: bindInfo.name,
        roleId: bindInfo.rid,
        vnId: flow.binding.vnId,
        actor,
      },
      now
    );
  }

  return {
    task: updatedTask,
    roleInfo: bindInfo,
    flow: normalizeKuaishouCloudFlow(nextContext.kuaishouCloudFulfillment),
  };
}

export {
  dispatchKuaishouCloudFulfillmentTask,
  returnKuaishouCloudFulfillmentTask,
} from "./kuaishou-cloud/task-finalization.js";
