import React, { useMemo, useState } from 'react';
import { themedColor } from '../../theme';
import { ActivityIndicator, Linking, Pressable, Text, TextInput, View } from 'react-native';
import { CheckCircle2, KeyRound, Package, Plug, ShieldAlert } from 'lucide-react-native';

import { ConnectorBrandIcon, hasConnectorBrandIcon } from '../connectors/connectorBrandIcons';
import { connectorRequiresConnectionConfig } from '../connectors/connectorCatalog';
import { conversationStyles } from '../conversation/conversationStyles';
import { useAgentBi } from '../../analytics/mixpanelContext';
import { getSequenceSignupUrl } from './sequenceSignup';
import { getPackageActionMode } from './packageUtils';
import { buildSkillInstallApprovalPayload, getSkillInstallRef } from './skillInstallApproval';
import type { SkillInstallRef } from './skillInstallApproval';
import { isRetiredPaymentsByWixApproval } from '../../toolWidgets/skillsAndConnectionsUtils';
import type {
  SuperagentAgent,
  SuperagentConnector,
  SuperagentConnectorActionInput,
  SuperagentConversation,
  SuperagentToolCall,
} from '../../types';

type SubmitToolCallInput = (
  toolCallId: string,
  approve: boolean,
  extraUserInput?: unknown,
  originRequestId?: string,
) => Promise<SuperagentConversation | null>;

type ApprovalKind = 'channel' | 'connector' | 'customConnector' | 'package' | 'payment' | 'secrets' | 'skill' | 'guard' | 'generic';

type SecretSchemaItem = {
  description?: string;
  name: string;
};

