// @bun
import {
  ensureJsonOnlyFormat
} from "./chunk-5zqmmchg.js";
import {
  userInput
} from "./chunk-tefbm840.js";
import {
  keyHints
} from "./chunk-bexyahs9.js";
import {
  resolveLinkContext
} from "./chunk-2jcqy5j6.js";
import {
  bind,
  bold,
  box,
  fg,
  getActiveTheme,
  mount,
  onKey,
  router,
  signal,
  t,
  text
} from "./chunk-m2h26j5f.js";
import {
  createCliLogger
} from "./chunk-t993tmz7.js";
import {
  AdkError,
  AgentProject,
  ConfigWriter,
  auth
} from "./chunk-7krp2mgz.js";
import {
  Uk
} from "./chunk-3xrpxgq4.js";

// src/components/radio-buttons.ts
function radioButtons(renderer, scope, props) {
  const { options, initialSelected, onSubmit, onCancel } = props;
  const theme = getActiveTheme();
  const startIndex = (() => {
    if (initialSelected) {
      const i = options.findIndex((opt) => opt.id === initialSelected);
      if (i >= 0)
        return i;
    }
    const firstEnabled = options.findIndex((opt) => !opt.disabled);
    return firstEnabled >= 0 ? firstEnabled : 0;
  })();
  const focused = signal(startIndex);
  const rows = [];
  options.forEach((option, index) => {
    const isDisabled = option.disabled || false;
    const row = text(renderer, "");
    scope.add(bind(() => {
      const isFocused = focused() === index;
      const indicatorColor = isDisabled ? theme.text.dimmer : isFocused ? theme.accent.cyan : theme.text.dim;
      const labelColor = isDisabled ? theme.text.dimmer : isFocused ? theme.accent.cyan : theme.text.primary;
      const indicator = fg(indicatorColor)(isFocused ? "\u25CF " : "\u25CB ");
      const label = isFocused && !isDisabled ? fg(labelColor)(bold(option.label)) : fg(labelColor)(option.label);
      row.content = isDisabled ? t`${indicator}${label} ${fg(theme.text.dimmer)("(not installed)")}` : t`${indicator}${label}`;
    }, [focused]));
    const rowChildren = [box(renderer, {}, [row])];
    if (option.description) {
      const desc = text(renderer, "");
      scope.add(bind(() => {
        const isFocused = focused() === index;
        const color = isFocused && !isDisabled ? theme.text.dim : theme.text.dimmer;
        desc.content = t`${fg(color)(option.description)}`;
      }, [focused]));
      rowChildren.push(box(renderer, { paddingLeft: 4 }, [desc]));
    }
    rows.push(box(renderer, { flexDirection: "column" }, rowChildren));
  });
  const hints = [
    { key: "\u2191\u2193", label: "navigate" },
    { key: "Enter", label: "select" }
  ];
  if (onCancel)
    hints.push({ key: "Esc", label: "cancel" });
  rows.push(box(renderer, { marginTop: 1 }, [keyHints(renderer, hints)]));
  const nextEnabled = (from, dir) => {
    const n = options.length;
    for (let step = 1;step <= n; step++) {
      const idx = ((from + dir * step) % n + n) % n;
      if (!options[idx].disabled)
        return idx;
    }
    return from;
  };
  scope.add(onKey(renderer, (key) => {
    if (key.name === "escape" && onCancel) {
      onCancel();
      return;
    }
    if (key.name === "return" && options.length > 0 && !options[focused()].disabled) {
      onSubmit(options[focused()].value);
      return;
    }
    if (key.name === "up") {
      focused.set(nextEnabled(focused(), -1));
    }
    if (key.name === "down") {
      focused.set(nextEnabled(focused(), 1));
    }
  }));
  return box(renderer, { flexDirection: "column", marginTop: 1 }, rows);
}

