// Builders for Weavy recipe execute payload, based on the actual flow structure
// from Kling 3.0 Motion Control using Replicate
//
// Node IDs are from this specific recipe — each account has its own copy.
// For bot-created accounts, node IDs are overridden via the recipeNodes field.

// ── Default node IDs ──
const DEFAULT_NODE_IDS = {
  model:   "ad2d29e0-0041-456a-963d-701f141beb62",
  image:   "03a3e452-8160-4ead-8628-4a345beed350",
  video:   "f8f9836f-daec-4e76-a0fc-98ba8d7cea70",
  group:   "3f7fadc2-a7a6-49a2-96dd-15e8b1e024c1",
};

// ── Handle IDs ──
const HANDLE_IDS = {
  prompt: "73a4d104-e5b5-40ba-b84c-e52f287c5857",
  image: "69b84a7c-b2fe-441c-9491-412f7653bfcf",
  video: "6ad8008c-3aaf-4222-94b0-3431254c5c46",
};

const MODEL_INFO = {
  name: "kwaivgi/\\rkling-v3-motion-control",
  service: "replicate",
  version: "15430b300f8c044e8f9e3567fd6daadf6d62e9bb0cee23fdb7969d3b26542f40",
  coverImage: "https://replicate.delivery/xezq/i748h9oPvLp6Ktupd1oFRcNbzcGl5j9gZCfDyHsv4MrN07GLA/tmp7gzjazeo.mp4",
  originalImportSource: "https://replicate.com/kwaivgi/\\rkling-v3-motion-control",
};

const MODEL_DESCRIPTION =
  "Kling 3.0 motion control: transfer motion from a reference video to any character image with improved consistency and quality.";

const DESCRIPTIONS = {
  prompt: "Text prompt for video generation. You can add elements to the screen and achieve motion effects through prompt words.",
  image: "Reference image. The characters, backgrounds, and other elements in the generated video are based on the reference image. Supports .jpg/.jpeg/.png, max 10MB, dimensions 340px-3850px, aspect ratio 1:2.5 to 2.5:1.",
  video: "Reference video. The character actions in the generated video are consistent with the reference video. Supports .mp4/.mov, max 100MB, 3-30 seconds duration depending on character_orientation.",
  keep_original_sound: "Whether to keep the original sound of the reference video",
  character_orientation: "Orientation of the character in the generated video. 'image': same orientation as the person in the picture (max 10s video). 'video': consistent with the orientation of the characters in the video (max 30s video). When binding elements, only 'video' orientation is supported.",
  mode: "Video generation mode. 'std': Standard mode (720p, cost-effective). 'pro': Professional mode (1080p, higher quality).",
};

function buildImageFile({ url, meta = {} }) {
  return {
    type: "image",
    url,
    width: meta.width ?? 1024,
    height: meta.height ?? 1024,
    thumbnailUrl: meta.thumbnailUrl ?? url,
    publicId: meta.publicId ?? "",
    id: meta.id ?? "",
    name: meta.name ?? "image.png",
  };
}

function buildVideoFile({ url, meta = {} }) {
  return {
    type: "video",
    url,
    width: meta.width ?? 576,
    height: meta.height ?? 1024,
    duration: meta.duration ?? 0,
    fps: meta.fps ?? 30,
    hasAudio: meta.hasAudio ?? true,
    thumbnailUrl: meta.thumbnailUrl ?? url,
    publicId: meta.publicId ?? "",
    id: meta.id ?? "",
    name: meta.name ?? "video.mp4",
  };
}

function buildHandlesBlock() {
  return {
    input: {
      image: {
        id: HANDLE_IDS.image,
        type: "image",
        label: "image",
        order: 1,
        format: "uri",
        required: true,
        description: DESCRIPTIONS.image,
      },
      video: {
        id: HANDLE_IDS.video,
        type: "video",
        label: "video",
        order: 2,
        format: "uri",
        required: true,
        description: DESCRIPTIONS.video,
      },
      prompt: {
        id: HANDLE_IDS.prompt,
        type: "text",
        label: "prompt",
        order: 0,
        format: "text",
        required: false,
        description: DESCRIPTIONS.prompt,
      },
    },
    output: [
      "result",
    ],
  };
}