export function ToolApprovalCard({
  agent,
  availableConnectors,
  onConnectConnector,
  submitToolCallInput,
  toolCall,
}: {
  agent: SuperagentAgent;
  availableConnectors?: SuperagentConnector[];
  // Accepted for backwards-compatible prop plumbing but intentionally unused:
  // the card only renders for tool calls still awaiting input (see
  // isApprovalRequest), so an approval is actionable regardless of position.
  isLastAssistantMessage?: boolean;
  onConnectConnector?: (input: SuperagentConnectorActionInput) => Promise<boolean | string | void> | boolean | string | void;
  submitToolCallInput?: SubmitToolCallInput;
  toolCall: SuperagentToolCall;
}) {
  const bi = useAgentBi();
  const args = useMemo(() => getToolArgs(toolCall), [toolCall]);
  const objectArgs = args && typeof args !== 'string' ? args : null;
  const isRetiredPayment = isRetiredPaymentsByWixApproval(toolCall.name, objectArgs);
  const guardData = useMemo(() => getGuardData(toolCall.results), [toolCall.results]);
  const kind = getApprovalKind(toolCall, guardData);
  const connectorId = getStringArg(args, ['integration_type', 'connector_id', 'provider']);
  const connector = availableConnectors?.find((item) => item.id === connectorId);
  const connectorName = connector?.name || formatConnectorName(connectorId);
  const paymentProvider = getStringArg(args, ['provider']);
  const paymentName = getPaymentName(paymentProvider || toolCall.name);
  const channelName = toolCall.name === 'setup_telegram_connection' ? 'Telegram' : 'Slack';
  const customConnectorName = getStringArg(args, ['name', 'integration_type']) || 'connector';
  const scopes = getStringArrayArg(args, 'scopes');
  const skillRef = kind === 'skill' ? getSkillInstallRef(args) : null;
  const reason = kind === 'skill'
    ? (skillRef?.description ?? '')
    : getStringArg(args, ['reason', 'description', 'message']);
  const packages = getPackageSpecs(args);
  const secretSchema = getSecretSchema(args);
  const signupUrl = getSequenceSignupUrl(toolCall.name);
  const displayName = kind === 'payment'
    ? (isRetiredPayment ? 'Payments by Wix' : paymentName)
    : kind === 'channel'
      ? channelName
      : kind === 'customConnector'
        ? formatConnectorName(customConnectorName)
        : kind === 'skill'
          ? (skillRef?.displayName || 'skill')
          : connectorName;
  const packageMode = kind === 'package' ? getPackageActionMode(packages) : null;
  const title = isRetiredPayment
    ? 'Payments by Wix is no longer available'
    : getApprovalTitle(kind, displayName, packageMode);
  const body = isRetiredPayment
    ? 'This pending installation request is no longer supported. Dismiss it to continue.'
    : getApprovalBody(kind, reason, guardData, displayName, packageMode);
  const primaryLabel = getPrimaryLabel(kind, displayName);
  const rejectLabel = isRetiredPayment ? 'Dismiss' : getRejectLabel(kind);
  const canAct = Boolean(toolCall.id && submitToolCallInput);
  // Track WHICH action is in flight so the spinner shows on the button the user
  // actually pressed — a single boolean lit up the primary (Install) spinner
  // even when the reject/dismiss action was the one submitting.
  const [pendingAction, setPendingAction] = useState<'approve' | 'reject' | null>(null);
  const isSubmitting = pendingAction !== null;
  const [error, setError] = useState<string | null>(null);
  const [formValues, setFormValues] = useState<Record<string, string>>({
    connectorName: customConnectorName === 'connector' ? '' : formatConnectorName(customConnectorName),
    scopes: scopes.join('\n'),
  });
  const [secretValues, setSecretValues] = useState<Record<string, string>>({});
  const [showKeyInput, setShowKeyInput] = useState(false);
  const needsSecrets = kind === 'secrets' && secretSchema.length > 0;
  const sequenceAwaitingReveal = needsSecrets && Boolean(signupUrl) && !showKeyInput;
  const secretsComplete = !needsSecrets || secretSchema.every((secret) => secretValues[secret.name]?.trim());
  const channelComplete = kind !== 'channel' || isChannelFormComplete(toolCall.name, formValues);
  const customConnectorComplete = kind !== 'customConnector' ||
    Boolean(formValues.connectorName?.trim() && formValues.clientId?.trim() && formValues.clientSecret?.trim());
  // Connectors needing extra connection_config (e.g. subdomain) can't complete
  // OAuth from native — the backend initiate rejects them — so block approval here
  // and steer to the web app, matching the connectors drawer. Skip stays enabled.
  // Fall back to the catalog: listConnectors drops already-connected integrations
  // from availableConnectors, so an already-connected config-required connector
  // (scope upgrade / force_reconnect) would otherwise have connector === undefined
  // and slip past this gate, then fail backend validation for missing connection_config.
  const connectorNeedsConfig = kind === 'connector'
    && (Boolean(connector?.requiresConnectionConfig) || connectorRequiresConnectionConfig(connectorId));
  // BYO_SHARED connectors arrive as an OrganizationConnector connector_id (no
  // integration_type). The native personal-agent facade has no connector_id field
  // and requires a real integration type, so the OAuth can only fail — block it
  // here too and steer to the web app.
  const isByoConnector = kind === 'connector'
    && !getStringArg(args, ['integration_type'])
    && Boolean(getStringArg(args, ['connector_id']));
  // register_workspace_connector for an integration needing connection_config
  // (e.g. subdomain) can't succeed here: this card only collects id/secret/name/
  // scopes, not connection_config, so the backend's get_validated_connection_config
  // rejects it. Block it and steer to the web app, like the config-required OAuth
  // path above. Skip stays enabled.
  const customConnectorNeedsConfig = kind === 'customConnector'
    && connectorRequiresConnectionConfig(getStringArg(args, ['integration_type', 'name']));
  const connectorUnsupportedHere = connectorNeedsConfig || isByoConnector || customConnectorNeedsConfig;
  // Without a namespace/slug reference an approve would reach the backend as a
  // decline (see skillInstallApproval.ts), so block it instead.
  const skillComplete = kind !== 'skill' || Boolean(skillRef);
  const disablePrimary = !canAct || isSubmitting || !secretsComplete || !channelComplete || !customConnectorComplete || !skillComplete || connectorUnsupportedHere || sequenceAwaitingReveal;

  const submitApproval = async () => {
    if (!toolCall.id || !submitToolCallInput) return;

    setPendingAction('approve');
    setError(null);
    try {
      let connectionId: string | undefined;
      if (kind === 'connector' && onConnectConnector && connectorId) {
        const connected = await onConnectConnector({
          accessMode: 'full_access',
          agentId: agent.id,
          connectorId,
          // Honor the agent's force_reconnect arg (e.g. "switch accounts") so the
          // initiate route shows the provider/account picker instead of
          // short-circuiting as already-authorized — matches the web flow.
          forceReconnect: getBooleanArg(args, ['force_reconnect']),
          scopes: scopes.length > 0 ? scopes : connector?.scopes,
        });
        if (connected === false) {
          throw new Error(`${connectorName} was not connected.`);
        }
        // onConnectConnector returns the connection_id (string) on success; the
        // backend verifies it against the just-created AppIntegration before
        // marking the tool successful, matching the web flow.
        if (typeof connected === 'string') {
          connectionId = connected;
        }
      }

      const result = await submitToolCallInput(toolCall.id, true, getApprovalPayload(kind, secretValues, paymentProvider, toolCall.name, formValues, connectionId, skillRef));
      if (result === null) {
        // The conversation client isn't ready yet (still initializing), so nothing
        // reached the server. Surface it instead of silently no-opping.
        throw new Error('The conversation is still loading. Try again in a moment.');
      }
      if (kind === 'skill') void bi.trackEditor('Skill Install', { decision: 'approved', skill_name: skillRef?.displayName });
      else void bi.trackEditor('Tool Approval', { kind, decision: 'approved' });
    } catch (submitError) {
      setError(submitError instanceof Error ? submitError.message : 'Unable to approve this request.');
    } finally {
      setPendingAction(null);
    }
  };

  const rejectApproval = async () => {
    if (!toolCall.id || !submitToolCallInput) return;

    setPendingAction('reject');
    setError(null);
    try {
      const result = await submitToolCallInput(toolCall.id, false, {});
      if (result === null) {
        throw new Error('The conversation is still loading. Try again in a moment.');
      }
      if (kind === 'skill') void bi.trackEditor('Skill Install', { decision: 'dismissed', skill_name: skillRef?.displayName });
      else void bi.trackEditor('Tool Approval', { kind, decision: 'rejected' });
    } catch (submitError) {
      setError(submitError instanceof Error ? submitError.message : 'Unable to reject this request.');
    } finally {
      setPendingAction(null);
    }
  };

  return (
    <View style={conversationStyles.toolApprovalCard}>
      <View style={conversationStyles.toolApprovalHeader}>
        <ApprovalIcon connector={connector} connectorId={connectorId} kind={kind} />
        <View style={conversationStyles.toolApprovalHeaderText}>
          <Text style={conversationStyles.toolApprovalTitle}>{title}</Text>
          <Text style={conversationStyles.toolApprovalSubtitle}>Waiting For User Input</Text>
        </View>
      </View>

      {sequenceAwaitingReveal ? (
        <Text style={conversationStyles.toolApprovalBody}>Connect your Sequence account to continue.</Text>
      ) : body ? (
        <Text style={conversationStyles.toolApprovalBody}>{body}</Text>
      ) : null}

      {connectorUnsupportedHere ? (
        <Text style={conversationStyles.toolApprovalMuted}>
          Connect {connectorName} from the web app — it isn't supported in the app yet.
        </Text>
      ) : null}

      {kind === 'connector' && scopes.length > 0 ? (
        <View style={conversationStyles.toolApprovalScopeWrap}>
          <Text style={conversationStyles.toolApprovalMuted}>Requested access</Text>
          <View style={conversationStyles.toolApprovalChipWrap}>
            {scopes.slice(0, 4).map((scope) => (
              <View key={scope} style={conversationStyles.toolApprovalChip}>
                <Text numberOfLines={1} style={conversationStyles.toolApprovalChipText}>{scope}</Text>
              </View>
            ))}
            {scopes.length > 4 ? (
              <View style={conversationStyles.toolApprovalChip}>
                <Text style={conversationStyles.toolApprovalChipText}>+{scopes.length - 4}</Text>
              </View>
            ) : null}
          </View>
        </View>
      ) : null}

      {kind === 'package' && packages.length > 0 ? (
        <View style={conversationStyles.toolApprovalChipWrap}>
          {packages.map((packageSpec) => (
            <View key={packageSpec.label} style={conversationStyles.toolApprovalChip}>
              <Text numberOfLines={1} style={conversationStyles.toolApprovalChipText}>
                {packageSpec.action === 'uninstall' ? `− ${packageSpec.label}` : packageSpec.label}
              </Text>
            </View>
          ))}
        </View>
      ) : null}

      {needsSecrets && sequenceAwaitingReveal ? (
        <View style={conversationStyles.toolApprovalInputs}>
          <Pressable
            accessibilityRole="button"
            onPress={() => { if (signupUrl) void Linking.openURL(signupUrl).catch(() => {}); }}
            style={conversationStyles.toolApprovalPrimaryButton}
          >
            <Text style={conversationStyles.toolApprovalPrimaryText}>Log in / Sign up to Sequence</Text>
          </Pressable>
          <Pressable accessibilityRole="button" onPress={() => setShowKeyInput(true)}>
            <Text style={conversationStyles.toolApprovalInputLink}>I already have an API key</Text>
          </Pressable>
        </View>
      ) : null}

      {needsSecrets && !sequenceAwaitingReveal ? (
        <View style={conversationStyles.toolApprovalInputs}>
          {secretSchema.map((secret) => (
            <ApprovalInput
              canAct={canAct}
              help={secret.description}
              isSubmitting={isSubmitting}
              key={secret.name}
              label={secret.name}
              onChange={(value) => setSecretValues((current) => ({ ...current, [secret.name]: value }))}
              placeholder={`Enter ${secret.name}`}
              secure
              value={secretValues[secret.name] ?? ''}
            />
          ))}
        </View>
      ) : null}

      {kind === 'channel' ? (
        <View style={conversationStyles.toolApprovalInputs}>
          {toolCall.name === 'setup_telegram_connection' ? (
            <ApprovalInput
              canAct={canAct}
              help="Paste the bot token from BotFather, or leave empty to create a managed bot."
              isSubmitting={isSubmitting}
              label="Bot Token"
              onChange={(value) => setFormValues((current) => ({ ...current, botToken: extractTelegramToken(value) }))}
              placeholder="123456789:ABC..."
              secure
              value={formValues.botToken ?? ''}
            />
          ) : (
            <>
              <ApprovalInput
                canAct={canAct}
                help="From OAuth & Permissions. Starts with xoxb-."
                isSubmitting={isSubmitting}
                label="Bot User OAuth Token"
                onChange={(value) => setFormValues((current) => ({ ...current, botToken: value }))}
                placeholder="xoxb-..."
                secure
                value={formValues.botToken ?? ''}
              />
              <ApprovalInput
                canAct={canAct}
                help="From Basic Information > App Credentials."
                isSubmitting={isSubmitting}
                label="Signing Secret"
                onChange={(value) => setFormValues((current) => ({ ...current, signingSecret: value }))}
                placeholder="Enter signing secret"
                secure
                value={formValues.signingSecret ?? ''}
              />
            </>
          )}
        </View>
      ) : null}

      {kind === 'customConnector' ? (
        <View style={conversationStyles.toolApprovalInputs}>
          <ApprovalInput
            canAct={canAct}
            isSubmitting={isSubmitting}
            label="Connector name"
            onChange={(value) => setFormValues((current) => ({ ...current, connectorName: value }))}
            placeholder="Calendar"
            value={formValues.connectorName ?? ''}
          />
          <ApprovalInput
            canAct={canAct}
            isSubmitting={isSubmitting}
            label="Client ID"
            onChange={(value) => setFormValues((current) => ({ ...current, clientId: value }))}
            placeholder="OAuth client id"
            value={formValues.clientId ?? ''}
          />
          <ApprovalInput
            canAct={canAct}
            isSubmitting={isSubmitting}
            label="Client Secret"
            onChange={(value) => setFormValues((current) => ({ ...current, clientSecret: value }))}
            placeholder="OAuth client secret"
            secure
            value={formValues.clientSecret ?? ''}
          />
          <ApprovalInput
            canAct={canAct}
            help="One scope per line."
            isSubmitting={isSubmitting}
            label="Scopes"
            multiline
            onChange={(value) => setFormValues((current) => ({ ...current, scopes: value }))}
            placeholder="https://www.googleapis.com/auth/calendar.readonly"
            value={formValues.scopes ?? ''}
          />
        </View>
      ) : null}

      {canAct ? (
        <View style={conversationStyles.toolApprovalActions}>
          {!isRetiredPayment && !sequenceAwaitingReveal ? (
            <Pressable
              accessibilityRole="button"
              disabled={disablePrimary}
              onPress={submitApproval}
              style={[
                conversationStyles.toolApprovalPrimaryButton,
                disablePrimary && conversationStyles.toolApprovalButtonDisabled,
              ]}
            >
              {pendingAction === 'approve' ? <ActivityIndicator color={themedColor('#111111')} size="small" /> : <CheckCircle2 color={themedColor('#111111')} size={15} strokeWidth={2.5} />}
              <Text style={conversationStyles.toolApprovalPrimaryText}>{pendingAction === 'approve' ? 'Working...' : primaryLabel}</Text>
            </Pressable>
          ) : null}
          <Pressable
            accessibilityRole="button"
            disabled={isSubmitting}
            onPress={rejectApproval}
            style={[
              conversationStyles.toolApprovalSecondaryButton,
              isSubmitting && conversationStyles.toolApprovalButtonDisabled,
            ]}
          >
            {pendingAction === 'reject' ? (
              <ActivityIndicator color={themedColor('#E4E4E7')} size="small" />
            ) : (
              <Text style={conversationStyles.toolApprovalSecondaryText}>{sequenceAwaitingReveal ? 'Skip' : rejectLabel}</Text>
            )}
          </Pressable>
        </View>
      ) : (
        <Text style={conversationStyles.toolApprovalPreviousText}>
          Approval controls are not available here.
        </Text>
      )}

      {error ? <Text style={conversationStyles.toolApprovalError}>{error}</Text> : null}
    </View>
  );
}

