/* ============================================================
   Basket AI - global chat + KOL Studio panel
   ============================================================ */

function aiActionLabel(action) {
  const type = action && action.type;
  if (type === 'open_basket') return 'Open basket';
  if (type === 'prepare_buy') return 'Confirm purchase';
  if (type === 'prepare_sell') return 'Confirm sale';
  if (type === 'apply_kol_draft') return 'Apply draft';
  if (type === 'save_kol_basket') return 'Save basket';
  if (type === 'submit_rebalance') return 'Submit rebalance';
  if (type === 'archive_kol_basket') return 'Archive basket';
  return 'Confirm';
}

function aiActionIcon(action) {
  const type = action && action.type;
  if (type === 'prepare_buy') return 'wallet';
  if (type === 'prepare_sell') return 'swap2';
  if (type === 'open_basket') return 'ext';
  if (type === 'submit_rebalance') return 'swap';
  if (type === 'archive_kol_basket') return 'x';
  return 'check';
}

const AI_BUY_STAGE_COPY = {
  waiting_deposit: {
    title: 'Waiting for payment',
    body: 'Send the exact Solana USDC amount to the order wallet.',
    message: 'Order created. I will watch the payment here and update you when the deposit is detected.',
  },
  deposit_seen: {
    title: 'Payment detected',
    body: 'The deposit arrived. Basket is preparing execution.',
    message: 'Payment detected. The order is now preparing the basket purchase.',
  },
  processing: {
    title: 'Buying basket assets',
    body: 'The worker is converting USDC into basket assets.',
    message: 'Payment is confirmed. Basket assets are being bought now.',
  },
  retrying: {
    title: 'Retrying execution',
    body: 'The worker hit a temporary route/RPC issue and will retry.',
    message: 'The order hit a temporary retry. Your funds stay in the order wallet while execution continues.',
  },
  settled: {
    title: 'Basket added to portfolio',
    body: 'Your position has settled and is visible in Portfolio.',
    message: 'Basket purchase settled. Your position is now in Portfolio.',
  },
  needs_admin_retry: {
    title: 'Admin retry needed',
    body: 'Funds are in the order wallet. Admins need to retry execution.',
    message: 'The order needs an admin retry. Your funds are in the order wallet and the team has the order status.',
  },
  cancelled: {
    title: 'Order cancelled',
    body: 'No active execution is running for this order.',
    message: 'This buy order was cancelled.',
  },
  failed: {
    title: 'Order failed',
    body: 'The order needs support review before it can continue.',
    message: 'The buy order failed and needs support review.',
  },
};

const AI_SELL_STAGE_COPY = {
  queued: {
    title: 'Sale queued',
    body: 'Your sell order is queued for execution.',
    message: 'Sell order created. I will watch the sale here and update you as it progresses.',
  },
  processing: {
    title: 'Selling basket assets',
    body: 'The worker is converting basket assets back to USDC.',
    message: 'Sale is processing. Basket assets are being converted to USDC.',
  },
  retrying: {
    title: 'Retrying sale',
    body: 'The worker hit a temporary route/RPC issue and will retry.',
    message: 'The sale hit a temporary retry. The order remains tracked while execution continues.',
  },
  settled: {
    title: 'Sale settled',
    body: 'Your position and payout status have been updated.',
    message: 'Sale settled. Your Portfolio has been updated.',
  },
  needs_admin_retry: {
    title: 'Admin retry needed',
    body: 'Admins need to retry execution before this sale can finish.',
    message: 'The sale needs an admin retry. The team has the order status.',
  },
  cancelled: {
    title: 'Sale cancelled',
    body: 'No active sale execution is running for this order.',
    message: 'This sell order was cancelled.',
  },
  failed: {
    title: 'Sale failed',
    body: 'The sale needs support review before it can continue.',
    message: 'The sell order failed and needs support review.',
  },
};

function aiShortAddress(value) {
  const text = String(value || '');
  if (text.length <= 16) return text;
  return text.slice(0, 6) + '...' + text.slice(-6);
}

function aiFormatUsd(value) {
  const amount = Number(value || 0);
  if (!Number.isFinite(amount) || amount <= 0) return '$0';
  return '$' + amount.toLocaleString(undefined, { maximumFractionDigits: amount >= 100 ? 0 : 2 });
}