function buildSchemaBlock() {
  return {
    mode: {
      type: "enum",
      order: 4,
      title: "Mode",
      default: "pro",
      options: ["std", "pro"],
      required: false,
      description: DESCRIPTIONS.mode,
    },
    image: {
      type: "string",
      order: 1,
      title: "Image",
      format: "uri",
      required: true,
      description: DESCRIPTIONS.image,
    },
    video: {
      type: "string",
      order: 2,
      title: "Video",
      format: "uri",
      required: true,
      description: DESCRIPTIONS.video,
    },
    prompt: {
      type: "string",
      order: 0,
      title: "Prompt",
      default: "",
      required: false,
      description: DESCRIPTIONS.prompt,
    },
    keep_original_sound: {
      type: "boolean",
      order: 5,
      title: "Keep Original Sound",
      default: true,
      required: false,
      description: DESCRIPTIONS.keep_original_sound,
    },
    character_orientation: {
      type: "enum",
      order: 3,
      title: "Character Orientation",
      default: "image",
      options: ["image", "video"],
      required: false,
      description: DESCRIPTIONS.character_orientation,
    },
  };
}

function buildKindBlock({ nodeIds, modeVal, keepOriginalSound, characterOrientation, imageFile, videoFile }) {
  return {
    type: "wildcard",
    model: { type: "imported", ...MODEL_INFO, description: MODEL_DESCRIPTION },
    inputs: [
      [
        { id: "prompt", title: "Prompt", description: DESCRIPTIONS.prompt, validTypes: ["text"], required: false },
        null,
      ],
      [
        { id: "image", title: "Image", description: DESCRIPTIONS.image, validTypes: ["image"], required: true },
        { nodeId: nodeIds.image, outputId: "file", file: imageFile },
      ],
      [
        { id: "video", title: "Video", description: DESCRIPTIONS.video, validTypes: ["video"], required: true },
        { nodeId: nodeIds.video, outputId: "file", file: videoFile },
      ],
    ],
    parameters: [
      [
        { id: "character_orientation", title: "Character Orientation", description: DESCRIPTIONS.character_orientation, constraint: { type: "enum", options: ["image", "video"] }, defaultValue: { type: "string", value: "image" } },
        { type: "value", data: { type: "string", value: characterOrientation } },
      ],
      [
        { id: "mode", title: "Mode", description: DESCRIPTIONS.mode, constraint: { type: "enum", options: ["std", "pro"] }, defaultValue: { type: "string", value: "pro" } },
        { type: "value", data: { type: "string", value: modeVal } },
      ],
      [
        { id: "keep_original_sound", title: "Keep Original Sound", description: DESCRIPTIONS.keep_original_sound, constraint: { type: "boolean" }, defaultValue: { type: "boolean", value: true } },
        { type: "value", data: { type: "boolean", value: Boolean(keepOriginalSound) } },
      ],
    ],
    outputs: [{ id: "result", title: "Result" }],
  };
}

function buildModelNode({ nodeIds, imageFile, videoFile, prompt, modelType, keepOriginalSound, characterOrientation }) {
  const modeVal = String(modelType).toLowerCase() === "pro" ? "pro" : "std";
  return {
    id: nodeIds.model,
    dragHandle: ".node-header",
    type: "custommodelV2",
    isModel: true,
    data: {
      handles: buildHandlesBlock(),
      name: MODEL_INFO.name,
      color: "Yambo_Purple",
      label: null,
      menu: null,
      model: MODEL_INFO,
      params: {
        mode: modeVal,
        image: imageFile,
        video: videoFile,
        keep_original_sound: Boolean(keepOriginalSound),
        character_orientation: characterOrientation,
        ...(prompt ? { prompt } : {}),
      },
      schema: buildSchemaBlock(),
      version: 2,
      dark_color: "Yambo_Purple_Dark",
      isWildCard: true,
      description: MODEL_DESCRIPTION,
      kind: buildKindBlock({ nodeIds, modeVal, keepOriginalSound, characterOrientation, imageFile, videoFile }),
      generations: [],
      border_color: "Yambo_Purple_Stroke",
      cameraLocked: false,
      selectedIndex: 0,
      selectedOutput: 0,
      result: [],
      output: {},
      input: {
        image: imageFile,
        video: videoFile,
      },
    },
    createdAt: "2026-03-15T11:31:36.278Z",
    locked: false,
    parentId: nodeIds.group,
    position: { x: 720, y: 630 },
    selected: true,
    width: 460,
    height: 130,
  };
}