function ApprovalIcon({
  connector,
  connectorId,
  kind,
}: {
  connector?: SuperagentConnector;
  connectorId: string;
  kind: ApprovalKind;
}) {
  if (kind === 'connector' && connectorId && hasConnectorBrandIcon(connectorId)) {
    return (
      <View style={conversationStyles.toolApprovalBrandIcon}>
        <ConnectorBrandIcon connectorId={connectorId} size={20} />
      </View>
    );
  }

  const iconColor = kind === 'guard' ? '#FDE047' : '#F4F4F5';
  const fallbackLabel = connector?.iconFallbackLabel || connectorId.slice(0, 2).toUpperCase();
  if (kind === 'connector' && fallbackLabel) {
    return (
      <View style={conversationStyles.toolApprovalBrandIcon}>
        <Text style={conversationStyles.toolApprovalBrandFallback}>{fallbackLabel}</Text>
      </View>
    );
  }

  return (
    <View style={conversationStyles.toolApprovalIcon}>
      {kind === 'package' || kind === 'skill' ? <Package color={iconColor} size={18} strokeWidth={2.3} /> : null}
      {kind === 'payment' ? <Plug color={iconColor} size={18} strokeWidth={2.3} /> : null}
      {kind === 'secrets' ? <KeyRound color={iconColor} size={18} strokeWidth={2.3} /> : null}
      {kind === 'guard' ? <ShieldAlert color={iconColor} size={18} strokeWidth={2.3} /> : null}
      {kind === 'generic' ? <Plug color={iconColor} size={18} strokeWidth={2.3} /> : null}
    </View>
  );
}