function aiFriendlyError(error) {
  const detail = error && error.detail && typeof error.detail === 'object' ? error.detail : null;
  if (detail && detail.message === 'Buy route is outside safety limits') {
    const token = [detail.symbol, detail.chain].filter(Boolean).join(' on ');
    const reason = detail.reason ? ` Reason: ${detail.reason}` : '';
    return `Buy route is outside safety limits${token ? ` for ${token}` : ''}.${reason}`;
  }
  if (detail && detail.message === 'High price impact requires acceptance') {
    const token = [detail.symbol, detail.chain].filter(Boolean).join(' on ');
    const impact = detail.price_impact_bps ? ` Price impact: ${(Number(detail.price_impact_bps) / 100).toFixed(2)}%.` : '';
    return `High price impact requires acceptance${token ? ` for ${token}` : ''}.${impact}`;
  }
  return (error && error.message) || 'Basket AI is unavailable right now.';
}

function aiStepStatus(step) {
  return String(step && step.status || 'pending').toLowerCase();
}

function aiFindStep(steps, names) {
  const wanted = new Set(names);
  return (steps || []).find(step => wanted.has(step.step_type));
}

function aiBuyOrderStage(order, steps = []) {
  const status = String(order && order.status || '').toLowerCase();
  if (status === 'settled') return 'settled';
  if (status === 'needs_admin_retry') return 'needs_admin_retry';
  if (status === 'cancelled') return 'cancelled';
  if (status === 'failed') return 'failed';
  if (status === 'retrying') return 'retrying';
  if (status === 'running') return 'processing';
  if (status === 'queued') return 'deposit_seen';
  const depositStep = aiFindStep(steps, ['deposit_seen']);
  const buyingStep = aiFindStep(steps, ['solana_swaps_running', 'base_swaps_running', 'solana_swaps_done', 'base_swaps_done']);
  if (buyingStep && aiStepStatus(buyingStep) !== 'pending') return 'processing';
  if (depositStep && aiStepStatus(depositStep) === 'done') return 'deposit_seen';
  return 'waiting_deposit';
}

function aiBuyStageProgress(stage) {
  if (stage === 'settled') return 100;
  if (stage === 'processing' || stage === 'retrying') return 68;
  if (stage === 'deposit_seen') return 42;
  if (stage === 'needs_admin_retry' || stage === 'failed') return 68;
  if (stage === 'cancelled') return 18;
  return 18;
}

function aiSellOrderStage(order, steps = []) {
  const status = String(order && order.status || '').toLowerCase();
  if (status === 'settled') return 'settled';
  if (status === 'needs_admin_retry') return 'needs_admin_retry';
  if (status === 'cancelled') return 'cancelled';
  if (status === 'failed') return 'failed';
  if (status === 'retrying') return 'retrying';
  if (status === 'running') return 'processing';
  const sellingStep = aiFindStep(steps, ['solana_assets_selling', 'base_assets_selling']);
  if (sellingStep && aiStepStatus(sellingStep) !== 'pending') return 'processing';
  return 'queued';
}

function aiSellStageProgress(stage) {
  if (stage === 'settled') return 100;
  if (stage === 'processing' || stage === 'retrying') return 58;
  if (stage === 'needs_admin_retry' || stage === 'failed') return 58;
  if (stage === 'cancelled') return 18;
  return 18;
}

function aiSuggestions(scope) {
  if (scope === 'kol') {
    return [
      'Create a basket with SOL 40%, JUP 25%, JTO 20%, PYTH 15%',
      'Suggest a balanced Solana infra basket',
      'Prepare a rebalance to reduce the largest token',
      'Archive my basket',
    ];
  }
  return [
    'Find @mindbinm basket',
    'I want to buy Rafael basket for $100',
    'Sell half of my basket position',
    'Show me baskets focused on AI agents',
  ];
}

