// Builders for Weavy recipe execute payload, based on the actual flow structure
// from https://app.weavy.ai/flow/V99kxpHSTJRHhTjRsui8hv (Kling Motion Control)
//
// 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 (recipe V99kxpHSTJRHhTjRsui8hv) ──
const DEFAULT_NODE_IDS = {
  model:   "34d68b8d-340d-4e68-8f29-c038f59f59e1",
  image:   "d7600752-393c-47fb-876a-bd0c4b6300cc",
  video:   "72531336-3be9-46ec-b7ba-08d6df824885",
  preview: "03874385-8757-4058-8efb-b5e33e69357a",
  group:   "6d49b40c-e964-494c-9485-bcea2223a21e",
};

// ── Handle IDs (shared across all recipe copies) ──
const HANDLE_IDS = {
  prompt: "e7dfb623-49f1-4721-b563-40814c4454e0",
  image_url: "23849317-61d9-463a-a019-0fec97a6cfb2",
  video_url: "71b66c49-368a-4451-bb6c-929aef10f6c7",
  result: "bb0d416a-f903-4aa3-b5b5-5d9b01736721",
};

const MODEL_INFO = {
  name: "fal-ai/kling-video/v2.6/pro/motion-control",
  service: "fal_imported",
  version: "fal-ai/kling-video/v2.6/pro/motion-control",
};

const MODEL_DESCRIPTION =
  "Transfer movements from a reference video to any character image. Pro mode delivers higher quality output, ideal for complex dance moves and gestures.";

const DESCRIPTIONS = {
  prompt: "Text prompt for the video generation. Can include positive and negative descriptions to add elements and motion effects.",
  image_url: "Reference image. The characters, backgrounds, and other elements in the generated video are based on this reference image. Characters should have clear body proportions, avoid occlusion, and occupy more than 5% of the image area.",
  video_url: "Reference video. The character actions in the generated video will be consistent with this reference video. Should contain a realistic style character with entire body or upper body visible, including head, without obstruction. Duration limit depends on character_orientation: 10s max for 'image', 30s max for 'video'.",
  keep_original_sound: "Whether to keep the original sound from the reference video.",
  character_orientation: "Controls whether the output character's orientation matches the reference image or video. 'video': orientation matches reference video - better for complex motions (max 30s). 'image': orientation matches reference image - better for following camera movements (max 10s).",
  model: "Kling Motion Control type",
  version: "Kling Motion Control version",
};

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: {
      prompt: { id: HANDLE_IDS.prompt, type: "text", label: "prompt", format: "text", required: false, description: DESCRIPTIONS.prompt },
      image_url: { id: HANDLE_IDS.image_url, type: "image", label: "image", format: "text", required: true, description: DESCRIPTIONS.image_url },
      video_url: { id: HANDLE_IDS.video_url, type: "video", label: "video", format: "text", required: true, description: DESCRIPTIONS.video_url },
    },
    output: {
      result: { id: HANDLE_IDS.result, type: "video", label: "result", order: 0, format: "uri", description: "Result video" },
    },
  };
}

function buildSchemaBlock() {
  return {
    model: { type: "enum", order: 0, title: "Model", default: "Pro", options: ["Pro", "Standard"], description: DESCRIPTIONS.model },
    prompt: { type: "string", title: "Prompt", required: false, description: DESCRIPTIONS.prompt },
    version: { type: "enum", order: 0, title: "Version", default: "V3.0", options: ["V2.6", "V3.0"], description: DESCRIPTIONS.version },
    keep_original_sound: { type: "boolean", title: "Keep Original Sound", default: true, required: false, description: DESCRIPTIONS.keep_original_sound },
    character_orientation: { type: "enum", title: "Character Orientation", options: ["image", "video"], required: true, description: DESCRIPTIONS.character_orientation },
  };
}

function buildKindBlock({ nodeIds, modelType, version, keepOriginalSound, characterOrientation, imageFile, videoFile }) {
  return {
    type: "wildcard",
    model: { type: "predefined", ...MODEL_INFO, description: MODEL_DESCRIPTION },
    inputs: [
      [
        { id: "prompt", title: "Prompt", description: DESCRIPTIONS.prompt, validTypes: ["text"], required: false },
        null,
      ],
      [
        { id: "image_url", title: "image", description: DESCRIPTIONS.image_url, validTypes: ["image"], required: true },
        { nodeId: nodeIds.image, outputId: "file", file: imageFile },
      ],
      [
        { id: "video_url", title: "video", description: DESCRIPTIONS.video_url, validTypes: ["video"], required: true },
        { nodeId: nodeIds.video, outputId: "file", file: videoFile },
      ],
    ],
    parameters: [
      [
        { id: "model", title: "Model", description: DESCRIPTIONS.model, constraint: { type: "enum", options: ["Pro", "Standard"] }, defaultValue: { type: "string", value: "Pro" } },
        { type: "value", data: { type: "string", value: modelType } },
      ],
      [
        { id: "version", title: "Version", description: DESCRIPTIONS.version, constraint: { type: "enum", options: ["V2.6", "V3.0"] }, defaultValue: { type: "string", value: "V3.0" } },
        { type: "value", data: { type: "string", value: version } },
      ],
      [
        { 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) } },
      ],
      [
        { 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 } },
      ],
    ],
    outputs: [{ id: "result", title: "result", description: "Result video", dataType: "video" }],
  };
}