function ApprovalInput({
  canAct,
  help,
  isSubmitting,
  label,
  multiline,
  onChange,
  placeholder,
  secure,
  value,
}: {
  canAct: boolean;
  help?: string;
  isSubmitting: boolean;
  label: string;
  multiline?: boolean;
  onChange: (value: string) => void;
  placeholder: string;
  secure?: boolean;
  value: string;
}) {
  return (
    <View style={conversationStyles.toolApprovalInputGroup}>
      <Text style={conversationStyles.toolApprovalInputLabel}>{label}</Text>
      {help ? <Text style={conversationStyles.toolApprovalInputHelp}>{help}</Text> : null}
      <TextInput
        autoCapitalize="none"
        autoCorrect={false}
        editable={!isSubmitting && canAct}
        multiline={multiline}
        onChangeText={onChange}
        placeholder={placeholder}
        placeholderTextColor={themedColor('#6F6F76')}
        secureTextEntry={secure}
        style={[
          conversationStyles.toolApprovalInput,
          multiline && conversationStyles.toolApprovalMultilineInput,
        ]}
        textAlignVertical={multiline ? 'top' : 'center'}
        value={value}
      />
    </View>
  );
}

function getApprovalKind(toolCall: SuperagentToolCall, guardData: Record<string, unknown> | null): ApprovalKind {
  if (guardData) return 'guard';
  if (toolCall.name === 'setup_slack_connection' || toolCall.name === 'setup_telegram_connection') return 'channel';
  if (toolCall.name === 'set_app_user_connector' || toolCall.name === 'register_workspace_connector') return 'customConnector';
  if (toolCall.name === 'request_oauth_authorization') return 'connector';
  if (toolCall.name === 'install_npm_package') return 'package';
  if (toolCall.name === 'suggest_payments_installation') return 'payment';
  if (toolCall.name === 'request_secrets' || toolCall.name === 'set_secrets' || toolCall.name === 'sequence_connect') return 'secrets';
  if (toolCall.name === 'suggest_skill_installation') return 'skill';
  return 'generic';
}