function useBasketAi({ scope = 'user', surface = '', basketId = null, activeBasket = null, auth, onConnectWallet, onClientAction }) {
  const [conversationId, setConversationId] = useState(null);
  const [messages, setMessages] = useState([
    {
      id: 'hello',
      role: 'assistant',
      content: scope === 'kol'
        ? 'Tell me what basket or rebalance you want. I can prepare a draft, then you confirm before anything changes.'
        : 'Ask me to find baskets, explain creator profiles, or prepare a purchase. I will ask for confirmation before any action.',
    },
  ]);
  const [input, setInput] = useState('');
  const [actions, setActions] = useState([]);
  const [trackedOrders, setTrackedOrders] = useState([]);
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState('');
  const orderPollersRef = useRef({});
  const orderStagesRef = useRef({});

  const appendMessage = (role, content, extra = {}) => {
    setMessages(prev => [...prev, { id: extra.id || `${Date.now()}-${Math.random()}`, role, content, ...extra }]);
  };

  const notifySite = (message) => {
    try {
      window.dispatchEvent(new CustomEvent('basket:site-notice', { detail: { message } }));
    } catch (_) {}
  };

  const upsertTrackedOrder = (row) => {
    setTrackedOrders(prev => {
      const next = prev.filter(item => item.orderId !== row.orderId);
      return [{ ...row, updatedAt: Date.now() }, ...next].slice(0, 4);
    });
  };

  const stopOrderMonitor = (orderId) => {
    const timer = orderPollersRef.current[orderId];
    if (timer) clearInterval(timer);
    delete orderPollersRef.current[orderId];
  };

  useEffect(() => () => {
    Object.values(orderPollersRef.current || {}).forEach(timer => clearInterval(timer));
  }, []);

  const pollBuyOrder = async (seed, options = {}) => {
    const orderId = seed && (seed.order_id || seed.orderId);
    if (!orderId || !window.BasketAPI || !window.BasketAPI.order || !window.BasketAPI.orderSteps) return;
    try {
      const [order, steps] = await Promise.all([
        window.BasketAPI.order(orderId),
        window.BasketAPI.orderSteps(orderId).catch(() => []),
      ]);
      const stage = aiBuyOrderStage(order, steps || []);
      const copy = AI_BUY_STAGE_COPY[stage] || AI_BUY_STAGE_COPY.waiting_deposit;
      const amount = order && order.gross_amount_usdc ? order.gross_amount_usdc : seed.gross_amount_usdc;
      const deposit = seed.deposit_address || (order && order.metadata && order.metadata.deposit_address) || '';
      const basketName = seed.basket_name || seed.basketName || seed.name || 'basket';
      upsertTrackedOrder({
        orderId,
        basketName,
        amount,
        depositAddress: deposit,
        status: order && order.status ? order.status : seed.status,
        stage,
        title: copy.title,
        body: copy.body,
        progress: aiBuyStageProgress(stage),
      });
      const previous = orderStagesRef.current[orderId];
      if (!previous) {
        orderStagesRef.current[orderId] = stage;
        if (options.initialMessage) appendMessage('assistant', options.initialMessage);
        return;
      }
      if (previous !== stage) {
        orderStagesRef.current[orderId] = stage;
        if (previous === 'waiting_deposit' && ['processing', 'retrying', 'settled'].includes(stage)) {
          appendMessage('assistant', AI_BUY_STAGE_COPY.deposit_seen.message);
          notifySite(AI_BUY_STAGE_COPY.deposit_seen.title + ': ' + basketName);
        }
        appendMessage('assistant', copy.message);
        if (['deposit_seen', 'settled', 'needs_admin_retry', 'failed', 'cancelled'].includes(stage)) notifySite(copy.title + ': ' + basketName);
        if (stage === 'settled') {
          window.dispatchEvent(new CustomEvent('basket:data-reload'));
          stopOrderMonitor(orderId);
        }
        if (['needs_admin_retry', 'failed', 'cancelled'].includes(stage)) stopOrderMonitor(orderId);
      }
    } catch (e) {
      upsertTrackedOrder({
        orderId,
        basketName: seed.basket_name || seed.basketName || 'basket',
        amount: seed.gross_amount_usdc,
        depositAddress: seed.deposit_address || '',
        status: 'checking',
        stage: 'waiting_deposit',
        title: 'Checking payment status',
        body: e.message || 'Could not read order status yet. I will try again.',
        progress: 18,
      });
    }
  };

  const startBuyOrderMonitor = (result, action) => {
    const orderId = result && result.order_id;
    if (!orderId) return;
    stopOrderMonitor(orderId);
    const payload = action && action.payload ? action.payload : {};
    const basket = payload.basket || {};
    const seed = {
      ...result,
      order_id: orderId,
      basket_name: basket.name || result.basket_name || 'basket',
    };
    const amountLabel = aiFormatUsd(result.gross_amount_usdc || payload.amount_usdc);
    const deposit = result.deposit_address || '';
    const initialMessage = [
      `Order created for ${amountLabel}.`,
      deposit ? `Send exactly ${amountLabel} Solana USDC to ${deposit}.` : 'Send the exact Solana USDC amount to the generated order wallet.',
      'I will keep watching this payment here and will tell you when the deposit is detected and when the basket reaches your Portfolio.',
    ].join('\n');
    orderStagesRef.current[orderId] = '';
    pollBuyOrder(seed, { initialMessage });
    orderPollersRef.current[orderId] = setInterval(() => pollBuyOrder(seed), 4000);
    try {
      window.dispatchEvent(new CustomEvent('basket:track-order', {
        detail: {
          kind: 'buy',
          order: result,
          basketName: seed.basket_name,
          amount: result.gross_amount_usdc || payload.amount_usdc,
          depositAddress: result.deposit_address || '',
        },
      }));
    } catch (_) {}
    notifySite('AI buy order created. Waiting for payment.');
  };

  const pollSellOrder = async (seed, options = {}) => {
    const orderId = seed && (seed.order_id || seed.orderId);
    if (!orderId || !window.BasketAPI || !window.BasketAPI.order || !window.BasketAPI.orderSteps) return;
    try {
      const [order, steps] = await Promise.all([
        window.BasketAPI.order(orderId),
        window.BasketAPI.orderSteps(orderId).catch(() => []),
      ]);
      const stage = aiSellOrderStage(order, steps || []);
      const copy = AI_SELL_STAGE_COPY[stage] || AI_SELL_STAGE_COPY.queued;
      const basketName = seed.basket_name || seed.basketName || seed.name || 'basket';
      upsertTrackedOrder({
        orderId,
        kind: 'sell',
        basketName,
        amount: '',
        percentLabel: seed.percentLabel || 'Sale',
        status: order && order.status ? order.status : seed.status,
        stage,
        title: copy.title,
        body: copy.body,
        progress: aiSellStageProgress(stage),
      });
      const previous = orderStagesRef.current[orderId];
      if (!previous) {
        orderStagesRef.current[orderId] = stage;
        if (options.initialMessage) appendMessage('assistant', options.initialMessage);
        return;
      }
      if (previous !== stage) {
        orderStagesRef.current[orderId] = stage;
        appendMessage('assistant', copy.message);
        if (['settled', 'needs_admin_retry', 'failed', 'cancelled'].includes(stage)) notifySite(copy.title + ': ' + basketName);
        if (stage === 'settled') {
          window.dispatchEvent(new CustomEvent('basket:data-reload'));
          stopOrderMonitor(orderId);
        }
        if (['needs_admin_retry', 'failed', 'cancelled'].includes(stage)) stopOrderMonitor(orderId);
      }
    } catch (e) {
      upsertTrackedOrder({
        orderId,
        kind: 'sell',
        basketName: seed.basket_name || seed.basketName || 'basket',
        amount: '',
        percentLabel: seed.percentLabel || 'Sale',
        status: 'checking',
        stage: 'queued',
        title: 'Checking sale status',
        body: e.message || 'Could not read sale status yet. I will try again.',
        progress: 18,
      });
    }
  };

  const startSellOrderMonitor = (result, action) => {
    const orderId = result && result.order_id;
    if (!orderId) return;
    stopOrderMonitor(orderId);
    const payload = action && action.payload ? action.payload : {};
    const basket = payload.basket || {};
    const fraction = Number(payload.sell_fraction_bps || result.sell_fraction_bps || 0);
    const percentLabel = fraction === 10000 ? 'Full sale' : fraction ? (fraction / 100).toLocaleString(undefined, { maximumFractionDigits: 2 }) + '% sale' : 'Sale';
    const seed = {
      ...result,
      order_id: orderId,
      basket_name: basket.name || result.basket_name || 'basket',
      percentLabel,
    };
    const initialMessage = [
      `Sell order created for ${percentLabel.toLowerCase()} of ${seed.basket_name}.`,
      'I will keep watching the sale here and will tell you when it settles or needs review.',
    ].join('\n');
    orderStagesRef.current[orderId] = '';
    pollSellOrder(seed, { initialMessage });
    orderPollersRef.current[orderId] = setInterval(() => pollSellOrder(seed), 4000);
    try {
      window.dispatchEvent(new CustomEvent('basket:track-order', {
        detail: {
          kind: 'sell',
          order: result,
          basketName: seed.basket_name,
        },
      }));
    } catch (_) {}
    notifySite('AI sell order created. Tracking sale progress.');
  };

  const handleClientAction = (clientAction, response) => {
    if (!clientAction) return;
    if (clientAction.type === 'navigate' && clientAction.basket_id) {
      window.dispatchEvent(new CustomEvent('basket:navigate', { detail: { view: 'explore', basketId: clientAction.basket_id } }));
    }
    if (clientAction.type === 'apply_kol_draft') {
      window.dispatchEvent(new CustomEvent('basket:ai-apply-kol-draft', { detail: clientAction.draft || {} }));
    }
    if (clientAction.type === 'kol_basket_saved' || clientAction.type === 'kol_basket_archived' || clientAction.type === 'rebalance_submitted' || clientAction.type === 'order_created' || clientAction.type === 'sell_order_created') {
      window.dispatchEvent(new CustomEvent('basket:data-reload'));
    }
    if (clientAction.type === 'kol_basket_archived') {
      notifySite('Basket archived.');
    }
    if (onClientAction) onClientAction(clientAction, response);
  };

  const send = async (textOverride) => {
    const text = String(textOverride || input || '').trim();
    if (!text || busy || !window.BasketAPI || !window.BasketAPI.aiChat) return;
    if (!(auth && auth.connected)) {
      if (onConnectWallet) onConnectWallet();
      setError('Connect your wallet to use Basket AI.');
      appendMessage('assistant', 'Connect your wallet to use Basket AI. I will keep actions behind confirmation after you sign in.');
      return;
    }
    setInput('');
    setError('');
    appendMessage('user', text);
    setBusy(true);
    try {
      const response = await window.BasketAPI.aiChat({
        message: text,
        conversation_id: conversationId,
        scope,
        surface,
        basket_id: basketId || undefined,
        context: activeBasket ? { active_basket: activeBasket } : undefined,
      });
      if (response && response.conversation && response.conversation.id) setConversationId(response.conversation.id);
      if (response && response.message) appendMessage('assistant', response.message.content, { id: response.message.id });
      if (response && Array.isArray(response.actions) && response.actions.length) {
        setActions(prev => [...response.actions, ...prev].slice(0, 8));
      }
    } catch (e) {
      const msg = aiFriendlyError(e);
      setError(msg);
      appendMessage('assistant', msg);
    } finally {
      setBusy(false);
    }
  };

  const confirm = async (action) => {
    if (!action || !window.BasketAPI || !window.BasketAPI.confirmAiAction) return;
    if ((action.type === 'prepare_buy' || action.type === 'prepare_sell') && !(auth && auth.connected)) {
      if (onConnectWallet) onConnectWallet();
      appendMessage('assistant', 'Connect your wallet first, then confirm this action again.');
      return;
    }
    setBusy(true);
    setError('');
    try {
      const response = await window.BasketAPI.confirmAiAction(action.id);
      setActions(prev => prev.map(item => item.id === action.id ? response.action : item));
      handleClientAction(response.client_action, response);
      const result = response && response.action && response.action.result ? response.action.result : {};
      if (action.type === 'prepare_buy' && result.deposit_address) {
        startBuyOrderMonitor(result, action);
      } else if (action.type === 'prepare_sell' && result.order_id) {
        startSellOrderMonitor(result, action);
      } else {
        appendMessage('assistant', response.message || 'Confirmed.');
      }
    } catch (e) {
      const msg = aiFriendlyError(e) || 'Could not confirm this action.';
      setError(msg);
      appendMessage('assistant', msg);
    } finally {
      setBusy(false);
    }
  };

  const cancel = async (action) => {
    if (!action || !window.BasketAPI || !window.BasketAPI.cancelAiAction) return;
    try {
      const response = await window.BasketAPI.cancelAiAction(action.id);
      setActions(prev => prev.map(item => item.id === action.id ? response.action : item));
    } catch (_) {}
  };

  return { messages, input, setInput, actions, trackedOrders, busy, error, send, confirm, cancel };
}