function buildModelNode({ nodeIds, imageFile, videoFile, prompt, modelType, version, keepOriginalSound, characterOrientation }) {
  return {
    id: nodeIds.model,
    dragHandle: ".node-header",
    owner: null,
    type: "custommodelV2",
    visibility: "private",
    isModel: true,
    data: {
      handles: buildHandlesBlock(),
      name: "Kling Motion Control",
      description: MODEL_DESCRIPTION,
      color: "Red",
      label: null,
      menu: { icon: "EmojiObjectsIcon", isModel: true, displayName: "Kling Motion Control" },
      model: MODEL_INFO,
      params: {
        model: modelType,
        version,
        keep_original_sound: Boolean(keepOriginalSound),
        character_orientation: characterOrientation,
        image_url: imageFile,
        video_url: videoFile,
        ...(prompt ? { prompt } : {}),
      },
      schema: buildSchemaBlock(),
      version: 3,
      kind: buildKindBlock({ nodeIds, modelType, version, keepOriginalSound, characterOrientation, imageFile, videoFile }),
      generations: [],
      selectedIndex: 0,
      cameraLocked: false,
      result: [],
      output: {},
      selectedOutput: 0,
      input: {
        image_url: imageFile,
        video_url: videoFile,
      },
    },
    createdAt: "2026-06-03T01:02:48.342Z",
    updatedAt: "2026-03-06T12:21:09.410Z",
    locked: false,
    parentId: nodeIds.group,
    position: { x: 720, y: 510 },
    selected: true,
    width: 460,
    height: 560,
  };
}

function buildImageImportNode({ nodeIds, imageFile }) {
  return {
    id: nodeIds.image,
    dragHandle: ".node-header",
    owner: null,
    type: "import",
    visibility: null,
    isModel: false,
    data: {
      handles: {
        output: {
          file: { type: "any", label: "File", order: 0, format: "uri", description: "The uploaded file" },
        },
      },
      name: "File",
      description: null,
      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-06-03T01:02:48.342Z",
    updatedAt: "2025-06-03T09:49:14.704Z",
    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",
    owner: null,
    type: "import",
    visibility: null,
    isModel: false,
    data: {
      handles: {
        output: {
          file: { type: "any", label: "File", order: 0, format: "uri", description: "The uploaded file" },
        },
      },
      name: "File",
      description: null,
      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-06-03T01:02:48.342Z",
    updatedAt: "2025-06-03T09:49:14.704Z",
    locked: false,
    parentId: nodeIds.group,
    position: { x: 30, y: 810 },
    selected: true,
    width: 460,
    height: 556,
  };
}

function buildPreviewNode({ nodeIds }) {
  return {
    id: nodeIds.preview,
    dragHandle: ".node-header",
    owner: null,
    type: "preview",
    visibility: null,
    isModel: false,
    data: {
      handles: { input: ["file"], output: [] },
      name: "Preview",
      description: null,
      color: "Yambo_Black",
      label: "Preview",
      menu: { icon: "PreviewIcon", order: 4, isModel: false, displayName: "Preview" },
      params: null,
      schema: null,
      version: null,
      dark_color: "Yambo_Black_Dark",
      border_color: "Yambo_Black_Stroke",
      inputNode: { nodeId: nodeIds.model, outputId: "result" },
    },
    createdAt: "2026-06-03T01:02:48.342Z",
    updatedAt: "2025-06-03T09:49:14.704Z",
    locked: false,
    parentId: nodeIds.group,
    position: { x: 1300, y: 510 },
    selected: true,
    width: 460,
    height: 506,
  };
}

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: "Group",
      handles: { input: [], output: [] },
      width: 1790,
      height: 1799,
      labelFontSize: 16,
    },
    position: { x: 1680, y: 830 },
    width: 1790,
    height: 1799,
    locked: false,
    selected: true,
    createdAt: "2026-06-03T01:02:48.342Z",
  };
}

function buildEdges({ nodeIds }) {
  return [
    {
      id: "d0d9ae6d-4430-4b12-a477-d77ce89e8a1a",
      source: nodeIds.video,
      target: nodeIds.model,
      sourceHandle: `${nodeIds.video}-output-file`,
      targetHandle: `${nodeIds.model}-input-video_url`,
      type: "custom",
      data: { sourceColor: "Yambo_Blue", targetColor: "Red", sourceHandleType: "any", targetHandleType: "video" },
      selected: true,
    },
    {
      id: "38a8b10a-3c64-4e06-95fb-2725de4dc82e",
      source: nodeIds.image,
      target: nodeIds.model,
      sourceHandle: `${nodeIds.image}-output-file`,
      targetHandle: `${nodeIds.model}-input-image_url`,
      type: "custom",
      data: { sourceColor: "Yambo_Blue", targetColor: "Red", sourceHandleType: "any", targetHandleType: "image" },
      selected: true,
    },
  ];
}

/**
 * Build the full execute payload for Weavy recipe API.
 * @param {object} opts
 * @param {string} opts.imageUrl - Uploaded image URL (from Weavy media)
 * @param {object} opts.imageMeta - Image metadata
 * @param {string} opts.videoUrl - Uploaded video URL (from Weavy media)
 * @param {object} opts.videoMeta - Video metadata
 * @param {string} [opts.prompt]
 * @param {string} [opts.modelType] - "Standard" | "Pro"
 * @param {string} [opts.version] - "V2.6" | "V3.0"
 * @param {boolean} [opts.keepOriginalSound]
 * @param {string} [opts.characterOrientation] - "image" | "video"
 * @param {number} [opts.numberOfRuns]
 * @param {object} [opts.recipeNodes] - Override node IDs (for per-account recipe)
 */
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, version, keepOriginalSound, characterOrientation }),
    ],
    edges: buildEdges({ nodeIds }),
  };
}

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