function getApprovalTitle(kind: ApprovalKind, displayName: string, packageMode?: 'install' | 'uninstall' | 'mixed' | null) {
  if (kind === 'channel') return `Connect ${displayName}`;
  if (kind === 'connector') return `Connect to ${displayName}`;
  if (kind === 'customConnector') return `Set up ${displayName}`;
  if (kind === 'payment') return `Connect ${displayName}`;
  if (kind === 'package') {
    if (packageMode === 'uninstall') return 'Approve package removal';
    if (packageMode === 'mixed') return 'Approve package changes';
    return 'Approve package install';
  }
  if (kind === 'secrets') return 'Provide secrets';
  if (kind === 'skill') return `Install ${displayName}`;
  if (kind === 'guard') return 'Approve protected change';
  return 'Approval required';
}

function getApprovalBody(
  kind: ApprovalKind,
  reason: string,
  guardData: Record<string, unknown> | null,
  displayName: string,
  packageMode?: 'install' | 'uninstall' | 'mixed' | null,
) {
  if (kind === 'channel') return reason || `Paste your ${displayName} credentials to connect this channel.`;
  if (kind === 'connector') return reason || `${displayName} needs authorization before this agent can continue.`;
  if (kind === 'customConnector') return reason || 'Provide OAuth credentials for this custom connector.';
  if (kind === 'payment') return reason || `Approve setting up ${displayName} for this app.`;
  if (kind === 'package') {
    if (reason) return reason;
    if (packageMode === 'uninstall') return 'Approve removing these packages from the app.';
    if (packageMode === 'mixed') return 'Approve these package changes for the app.';
    return 'Approve installing the packages required for this step.';
  }
  if (kind === 'secrets') return reason || 'Enter the requested secret values to continue.';
  if (kind === 'skill') return reason || `Approve installing ${displayName} from the skill store.`;
  if (kind === 'guard') return String(guardData?.reason || 'Review and approve this protected change to continue.');
  return reason || 'Review this request to continue.';
}