function BasketAiInlineText({ text }) {
  const parts = String(text || '').split(/(\*\*[^*]+\*\*)/g);
  return (
    <>
      {parts.map((part, index) => {
        if (part.startsWith('**') && part.endsWith('**') && part.length > 4) {
          return <strong key={index} style={{ fontWeight: 900 }}>{part.slice(2, -2)}</strong>;
        }
        return <React.Fragment key={index}>{part}</React.Fragment>;
      })}
    </>
  );
}

function BasketAiFormattedText({ content }) {
  const lines = String(content || '').split(/\r?\n/);
  return (
    <div style={{ display: 'grid', gap: lines.length > 1 ? 6 : 0 }}>
      {lines.map((line, index) => {
        if (!line.trim()) return <span key={index} style={{ height: 2 }} />;
        const bullet = line.match(/^\s*[-•]\s+(.+)$/);
        if (bullet) {
          return (
            <div key={index} style={{ display: 'grid', gridTemplateColumns: '12px minmax(0, 1fr)', gap: 6, alignItems: 'start' }}>
              <span style={{ color: 'var(--text-3)' }}>•</span>
              <span style={{ minWidth: 0 }}><BasketAiInlineText text={bullet[1]} /></span>
            </div>
          );
        }
        return <div key={index}><BasketAiInlineText text={line} /></div>;
      })}
    </div>
  );
}