function buildImageImportNode({ nodeIds, imageFile }) {
  return {
    id: nodeIds.image,
    dragHandle: ".node-header",
    type: "import",
    data: {
      handles: {
        output: {
          file: { type: "any", label: "File", order: 0, format: "uri", description: "The uploaded file" },
        },
      },
      name: "File",
      color: "Yambo_Blue",
      label: null,
      menu: null,
      params: null,
      schema: null,
      version: 3,
      dark_color: "Yambo_Blue_Dark",
      border_color: "Yambo_Blue_Stroke",
      files: [imageFile],
      cameraLocked: false,
      selectedIndex: 0,
      output: { file: imageFile },
    },
    createdAt: "2026-03-15T15:00:54.691Z",
    locked: false,
    parentId: nodeIds.group,
    position: { x: 30, y: 30 },
    selected: true,
    width: 460,
    height: 556,
  };
}

function buildVideoImportNode({ nodeIds, videoFile }) {
  return {
    id: nodeIds.video,
    dragHandle: ".node-header",
    type: "import",
    data: {
      handles: {
        output: {
          file: { type: "any", label: "File", order: 0, format: "uri", description: "The uploaded file" },
        },
      },
      name: "File",
      color: "Yambo_Blue",
      label: null,
      menu: null,
      params: null,
      schema: null,
      version: 3,
      dark_color: "Yambo_Blue_Dark",
      border_color: "Yambo_Blue_Stroke",
      files: [videoFile],
      cameraLocked: false,
      selectedIndex: 0,
      output: { file: videoFile },
    },
    createdAt: "2026-03-15T15:01:18.370Z",
    locked: false,
    parentId: nodeIds.group,
    position: { x: 30, y: 920 },
    selected: true,
    width: 460,
    height: 556,
  };
}

function buildGroupNode({ nodeIds }) {
  return {
    id: nodeIds.group,
    type: "custom_group",
    data: {
      version: 3,
      color: "rgba(227, 232, 236, 0.64)",
      description: "Group of nodes",
      type: "custom_group",
      name: "MC-3.0",
      handles: { input: [], output: [] },
      width: 1210,
      height: 1506,
      labelFontSize: 16,
    },
    position: { x: 1980, y: 400 },
    width: 1210,
    height: 1506,
    locked: false,
    selected: true,
    createdAt: "2026-03-15T11:31:36.278Z",
  };
}

function buildEdges({ nodeIds }) {
  return [
    {
      id: "f1516332-935d-4f23-afb4-d1a83c106c45",
      source: nodeIds.image,
      target: nodeIds.model,
      sourceHandle: `${nodeIds.image}-output-file`,
      targetHandle: `${nodeIds.model}-input-image`,
      type: "custom",
      data: { sourceColor: "Yambo_Blue", targetColor: "Yambo_Purple", sourceHandleType: "any", targetHandleType: "image" },
      selected: true,
    },
    {
      id: "675ca4cc-30c7-4220-b37a-f1c4ae9135ea",
      source: nodeIds.video,
      target: nodeIds.model,
      sourceHandle: `${nodeIds.video}-output-file`,
      targetHandle: `${nodeIds.model}-input-video`,
      type: "custom",
      data: { sourceColor: "Yambo_Blue", targetColor: "Yambo_Purple", sourceHandleType: "any", targetHandleType: "video" },
      selected: true,
    },
  ];
}

/**
 * Build the full execute payload for Weavy recipe API.
 */
export function buildExecutePayload({
  imageUrl, imageMeta = {},
  videoUrl, videoMeta = {},
  prompt = "",
  modelType = "Standard",
  version = "V2.6",
  keepOriginalSound = true,
  characterOrientation = "video",
  numberOfRuns = 1,
  recipeNodes = null,
}) {
  const nodeIds = recipeNodes || DEFAULT_NODE_IDS;

  const imageFile = buildImageFile({ url: imageUrl, meta: imageMeta });
  const videoFile = buildVideoFile({ url: videoUrl, meta: videoMeta });

  return {
    numberOfRuns,
    nodes: [
      buildModelNode({ nodeIds, imageFile, videoFile, prompt, modelType, keepOriginalSound, characterOrientation }),
    ],
    edges: buildEdges({ nodeIds }),
  };
}

export function buildSavePayload(opts) {
  return buildExecutePayload(opts);
}
