// Builders for Weavy recipe execute payload, based on the actual flow structure
// from Kling 2.6 Motion Control (Standard, locked) via FAL.AI / fal_imported
//
// 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 (Kling 2.6 Motion Control) ──
const DEFAULT_NODE_IDS = {
  model:   "d71e5878-4bbf-4b2a-96f4-5d951de57ade",
  image:   "47a69cb2-c969-4b7a-882d-d252d627945d",
  video:   "5a74898a-cd75-4ee0-911c-e0926dea3cb1",
  export:  "077ea58d-33f1-49fb-b2a5-3e0610817888",
  group:   "01f160e1-e6c1-4dd1-a013-00755e03ddce",
};

// ── Handle IDs (locked to the current recipe shape) ──
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_DISPLAY_NAME = "Kling 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: "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: "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 of 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",
};

// ── LOCKED VALUES (frontend is locked to Kling 2.6 Standard) ──
const LOCKED_MODEL = "Standard";
const LOCKED_VERSION = "V2.6";
const LOCKED_ORIENTATION = "video";

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,
      },
      video_url: {
        id: HANDLE_IDS.video_url,
        type: "video",
        label: "video",
        format: "text",
        required: true,
        description: DESCRIPTIONS.video,
      },
    },
    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, keepOriginalSound, imageFile, videoFile }) {
  return {
    type: "wildcard",
    model: {
      type: "predefined",
      name: MODEL_INFO.name,
      version: MODEL_INFO.version,
      service: MODEL_INFO.service,
      description: MODEL_DESCRIPTION,
    },
    inputs: [
      [
        { id: "prompt", title: "Prompt", description: DESCRIPTIONS.prompt, validTypes: ["text"], required: false },
        null,
      ],
      [
        { id: "image_url", title: "image", description: DESCRIPTIONS.image, validTypes: ["image"], required: true },
        { nodeId: nodeIds.image, outputId: "file", file: imageFile },
      ],
      [
        { id: "video_url", title: "video", description: DESCRIPTIONS.video, 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: LOCKED_MODEL } },
      ],
      [
        { 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: LOCKED_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: LOCKED_ORIENTATION } },
      ],
    ],
    outputs: [
      { id: "result", title: "result", description: "Result video", dataType: "video" },
    ],
  };
}

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

function buildImageImportNode({ nodeIds, imageFile }) {
  return {
    id: nodeIds.image,
    dragHandle: ".node-header",
    type: "import",
    isModel: false,
    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-06-16T09:51:23.801Z",
    updatedAt: "2025-06-03T09:49:14.704Z",
    locked: false,
    parentId: nodeIds.group,
    position: { x: 30, y: 30 },
    selected: false,
    width: 460,
    height: 556,
  };
}

function buildVideoImportNode({ nodeIds, videoFile }) {
  return {
    id: nodeIds.video,
    dragHandle: ".node-header",
    type: "import",
    isModel: false,
    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-06-16T09:51:23.801Z",
    updatedAt: "2025-06-03T09:49:14.704Z",
    locked: false,
    parentId: nodeIds.group,
    position: { x: 30, y: 810 },
    selected: false,
    width: 460,
    height: 556,
  };
}

function buildExportNode({ nodeIds }) {
  return {
    id: nodeIds.export,
    dragHandle: ".node-header",
    type: "export",
    isModel: false,
    data: {
      handles: {
        input: {
          file: { type: "any", label: "file", order: 0, required: false, description: "File to download" },
        },
        output: {},
      },
      name: "Export",
      description: "Export file",
      color: "Yambo_Blue",
      label: null,
      menu: null,
      params: null,
      schema: null,
      version: 3,
      dark_color: "Yambo_Blue_Dark",
      border_color: "Yambo_Blue_Stroke",
      inputNode: { nodeId: nodeIds.model, outputId: "result" },
    },
    createdAt: "2026-06-16T09:51:38.810Z",
    updatedAt: "2025-06-03T09:49:14.704Z",
    locked: false,
    parentId: nodeIds.group,
    position: { x: 1300, y: 630 },
    selected: false,
    width: 460,
    height: 112,
  };
}

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-2.6",
      handles: { input: [], output: [] },
      width: 1790,
      height: 1799,
      labelFontSize: 16,
    },
    position: { x: 3770, y: 1520 },
    width: 1790,
    height: 1799,
    locked: false,
    selected: true,
    createdAt: "2026-06-16T09:51:23.801Z",
  };
}

// Input edges only (image→model, video→model). Used by execute payload —
// mirrors the working HOST.CPANEL structure where execute only sends the
// model node with files embedded, and topology edges feed into it.
function buildInputEdges({ nodeIds }) {
  return [
    {
      id: "9de29626-e496-4c7b-97f2-dae77cab83d8",
      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: false,
    },
    {
      id: "1f9703b9-5c56-444b-924b-6f0d08b52454",
      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: false,
    },
  ];
}

// Full edge set including the export edge. Used by save payload (full graph).
function buildEdges({ nodeIds }) {
  return [
    ...buildInputEdges({ nodeIds }),
    {
      id: "02e481db-8a0c-4c66-a68a-8e3ffbd9d033",
      source: nodeIds.model,
      target: nodeIds.export,
      sourceHandle: `${nodeIds.model}-output-result`,
      targetHandle: `${nodeIds.export}-input-file`,
      type: "custom",
      data: { sourceColor: "Red", targetColor: "Yambo_Blue", sourceHandleType: "video", targetHandleType: "any" },
      selected: false,
    },
  ];
}

/**
 * Build the execute payload for Weavy recipe API.
 * Mirrors the working HOST.CPANEL structure: only the model node is sent,
 * with image/video files embedded in params/input/kind. Import/export/group
 * nodes are NOT sent on execute — sending them makes Weavy try to "run" the
 * File node, which fails with "Failed running File".
 * NOTE: modelType/version/characterOrientation params are ignored —
 * the recipe is locked to Kling 2.6 Standard + video orientation.
 */
export function buildExecutePayload({
  imageUrl, imageMeta = {},
  videoUrl, videoMeta = {},
  prompt = "",
  modelType = LOCKED_MODEL,
  version = LOCKED_VERSION,
  keepOriginalSound = true,
  characterOrientation = LOCKED_ORIENTATION,
  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, keepOriginalSound }),
    ],
    edges: buildInputEdges({ nodeIds }),
  };
}

/**
 * Build the full save payload (complete graph) for persisting a recipe.
 * Includes import, export and group nodes plus all edges.
 */
export function buildSavePayload({
  imageUrl, imageMeta = {},
  videoUrl, videoMeta = {},
  prompt = "",
  keepOriginalSound = true,
  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, keepOriginalSound }),
      buildImageImportNode({ nodeIds, imageFile }),
      buildVideoImportNode({ nodeIds, videoFile }),
      buildExportNode({ nodeIds }),
      buildGroupNode({ nodeIds }),
    ],
    edges: buildEdges({ nodeIds }),
  };
}