function BasketAiMessages({ state }) {
  return (
    <div style={{ display: 'grid', gap: 10, minHeight: 0 }}>
      {state.messages.map(msg => (
        <div key={msg.id} style={{
          justifySelf: msg.role === 'user' ? 'end' : 'start',
          maxWidth: '88%',
          border: '1px solid var(--line)',
          background: msg.role === 'user' ? 'var(--accent-wash)' : 'var(--surface)',
          color: 'var(--text)',
          borderRadius: msg.role === 'user' ? '14px 14px 4px 14px' : '14px 14px 14px 4px',
          padding: '10px 12px',
          fontSize: 13,
          lineHeight: 1.45,
          overflowWrap: 'anywhere',
        }}><BasketAiFormattedText content={msg.content} /></div>
      ))}
      {state.busy && (
        <div style={{ justifySelf: 'start', border: '1px solid var(--line)', borderRadius: 14, padding: '9px 11px', color: 'var(--text-3)', fontSize: 12 }}>
          Thinking...
        </div>
      )}
    </div>
  );
}

function BasketAiMessageScroller({ state, style = {} }) {
  const scrollRef = useRef(null);

  useEffect(() => {
    const frame = requestAnimationFrame(() => {
      const el = scrollRef.current;
      if (!el) return;
      el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' });
    });
    return () => cancelAnimationFrame(frame);
  }, [state.messages.length, state.busy, state.actions.length]);

  return (
    <div ref={scrollRef} style={{ overflowY: 'auto', paddingRight: 2, ...style }}>
      <BasketAiMessages state={state} />
    </div>
  );
}