// src/commands/adk-link-utils.ts
function validateBotName(value) {
  const trimmed = value.trim();
  if (!trimmed) {
    return "Bot name cannot be empty";
  }
  if (trimmed.length < 3) {
    return "Bot name must be at least 3 characters";
  }
  if (trimmed.length > 50) {
    return "Bot name must be less than 50 characters";
  }
  return null;
}
function getAutoCreateBotName(projectName) {
  if (!projectName) {
    return null;
  }
  const validationError = validateBotName(projectName);
  return validationError ? null : projectName.trim();
}
function findProjectNameExactMatch(bots, projectName) {
  if (!projectName) {
    return null;
  }
  const matches = bots.filter((bot) => bot.name.toLowerCase() === projectName.toLowerCase());
  return matches.length === 1 ? matches[0] : null;
}
function shouldClearDevIdAfterRelink(previous, next) {
  if (!previous?.devId) {
    return false;
  }
  return previous.botId !== next.botId || previous.workspaceId !== next.workspaceId || previous.apiUrl !== next.apiUrl;
}
function resolveLinkApiUrl(options) {
  return options.explicitApiUrl || options.projectApiUrl || options.credentialsApiUrl;
}
function getCreateNewBotAction(options) {
  const { fromInit, projectName } = options;
  const autoCreateBotName = fromInit ? getAutoCreateBotName(projectName) : null;
  if (autoCreateBotName) {
    return {
      type: "auto-create",
      botName: autoCreateBotName
    };
  }
  if (fromInit && projectName) {
    return {
      type: "prompt",
      message: `\u26A0\uFE0F  Project name "${projectName.trim()}" does not meet bot name requirements. Enter a bot name manually.`
    };
  }
  return {
    type: "prompt"
  };
}