function getPrimaryLabel(kind: ApprovalKind, displayName: string) {
  if (kind === 'channel' || kind === 'connector') return `Connect ${displayName}`;
  if (kind === 'customConnector') return 'Connect';
  if (kind === 'payment') return 'Connect';
  if (kind === 'secrets') return 'Submit';
  if (kind === 'skill') return 'Install';
  return 'Approve';
}

function getRejectLabel(kind: ApprovalKind) {
  if (kind === 'connector' || kind === 'channel') return 'Skip';
  if (kind === 'skill') return 'Dismiss';
  return 'Reject';
}

function getApprovalPayload(
  kind: ApprovalKind,
  secretValues: Record<string, string>,
  paymentProvider: string,
  toolName: string,
  formValues: Record<string, string>,
  connectionId?: string,
  skillRef?: SkillInstallRef | null,
) {
  if (kind === 'channel' && toolName === 'setup_telegram_connection') {
    // Empty string routes to the managed one-click bot flow on the backend.
    return { bot_token: formValues.botToken?.trim() ?? '' };
  }
  if (kind === 'channel') {
    return { bot_token: formValues.botToken, signing_secret: formValues.signingSecret };
  }
  if (kind === 'customConnector') {
    return {
      client_id: formValues.clientId?.trim(),
      client_secret: formValues.clientSecret?.trim(),
      name: formValues.connectorName?.trim(),
      scopes: splitLines(formValues.scopes),
    };
  }
  if (kind === 'secrets') return { secrets: secretValues };
  if (kind === 'payment' && paymentProvider === 'wix_payments') return { tos_accepted: true };
  if (kind === 'connector') return connectionId ? { connection_id: connectionId } : {};
  if (kind === 'skill' && skillRef) return buildSkillInstallApprovalPayload(skillRef);
  return {};
}