function BasketAiActionList({ actions, onConfirm, onCancel }) {
  const pending = (actions || []).filter(action => action.status === 'pending');
  if (!pending.length) return null;
  return (
    <div style={{ display: 'grid', gap: 8 }}>
      {pending.map(action => (
        <div key={action.id} style={{ border: '1px solid var(--line-2)', background: 'var(--bg-2)', borderRadius: 14, padding: 10, display: 'grid', gap: 8 }}>
          <div style={{ display: 'flex', alignItems: 'flex-start', gap: 9 }}>
            <span style={{ width: 28, height: 28, borderRadius: 9, background: 'var(--accent-wash)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
              <Icon name={aiActionIcon(action)} size={14} color="var(--accent-ink)" />
            </span>
            <div style={{ minWidth: 0, flex: 1 }}>
              <div style={{ fontSize: 12.5, fontWeight: 850 }}>{action.title}</div>
              {action.body && <div style={{ marginTop: 2, fontSize: 11.5, color: 'var(--text-3)', lineHeight: 1.35 }}>{action.body}</div>}
            </div>
          </div>
          <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', justifyContent: 'flex-end' }}>
            <Btn size="sm" variant="ghost" icon="x" onClick={() => onCancel(action)}>Cancel</Btn>
            <Btn size="sm" variant="soft" icon={aiActionIcon(action)} onClick={() => onConfirm(action)}>{aiActionLabel(action)}</Btn>
          </div>
        </div>
      ))}
    </div>
  );
}

function BasketAiOrderProgressList({ orders = [] }) {
  if (!orders.length) return null;
  return (
    <div style={{ display: 'grid', gap: 8 }}>
      {orders.map(order => {
        const done = order.stage === 'settled';
        const warning = ['retrying', 'needs_admin_retry', 'failed'].includes(order.stage);
        const isSell = order.kind === 'sell';
        const detailLabel = isSell ? (order.percentLabel || 'Sale order') : aiFormatUsd(order.amount);
        return (
          <div key={order.orderId} style={{ border: '1px solid var(--line-2)', background: done ? 'var(--accent-wash)' : 'var(--bg-2)', borderRadius: 14, padding: 11, display: 'grid', gap: 9 }}>
            <div style={{ display: 'flex', alignItems: 'flex-start', gap: 10 }}>
              <span style={{ width: 30, height: 30, borderRadius: 10, background: done ? 'var(--accent)' : warning ? 'rgba(245,166,35,0.14)' : 'var(--blue-wash)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                <Icon name={done ? 'check' : warning ? 'info' : isSell ? 'swap2' : 'wallet'} size={14} color={done ? 'var(--on-accent)' : warning ? 'var(--warning)' : 'var(--blue)'} />
              </span>
              <div style={{ minWidth: 0, flex: 1 }}>
                <div style={{ fontSize: 12.5, fontWeight: 900 }}>{order.title}</div>
                <div style={{ marginTop: 2, fontSize: 11.5, color: 'var(--text-3)', lineHeight: 1.35 }}>
                  {order.basketName} · {detailLabel} · <span className="tnum">{order.status || order.stage}</span>
                </div>
              </div>
            </div>
            <div style={{ height: 6, borderRadius: 99, background: 'var(--surface-2)', overflow: 'hidden' }}>
              <div style={{ width: `${Math.max(5, Math.min(100, order.progress || 0))}%`, height: '100%', background: warning ? 'var(--warning)' : 'linear-gradient(90deg, var(--blue), var(--accent))', transition: 'width .4s ease' }} />
            </div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0, fontSize: 11.5, color: 'var(--text-2)', lineHeight: 1.35 }}>
              <span style={{ flex: 1 }}>{order.body}</span>
              {order.depositAddress && (
                <span className="tnum" title={order.depositAddress} style={{ color: 'var(--text-3)', whiteSpace: 'nowrap' }}>
                  {aiShortAddress(order.depositAddress)}
                </span>
              )}
            </div>
          </div>
        );
      })}
    </div>
  );
}

function BasketAiComposer({ state, compact = false }) {
  return (
    <div style={{ display: 'grid', gap: 8 }}>
      <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0,1fr) auto', gap: 8, alignItems: 'end' }}>
        <textarea
          value={state.input}
          onChange={e => state.setInput(e.target.value)}
          onKeyDown={e => {
            if (e.key === 'Enter' && !e.shiftKey && !(e.nativeEvent && e.nativeEvent.isComposing)) {
              e.preventDefault();
              state.send();
            }
          }}
          rows={compact ? 2 : 3}
          placeholder="Ask Basket AI..."
          style={{ width: '100%', minWidth: 0, resize: 'vertical', border: '1px solid var(--line-2)', background: 'var(--bg-2)', color: 'var(--text)', borderRadius: 12, padding: '10px 12px', fontFamily: 'var(--font)', fontSize: 13, lineHeight: 1.45, outline: 'none' }}
        />
        <Btn size="sm" variant="soft" icon="spark" disabled={state.busy || !state.input.trim()} onClick={() => state.send()}>
          Send
        </Btn>
      </div>
      {state.error && <div style={{ fontSize: 11.5, color: 'var(--warning)' }}>{state.error}</div>}
    </div>
  );
}

function BasketAiSuggestionRow({ scope, onPick }) {
  return (
    <div style={{ display: 'flex', gap: 7, flexWrap: 'wrap' }}>
      {aiSuggestions(scope).map(item => (
        <button key={item} onClick={() => onPick(item)} style={{
          border: '1px solid var(--line)',
          background: 'var(--surface-2)',
          color: 'var(--text-2)',
          borderRadius: 999,
          padding: '6px 9px',
          fontFamily: 'var(--font)',
          fontSize: 11.5,
          fontWeight: 750,
          cursor: 'pointer',
          maxWidth: '100%',
          overflow: 'hidden',
          textOverflow: 'ellipsis',
          whiteSpace: 'nowrap',
        }}>{item}</button>
      ))}
    </div>
  );
}

function BasketAiAuthGate({ scope, auth, onConnectWallet }) {
  if (auth && auth.connected) return null;
  return (
    <div style={{ border: '1px solid var(--line-2)', background: 'var(--bg-2)', borderRadius: 14, padding: 12, display: 'grid', gap: 8 }}>
      <div style={{ fontWeight: 850, fontSize: 13 }}>Wallet required</div>
      <div style={{ color: 'var(--text-2)', fontSize: 12.5, lineHeight: 1.45 }}>
        Basket AI is available only after wallet authentication. {scope === 'kol' ? 'KOL tools also require approved KOL access.' : 'Actions always require confirmation.'}
      </div>
      <div>
        <Btn size="sm" variant="soft" icon="wallet" onClick={onConnectWallet}>Connect wallet</Btn>
      </div>
    </div>
  );
}

function BasketAiStudioPanel({ auth, onConnectWallet, basketId = null, activeBasket = null }) {
  const state = useBasketAi({ scope: 'kol', surface: 'kol_studio', basketId, activeBasket, auth, onConnectWallet });
  return (
    <div className="basket-ai-studio" style={{ borderTop: '1px solid var(--line)', borderBottom: '1px solid var(--line)', padding: '14px 0', margin: '12px 0 14px', display: 'grid', gap: 12 }}>
      <div style={{ display: 'flex', alignItems: 'flex-start', gap: 12, minWidth: 0 }}>
        <span style={{ width: 34, height: 34, borderRadius: 12, background: 'var(--blue-wash)', color: 'var(--blue)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
          <Icon name="spark" size={16} color="currentColor" />
        </span>
        <div style={{ minWidth: 0, flex: 1 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
            <strong style={{ fontSize: 14 }}>Basket AI</strong>
            <span className="eyebrow" style={{ color: 'var(--blue)', letterSpacing: '0.12em' }}>KOL copilot</span>
          </div>
          <div style={{ color: 'var(--text-2)', fontSize: 12.5, lineHeight: 1.45, marginTop: 3 }}>
            Describe a basket or rebalance. Basket AI prepares a draft, then you confirm before anything changes.
          </div>
        </div>
      </div>
      <BasketAiAuthGate scope="kol" auth={auth} onConnectWallet={onConnectWallet} />
      <BasketAiSuggestionRow scope="kol" onPick={state.send} />
      <BasketAiMessageScroller state={state} style={{ maxHeight: 260 }} />
      <BasketAiOrderProgressList orders={state.trackedOrders} />
      <BasketAiActionList actions={state.actions} onConfirm={state.confirm} onCancel={state.cancel} />
      <BasketAiComposer state={state} compact />
    </div>
  );
}

function BasketAiWidget({ auth, nav, onConnectWallet }) {
  const isMobile = useIsMobile();
  const [open, setOpen] = useState(false);
  const scope = nav && nav.view === 'kol' ? 'kol' : 'user';
  const state = useBasketAi({
    scope,
    surface: nav && nav.view ? nav.view : 'global',
    basketId: nav && nav.basketId ? nav.basketId : null,
    auth,
    onConnectWallet,
  });
  return (
    <>
      <button onClick={() => setOpen(true)} aria-label="Open Basket AI" title="Basket AI" style={{
        position: 'fixed',
        right: isMobile ? 16 : 24,
        bottom: isMobile ? 88 : 24,
        zIndex: 70,
        width: isMobile ? 52 : 56,
        height: isMobile ? 52 : 56,
        borderRadius: 18,
        border: '1px solid var(--line-2)',
        background: 'var(--accent)',
        color: 'var(--on-accent)',
        boxShadow: 'var(--shadow-lg)',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        cursor: 'pointer',
      }}>
        <Icon name="spark" size={22} color="currentColor" />
      </button>
      {open && (
        <Portal>
          <div style={{ position: 'fixed', inset: 0, zIndex: 120, background: isMobile ? 'rgba(0,0,0,0.24)' : 'transparent', pointerEvents: 'none' }}>
            <div style={{
              position: 'absolute',
              right: isMobile ? 0 : 24,
              bottom: isMobile ? 0 : 92,
              width: isMobile ? '100%' : 420,
              height: isMobile ? '82vh' : 600,
              maxHeight: isMobile ? '82vh' : 'calc(100vh - 116px)',
              background: 'var(--surface)',
              border: '1px solid var(--line-2)',
              borderRadius: isMobile ? '24px 24px 0 0' : 20,
              boxShadow: 'var(--shadow-lg)',
              padding: 14,
              display: 'grid',
              gridTemplateRows: 'auto auto minmax(0,1fr) auto auto',
              gap: 10,
              pointerEvents: 'auto',
            }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <span style={{ width: 36, height: 36, borderRadius: 12, background: 'var(--accent-wash)', color: 'var(--accent-ink)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                  <Icon name="spark" size={17} color="currentColor" />
                </span>
                <div style={{ minWidth: 0, flex: 1 }}>
                  <div style={{ fontSize: 15, fontWeight: 900 }}>Basket AI</div>
                  <div style={{ fontSize: 11.5, color: 'var(--text-3)' }}>{scope === 'kol' ? 'KOL Studio mode' : 'Find, learn, prepare buys'}</div>
                </div>
                <button onClick={() => setOpen(false)} style={{ width: 34, height: 34, borderRadius: 10, border: '1px solid var(--line)', background: 'var(--surface-2)', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  <Icon name="x" size={15} color="var(--text-2)" />
                </button>
              </div>
              <BasketAiAuthGate scope={scope} auth={auth} onConnectWallet={onConnectWallet} />
              <BasketAiSuggestionRow scope={scope} onPick={state.send} />
              <BasketAiMessageScroller state={state} />
              <BasketAiOrderProgressList orders={state.trackedOrders} />
              <BasketAiActionList actions={state.actions} onConfirm={state.confirm} onCancel={state.cancel} />
              <BasketAiComposer state={state} />
            </div>
          </div>
        </Portal>
      )}
    </>
  );
}

Object.assign(window, { BasketAiWidget, BasketAiStudioPanel });