// src/commands/adk-link.ts
async function resolveLinkCommandContext(apiUrl) {
  const context = await resolveLinkContext({
    apiUrl
  });
  return {
    project: context.project,
    credentials: context.credentials,
    apiUrl: context.apiUrl
  };
}
async function getClient(workspaceId, apiUrl, credentials) {
  const resolvedCredentials = credentials ?? await auth.getActiveCredentials();
  return new Uk({
    token: resolvedCredentials.token,
    apiUrl: apiUrl || resolvedCredentials.apiUrl,
    workspaceId: workspaceId || resolvedCredentials.workspaceId,
    headers: {
      "x-multiple-integrations": "true"
    }
  });
}
async function getBot(botId, workspaceId, apiUrl, credentials) {
  const client = await getClient(workspaceId, apiUrl, credentials);
  let bot;
  let introWsId;
  try {
    ({ bot } = await client.getBot({ id: botId }));
    ({ workspaceId: introWsId } = await client.introspect({ botId }));
  } catch (error) {
    throw new AdkError({
      code: "BOT_FETCH_FAILED",
      message: `Failed to get bot: ${error instanceof Error ? error.message : String(error)}`,
      expected: true,
      cause: error
    });
  }
  if (workspaceId && introWsId !== workspaceId) {
    throw new AdkError({
      code: "WORKSPACE_MISMATCH",
      message: `Bot ${botId} does not belong to workspace ${workspaceId}, it belongs to ${introWsId}`,
      expected: true
    });
  }
  return { ...bot, workspaceId };
}
async function listWorkspaces(apiUrl, credentials) {
  const client = await getClient(undefined, apiUrl, credentials);
  try {
    return await client.list.workspaces({}).collect();
  } catch (error) {
    throw new AdkError({
      code: "WORKSPACE_LIST_FAILED",
      message: `Failed to list workspaces: ${error instanceof Error ? error.message : String(error)}`,
      expected: true,
      cause: error
    });
  }
}
async function validateWorkspace(workspaceId, apiUrl, credentials) {
  const client = await getClient(workspaceId, apiUrl, credentials);
  try {
    return await client.getWorkspace({ id: workspaceId });
  } catch (error) {
    throw new AdkError({
      code: "WORKSPACE_ACCESS_FAILED",
      message: `Failed to access workspace: ${error instanceof Error ? error.message : String(error)}`,
      expected: true,
      cause: error
    });
  }
}
async function listBots(workspaceId, apiUrl, credentials) {
  const client = await getClient(workspaceId, apiUrl, credentials);
  try {
    return await client.list.bots({ dev: false }).collect();
  } catch (error) {
    throw new AdkError({
      code: "BOT_LIST_FAILED",
      message: `Failed to list bots: ${error instanceof Error ? error.message : String(error)}`,
      expected: true,
      cause: error
    });
  }
}
async function createBot(workspaceId, name, apiUrl, credentials) {
  const client = await getClient(workspaceId, apiUrl, credentials);
  try {
    const { bot } = await client.createBot({
      name: name.trim(),
      tags: {
        runtime: "adk"
      }
    });
    return bot;
  } catch (error) {
    throw new AdkError({
      code: "BOT_CREATE_FAILED",
      message: `Failed to create bot: ${error instanceof Error ? error.message : String(error)}`,
      expected: true,
      cause: error
    });
  }
}
function linkSetup(renderer, scope, deps) {
  const {
    initialWorkspaceId,
    initialBotId,
    initialDevId,
    apiUrl,
    credentials,
    fromInit,
    projectName,
    local,
    onComplete
  } = deps;
  const theme = getActiveTheme();
  const devId = initialDevId;
  const view = signal("loading");
  const messages = signal([]);
  let workspaces = [];
  let selectedWorkspace = null;
  let bots = [];
  let selectedBot = null;
  let matchedBot = null;
  let errorMsg = "";
  const addMessage = (m) => messages.set([...messages(), m]);
  const fail = (err) => {
    errorMsg = err instanceof Error ? err.message : String(err);
    view.set("error");
  };
  const saveAgentJson = async (workspaceId, botId, devBotId, botName) => {
    try {
      view.set("loading");
      if (devBotId) {
        try {
          const devBot = await getBot(devBotId, workspaceId, apiUrl, credentials);
          if (devBot.workspaceId !== workspaceId) {
            throw new AdkError({
              code: "INVALID_LINK_TARGET",
              message: `Dev bot ${devBotId} does not belong to workspace ${workspaceId}`,
              expected: true
            });
          }
          if (!devBot.dev) {
            addMessage({
              text: `${theme.symbols.warning} Bot ${devBotId} is not marked as a dev bot.`,
              color: theme.status.warning
            });
          }
        } catch (err) {
          addMessage({
            text: `${theme.symbols.warning} Could not validate dev bot ${devBotId}: ${err}`,
            color: theme.status.warning
          });
        }
      }
      const project = await AgentProject.load(process.cwd());
      const previousAgentInfo = project.agentInfo ? { ...project.agentInfo } : undefined;
      const resolvedCredentials = credentials ?? await auth.getActiveCredentials();
      const finalApiUrl = resolveLinkApiUrl({
        explicitApiUrl: apiUrl,
        projectApiUrl: previousAgentInfo?.apiUrl,
        credentialsApiUrl: resolvedCredentials.apiUrl
      });
      const clearStaleDevId = !local && !devBotId && shouldClearDevIdAfterRelink(previousAgentInfo, {
        botId,
        workspaceId,
        apiUrl: finalApiUrl
      });
      if (local) {
        await project.createAgentLocalInfo({
          botId,
          workspaceId,
          apiUrl: finalApiUrl,
          ...devBotId ? { devId: devBotId } : {}
        });
      } else {
        const agentInfo = {
          botId,
          workspaceId,
          apiUrl: finalApiUrl
        };
        await project.createAgentInfo(agentInfo);
        const localUpdates = {
          botId: undefined,
          workspaceId: undefined,
          apiUrl: undefined
        };
        if (devBotId) {
          localUpdates.devId = devBotId;
        } else if (clearStaleDevId) {
          localUpdates.devId = undefined;
        }
        await project.updateAgentLocalInfo(localUpdates);
      }
      if (botName && project.config?.name !== botName) {
        try {
          const configWriter = new ConfigWriter(project.path);
          await configWriter.updateName(botName);
        } catch {}
      }
      view.set("done");
      onComplete({
        botName: selectedBot?.name,
        botId: selectedBot?.id,
        workspaceName: selectedWorkspace?.name
      });
    } catch (err) {
      fail(err);
    }
  };
  const loadBots = async () => {
    try {
      view.set("loading");
      const botsData = await listBots(selectedWorkspace.id, apiUrl, credentials);
      bots = botsData;
      const exactMatch = findProjectNameExactMatch(botsData, projectName);
      if (exactMatch) {
        matchedBot = exactMatch;
        view.set("confirm-bot");
        return;
      }
      view.set("bots");
    } catch (err) {
      fail(err);
    }
  };
  const handleWorkspaceSelect = (workspace) => {
    selectedWorkspace = workspace;
    loadBots();
  };
  const handleBotSelect = async (bot) => {
    selectedBot = bot;
    await saveAgentJson(selectedWorkspace.id, bot.id, devId, bot.name);
  };
  const handleBotNameSubmit = async (name) => {
    try {
      view.set("loading");
      const bot = await createBot(selectedWorkspace.id, name, apiUrl, credentials);
      selectedBot = bot;
      await saveAgentJson(selectedWorkspace.id, bot.id, devId, bot.name);
    } catch (err) {
      fail(err);
    }
  };
  const handleCreateNewBot = () => {
    const action = getCreateNewBotAction({ fromInit, projectName });
    if (action.type === "auto-create") {
      handleBotNameSubmit(action.botName);
      return;
    }
    if (action.message) {
      addMessage({ text: action.message, color: theme.status.warning });
    }
    view.set("create-bot");
  };
  (async () => {
    try {
      if (initialBotId) {
        const bot = await getBot(initialBotId, initialWorkspaceId, apiUrl, credentials);
        if (bot.dev) {
          throw new AdkError({
            code: "INVALID_LINK_TARGET",
            message: "Cannot link to a dev bot. Please use a production bot.",
            expected: true
          });
        }
        if (initialWorkspaceId && bot.workspaceId !== initialWorkspaceId) {
          throw new AdkError({
            code: "INVALID_LINK_TARGET",
            message: `Bot ${initialBotId} does not belong to workspace ${initialWorkspaceId}`,
            expected: true
          });
        }
        const workspace = await validateWorkspace(bot.workspaceId, apiUrl, credentials);
        selectedBot = bot;
        selectedWorkspace = workspace;
        await saveAgentJson(workspace.id, bot.id, devId, bot.name);
        return;
      }
      if (initialWorkspaceId) {
        const workspace = await validateWorkspace(initialWorkspaceId, apiUrl, credentials);
        selectedWorkspace = workspace;
        await loadBots();
        return;
      }
      const ws = await listWorkspaces(apiUrl, credentials);
      if (ws.length === 1) {
        selectedWorkspace = ws[0];
        addMessage({ text: `Auto-linking to ${ws[0].name}`, color: theme.status.success });
        await loadBots();
        return;
      }
      workspaces = ws;
      view.set("workspaces");
    } catch (err) {
      fail(err);
    }
  })();
  const messagesBox = box(renderer, { flexDirection: "column" });
  scope.add(bind(() => {
    for (const child of [...messagesBox.getChildren()]) {
      messagesBox.remove(child.id);
      child.destroyRecursively();
    }
    const msgs = messages();
    if (msgs.length > 0) {
      messagesBox.add(box(renderer, { flexDirection: "column", marginBottom: 1 }, msgs.map((m) => text(renderer, t`${fg(m.color)(m.text)}`))));
    }
  }, [messages]));
  const body = router(renderer, scope, view, (v, viewScope) => {
    if (v === "loading") {
      return box(renderer, { flexDirection: "column" }, [text(renderer, t`${fg(theme.status.info)("Loading\u2026")}`)]);
    }
    if (v === "error") {
      return box(renderer, { flexDirection: "column" }, [
        text(renderer, t`${fg(theme.status.error)(`${theme.symbols.cross} ${errorMsg}`)}`),
        text(renderer, t`${fg(theme.text.dim)("Press Ctrl-C to exit.")}`)
      ]);
    }
    if (v === "workspaces") {
      const dropdownOptions = workspaces.map((ws) => ({
        id: ws.id,
        label: `${ws.name} (${ws.handle}) - ${ws.plan || "Free"}`,
        value: ws.id,
        description: ws.handle
      }));
      return box(renderer, { flexDirection: "column" }, [
        userInput(renderer, viewScope, {
          prompt: "Select a workspace (type to search):",
          placeholder: "Start typing to search workspaces...",
          dropdownConfigs: [{ trigger: "", options: dropdownOptions, behavior: "submit" }],
          onSubmit: (value) => {
            const ws = workspaces.find((w) => w.id === value);
            if (ws)
              handleWorkspaceSelect(ws);
          },
          validate: (value) => workspaces.find((w) => w.id === value) ? null : "Please select a workspace from the dropdown"
        })
      ]);
    }
    if (v === "bots") {
      const norm = projectName?.toLowerCase();
      const sortedBots = [...bots].sort((a, b) => {
        const aMatch = norm && a.name.toLowerCase() === norm ? -1 : 0;
        const bMatch = norm && b.name.toLowerCase() === norm ? -1 : 0;
        return aMatch - bMatch;
      });
      const dropdownOptions = [
        {
          id: "create-new",
          label: "Create a new bot",
          value: "create-new",
          description: "Create a new bot in this workspace"
        },
        ...sortedBots.map((bot) => {
          const shortId = `${bot.id.slice(0, 4)}...${bot.id.slice(-4)}`;
          const createdAt = new Date(bot.createdAt).toLocaleDateString();
          const isMatch = norm && bot.name.toLowerCase() === norm;
          return {
            id: bot.id,
            label: isMatch ? `${theme.symbols.pointer} ${bot.name}` : bot.name,
            value: bot.id,
            description: `ID: ${shortId} \u2022 Created: ${createdAt}`
          };
        })
      ];
      return box(renderer, { flexDirection: "column" }, [
        userInput(renderer, viewScope, {
          prompt: "Select a bot or create a new one (type to search):",
          placeholder: "Start typing to search bots...",
          dropdownConfigs: [{ trigger: "", options: dropdownOptions, behavior: "submit" }],
          onSubmit: (value) => {
            if (value === "create-new") {
              handleCreateNewBot();
            } else {
              const bot = bots.find((b) => b.id === value);
              if (bot)
                handleBotSelect(bot);
            }
          },
          validate: (value) => value === "create-new" || bots.find((b) => b.id === value) ? null : "Please select a bot from the dropdown",
          onCancel: () => view.set("workspaces")
        })
      ]);
    }
    if (v === "create-bot") {
      return box(renderer, { flexDirection: "column" }, [
        userInput(renderer, viewScope, {
          prompt: "Enter a name for your new bot:",
          placeholder: "My awesome bot",
          onSubmit: (name) => void handleBotNameSubmit(name),
          validate: validateBotName,
          onCancel: () => view.set("bots")
        })
      ]);
    }
    if (v === "confirm-bot") {
      const bot = matchedBot;
      return box(renderer, { flexDirection: "column" }, [
        text(renderer, t`${fg(theme.text.primary)("Found a bot matching your project name:")}`),
        text(renderer, t`${fg(theme.accent.cyan)(` Name: ${bot.name}`)}`),
        text(renderer, t`${fg(theme.text.dim)(` ID: ${bot.id}`)}`),
        radioButtons(renderer, viewScope, {
          options: [
            { id: "yes", label: "Yes, link to this bot", value: "yes" },
            { id: "no", label: "No, create a new bot or link to other bot", value: "no" }
          ],
          onSubmit: (value) => {
            if (value === "yes")
              handleBotSelect(matchedBot);
            else
              view.set("bots");
          },
          onCancel: () => view.set("bots")
        })
      ]);
    }
    const doneChildren = [
      text(renderer, t`${fg(theme.status.success)(`${theme.symbols.checkmark} Successfully linked local agent to remote bot!`)}`),
      text(renderer, t`${fg(theme.text.dim)(`Bot: ${selectedBot?.name} (${selectedBot?.id})`)}`),
      text(renderer, t`${fg(theme.text.dim)(`Workspace: ${selectedWorkspace?.name}`)}`)
    ];
    if (devId)
      doneChildren.push(text(renderer, t`${fg(theme.text.dim)(`Dev Bot ID: ${devId}`)}`));
    if (!fromInit) {
      doneChildren.push(box(renderer, { marginTop: 1 }, [
        text(renderer, t`${fg(theme.status.info)("You can now use 'adk deploy' and other remote operations.")}`)
      ]));
    }
    return box(renderer, { flexDirection: "column" }, doneChildren);
  });
  return box(renderer, { flexDirection: "column" }, [messagesBox, body]);
}
async function adkLink(options) {
  ensureJsonOnlyFormat(options.format);
  const logger = createCliLogger({ format: options.format });
  const isJson = options.format === "json";
  if (isJson) {
    try {
      if (!options.workspace || !options.bot) {
        throw new AdkError({
          code: "INVALID_LINK_TARGET",
          message: "--format json requires --workspace and --bot flags",
          expected: true
        });
      }
      const { project, credentials, apiUrl: resolvedApiUrl } = await resolveLinkCommandContext(options.apiUrl);
      const bot = await getBot(options.bot, options.workspace, resolvedApiUrl, credentials);
      if (bot.dev) {
        throw new AdkError({
          code: "INVALID_LINK_TARGET",
          message: "Cannot link to a dev bot. Please use a production bot.",
          expected: true
        });
      }
      const workspace = await validateWorkspace(options.workspace, resolvedApiUrl, credentials);
      const previousAgentInfo = project.agentInfo ? { ...project.agentInfo } : undefined;
      const finalApiUrl = resolveLinkApiUrl({
        explicitApiUrl: options.apiUrl,
        projectApiUrl: previousAgentInfo?.apiUrl,
        credentialsApiUrl: credentials.apiUrl
      });
      const clearStaleDevId = !options.local && !options.dev && shouldClearDevIdAfterRelink(previousAgentInfo, {
        botId: options.bot,
        workspaceId: options.workspace,
        apiUrl: finalApiUrl
      });
      if (options.local) {
        await project.createAgentLocalInfo({
          botId: options.bot,
          workspaceId: options.workspace,
          apiUrl: finalApiUrl,
          ...options.dev ? { devId: options.dev } : {}
        });
      } else {
        await project.createAgentInfo({
          botId: options.bot,
          workspaceId: options.workspace,
          apiUrl: finalApiUrl
        });
        const localUpdates = {
          botId: undefined,
          workspaceId: undefined,
          apiUrl: undefined
        };
        if (options.dev) {
          localUpdates.devId = options.dev;
        } else if (clearStaleDevId) {
          localUpdates.devId = undefined;
        }
        await project.updateAgentLocalInfo(localUpdates);
      }
      if (bot.name && project.config?.name !== bot.name) {
        try {
          const configWriter = new ConfigWriter(project.path);
          await configWriter.updateName(bot.name);
        } catch {}
      }
      logger.info(`Linked to bot ${options.bot} in workspace ${workspace.id}`).result({ success: true, botId: options.bot, workspaceId: workspace.id });
    } catch (error) {
      logger.fatal(error);
    }
    return;
  }
  try {
    const {
      project,
      credentials: linkCredentials,
      apiUrl: resolvedApiUrl
    } = await resolveLinkCommandContext(options.apiUrl);
    if (!options.force && !options.local) {
      if (project.agentInfo?.botId) {
        logger.warn("\u26A0\uFE0F  agent.json already exists:");
        logger.info(`   Bot ID: ${project.agentInfo.botId}`, "gray");
        logger.info(`   Workspace ID: ${project.agentInfo.workspaceId}`, "gray");
        logger.info("\uD83D\uDCA1 Use --force to overwrite, or delete agent.json manually.", "blue");
        return;
      }
    }
    let appRef = null;
    const app = await mount((renderer, scope) => linkSetup(renderer, scope, {
      initialWorkspaceId: options.workspace,
      initialBotId: options.bot,
      initialDevId: options.dev,
      apiUrl: resolvedApiUrl,
      credentials: linkCredentials,
      fromInit: options.fromInit,
      projectName: options.projectName,
      local: options.local,
      onComplete: () => setTimeout(() => appRef?.unmount(), 120)
    }), { exitOnCtrlC: true });
    appRef = app;
    await app.waitUntilExit();
  } catch (error) {
    logger.fatal(error);
  }
}

export { linkSetup, adkLink };