function getToolArgs(toolCall: SuperagentToolCall): Record<string, unknown> | string | null {
  const raw = toolCall.arguments_string ?? toolCall.arguments ?? null;
  if (!raw) return null;
  if (typeof raw !== 'string') return raw as Record<string, unknown>;

  try {
    const parsed = JSON.parse(raw);
    return parsed && typeof parsed === 'object' ? parsed as Record<string, unknown> : raw;
  } catch {
    return raw;
  }
}

function getStringArg(args: Record<string, unknown> | string | null, keys: string[]) {
  if (!args || typeof args === 'string') return '';
  for (const key of keys) {
    const value = args[key];
    if (typeof value === 'string' && value.trim()) return value.trim();
  }
  return '';
}

function getBooleanArg(args: Record<string, unknown> | string | null, keys: string[]): boolean {
  if (!args || typeof args === 'string') return false;
  for (const key of keys) {
    const value = args[key];
    if (typeof value === 'boolean') return value;
    if (value === 'true') return true;
  }
  return false;
}

function getStringArrayArg(args: Record<string, unknown> | string | null, key: string) {
  if (!args || typeof args === 'string') return [];
  const value = args[key];
  if (Array.isArray(value)) return value.filter((item): item is string => typeof item === 'string' && item.trim().length > 0);
  if (typeof value === 'string' && value.trim()) return [value.trim()];
  return [];
}

type PackageAction = 'install' | 'uninstall';
interface PackageSpec {
  label: string;
  action: PackageAction;
}

function getPackageSpecs(args: Record<string, unknown> | string | null): PackageSpec[] {
  if (!args || typeof args === 'string') return [];
  const packages = args.packages;
  if (Array.isArray(packages)) {
    return packages
      .map((packageEntry): PackageSpec | null => {
        if (packageEntry && typeof packageEntry === 'object') {
          const entry = packageEntry as Record<string, unknown>;
          const name = String(entry.name || '').trim();
          if (!name) return null;
          const action: PackageAction = entry.action === 'uninstall' ? 'uninstall' : 'install';
          const semver = String(entry.semver || '').trim();
          const label = action === 'install' && semver ? `${name}@${semver}` : name;
          return { label, action };
        }
        const label = String(packageEntry || '').trim();
        return label ? { label, action: 'install' } : null;
      })
      .filter((spec): spec is PackageSpec => spec !== null);
  }
  if (typeof packages === 'string' && packages.trim()) {
    return packages
      .split(packages.includes(',') ? ',' : /\s+/)
      .map((item) => item.trim())
      .filter(Boolean)
      .map((label) => ({ label, action: 'install' as const }));
  }

  const packageName = getStringArg(args, ['package_name']);
  const version = getStringArg(args, ['version']);
  if (!packageName) return [];
  return [{ label: version ? `${packageName}@${version}` : packageName, action: 'install' }];
}

function getSecretSchema(args: Record<string, unknown> | string | null): SecretSchemaItem[] {
  if (!args || typeof args === 'string') return [];
  let schema = args.secrets_schema;
  if (typeof schema === 'string') {
    try {
      schema = JSON.parse(schema);
    } catch {
      schema = [];
    }
  }
  if (!Array.isArray(schema)) return [];

  const secretItems: SecretSchemaItem[] = [];
  schema.forEach((item) => {
    if (!item || typeof item !== 'object') return;
      const record = item as Record<string, unknown>;
      const name = String(record.secretName || record.name || record.key || '').trim();
      if (!name) return;
      secretItems.push({
        description: typeof record.description === 'string' ? record.description : undefined,
        name,
      });
  });

  return secretItems;
}

function isChannelFormComplete(toolName: string, formValues: Record<string, string>) {
  if (toolName === 'setup_telegram_connection') {
    const token = formValues.botToken?.trim() || '';
    // An empty token is valid: the backend creates a managed one-click bot in that
    // case. A token that *is* provided must be a well-formed BotFather token.
    return token === '' || /^\d+:[A-Za-z0-9_-]{35,}$/.test(token);
  }

  return Boolean(
    formValues.botToken?.trim().startsWith('xoxb-') &&
      (formValues.botToken?.trim().length ?? 0) >= 20 &&
      (formValues.signingSecret?.trim().length ?? 0) >= 16,
  );
}

function extractTelegramToken(value: string) {
  const trimmed = value.trim();
  if (/^\d+:[A-Za-z0-9_-]{35,}$/.test(trimmed)) return trimmed;
  const match = trimmed.match(/\d{5,15}:[A-Za-z0-9_-]{35,50}/);
  return match?.[0] ?? value;
}

function splitLines(value?: string) {
  return (value || '')
    .split('\n')
    .map((line) => line.trim())
    .filter(Boolean);
}

function getGuardData(result: unknown) {
  if (!result) return null;
  const value = typeof result === 'string' ? parseJson(result) : result;
  if (value && typeof value === 'object' && 'guard' in value) return value as Record<string, unknown>;
  return null;
}

function parseJson(value: string) {
  try {
    return JSON.parse(value);
  } catch {
    return null;
  }
}

function formatConnectorName(connectorId: string) {
  if (!connectorId) return 'connector';
  return connectorId
    .split(/[_-]/)
    .filter(Boolean)
    .map((part) => part.slice(0, 1).toUpperCase() + part.slice(1))
    .join(' ');
}

function getPaymentName(provider: string) {
  if (provider === 'wix_payments') return 'Base44 Payments';
  if (provider === 'stripe') return 'Stripe';
  return 'payments';
}
