import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select';
import OpenOrders from '@/components/user/default/open-orders';
import { createRequest } from '@/data/request/create-request';
import { useCurrency } from '@/hooks/use-currency';
import { useApiSWR } from '@/hooks/useSWR';
import { cn } from '@/lib/utils';
import {
  TMarket,
  TMarketOutcome,
  TOrderBook,
  TOrderBookLevel,
} from '@/types/market.types';
import { getImageUrl } from '@/utils/getImageUrl';
import { useEchoPublic } from '@laravel/echo-react';
import { TrendUpIcon } from '@phosphor-icons/react';
import dayjs from 'dayjs';
import { useEffect, useMemo, useState, useTransition } from 'react';
import { useTranslation } from 'react-i18next';
import { toast } from 'sonner';

const DEFAULT_COLORS = ['#22c55e', '#ef4444', '#3b82f6', '#a855f7'];
const round6 = (n: number) => Math.round(n * 1e6) / 1e6;

type Side = 'buy' | 'sell';
type OrderType = 'market' | 'limit';

function walkBuy(asks: TOrderBookLevel[], budget: number) {
  let rem = budget;
  let shares = 0;
  let cost = 0;
  for (const lvl of asks) {
    const levelCost = lvl.price * lvl.shares;
    if (levelCost <= rem) {
      rem -= levelCost;
      shares += lvl.shares;
      cost += levelCost;
    } else if (rem > 0) {
      shares += rem / lvl.price;
      cost += rem;
      rem = 0;
      break;
    }
  }
  return {
    shares,
    cost,
    avg: shares > 0 ? cost / shares : 0,
    filled: rem <= 1e-9,
  };
}

function walkSell(bids: TOrderBookLevel[], shares: number) {
  let rem = shares;
  let proceeds = 0;
  for (const lvl of bids) {
    const take = Math.min(rem, lvl.shares);
    proceeds += take * lvl.price;
    rem -= take;
    if (rem <= 1e-9) break;
  }
  return { proceeds, sold: shares - rem, filled: rem <= 1e-9 };
}

// Quick-add chips: dollars for a market buy, share counts otherwise.
const STEPS = [1, 5, 10, 100];
// Limit share steps allow decrementing too.
const LIMIT_STEPS = [-100, -10, 10, 100, 200];

// GTD expiry presets. 'never' maps to GTC; the rest resolve to a timestamp.
const EXPIRY_OPTIONS = [
  { value: 'never', label: 'Never' },
  { value: 'eod', label: 'End of day' },
  { value: '1d', label: '1 day' },
  { value: '7d', label: '7 days' },
];

function resolveExpiry(choice: string): { tif: string; expires_at?: string } {
  if (choice === 'never') return { tif: 'gtc' };
  const base = dayjs();
  const at =
    choice === 'eod'
      ? base.endOf('day')
      : choice === '1d'
        ? base.add(1, 'day')
        : base.add(7, 'day');
  return { tif: 'gtd', expires_at: at.toISOString() };
}

/**
 * CLOB order-entry panel, styled to match the AMM `TradeForm`: outcome grid,
 * market/limit toggle, big amount/shares input with quick-add chips, a book-
 * walked estimate footer, and the user's resting orders. Live book updates
 * arrive over the public `market.{slug}` channel; the depth ladder itself lives
 * below the chart in `ClobBook`.
 */
export default function ClobTradePanel({
  market,
  slug,
  selected,
  onSelectOutcome,
  isLoggedIn,
  onTraded,
  prefill,
}: {
  market: TMarket;
  slug: string;
  selected: TMarketOutcome;
  onSelectOutcome: (o: TMarketOutcome) => void;
  isLoggedIn: boolean;
  onTraded?: () => void;
  prefill?: { price: string; n: number };
}) {
  const { t } = useTranslation();
  const { format } = useCurrency();

  const [side, setSide] = useState<Side>('buy');
  const [orderType, setOrderType] = useState<OrderType>('market');
  const [amount, setAmount] = useState(''); // $ for a market buy
  const [shares, setShares] = useState(''); // shares for a sell / limit qty
  const [price, setPrice] = useState(''); // limit price
  const [expiry, setExpiry] = useState('never'); // GTD expiry preset
  const [refreshKey, setRefreshKey] = useState(0);
  const [isPending, startTransition] = useTransition();

  const { data: book, mutate: mutateBook } = useApiSWR<TOrderBook>(
    `/markets/${slug}/book`,
  );

  useEchoPublic(
    `market.${slug}`,
    'MarketBookUpdated',
    (e: { book: TOrderBook }) => {
      if (e?.book) mutateBook(e.book, false);
      setRefreshKey((k) => k + 1);
    },
  );

  // A book-level click below the chart prefills the limit price here.
  useEffect(() => {
    if (prefill?.price) {
      setOrderType('limit');
      setPrice(prefill.price);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [prefill?.n]);

  const symbol = useMemo(
    () => format(0).replace(/[\d\s.,]/g, '') || '$',
    [format],
  );

  const outcomeColor = (o: TMarketOutcome, i: number) =>
    o.color ?? DEFAULT_COLORS[i % DEFAULT_COLORS.length];

  const selectedIndex = market.outcomes.findIndex((o) => o.id === selected.id);
  const selColor = outcomeColor(selected, Math.max(selectedIndex, 0));

  // The API returns a single YES-denominated book; mirror it for the complement.
  const { asks, bids } = useMemo(() => {
    if (!book)
      return { asks: [] as TOrderBookLevel[], bids: [] as TOrderBookLevel[] };
    const isRef = book.reference_outcome_id === selected.id;
    if (isRef) return { asks: book.asks, bids: book.bids };
    return {
      asks: book.bids
        .map((b) => ({ price: round6(1 - b.price), shares: b.shares }))
        .sort((a, b) => a.price - b.price),
      bids: book.asks
        .map((a) => ({ price: round6(1 - a.price), shares: a.shares }))
        .sort((a, b) => b.price - a.price),
    };
  }, [book, selected.id]);

  const numAmount = Number(amount) || 0;
  const numShares = Number(shares) || 0;
  const numPrice = Number(price) || 0;

  const isBuyMarket = orderType === 'market' && side === 'buy';
  // Buy market orders take a $ budget; everything else takes a share count.
  const usingAmount = isBuyMarket;
  const inputVal = usingAmount ? amount : shares;
  const setInput = (v: string) => (usingAmount ? setAmount(v) : setShares(v));
  const hasValue = usingAmount ? numAmount > 0 : numShares > 0;

  const estimate = useMemo(() => {
    if (orderType !== 'market') return null;
    return side === 'buy'
      ? walkBuy(asks, numAmount)
      : walkSell(bids, numShares);
  }, [orderType, side, asks, bids, numAmount, numShares]);

  // Primary footer figure: shares to win (buy) or proceeds (sell).
  const outShares =
    orderType === 'market'
      ? ((estimate as { shares: number })?.shares ?? 0)
      : numShares;
  const outProceeds =
    orderType === 'market'
      ? ((estimate as { proceeds: number })?.proceeds ?? 0)
      : numPrice * numShares;
  const primaryValue = side === 'buy' ? outShares : outProceeds;
  const avgPrice =
    orderType === 'limit'
      ? numPrice
      : side === 'buy'
        ? ((estimate as { avg: number })?.avg ?? 0)
        : (selected.probability ?? 0.5);
  const notEnoughLiquidity =
    orderType === 'market' && estimate ? !estimate.filled : false;

  const handleStep = (v: number) =>
    setInput(String((Number(inputVal) || 0) + v));

  // Limit-mode helpers: price stepper (by tick), share stepper, and displays.
  const tick = Number(market.tick_size) || 0.01;
  const clampTick = (p: number) => {
    const stepped = Math.round(p / tick) * tick;
    return Math.min(1 - tick, Math.max(tick, +stepped.toFixed(6)));
  };
  const stepPrice = (dir: number) =>
    setPrice(
      String(
        clampTick((numPrice || (selected.probability ?? 0.5)) + dir * tick),
      ),
    );
  const stepShares = (v: number) =>
    setShares(String(Math.max(0, (Number(shares) || 0) + v)));
  const centsLabel = numPrice ? `${+(numPrice * 100).toFixed(1)}¢` : '—';
  const limitTotal = numPrice * numShares;

  const submit = () => {
    const outcome_id = selected.id;
    let payload: Record<string, unknown> | null = null;

    if (orderType === 'market' && side === 'buy') {
      if (numAmount <= 0) return;
      payload = { outcome_id, side, type: 'market', budget: numAmount };
    } else if (orderType === 'market' && side === 'sell') {
      if (numShares <= 0) return;
      payload = { outcome_id, side, type: 'market', qty: numShares };
    } else {
      if (numPrice <= 0 || numPrice >= 1 || numShares <= 0) return;
      payload = {
        outcome_id,
        side,
        type: 'limit',
        price: numPrice,
        qty: numShares,
        ...resolveExpiry(expiry),
      };
    }

    startTransition(async () => {
      const result = await createRequest({
        url: `/api/user/markets/${slug}/orders`,
        formData: payload,
      });
      if (result) {
        toast.success(result.message ?? t('Order placed.'));
        setAmount('');
        setShares('');
        setPrice('');
        mutateBook();
        setRefreshKey((k) => k + 1);
        onTraded?.();
      }
    });
  };

  return (
    <Card className="gap-0 overflow-hidden rounded-2xl p-0 dark:border-white/8">
      {/* Market header */}
      <div className="flex items-center gap-3 p-4">
        <div className="size-10 shrink-0 overflow-hidden rounded-md bg-secondary">
          {market.image ? (
            <img
              src={getImageUrl(market.image)}
              alt={market.title}
              className="size-full object-cover"
            />
          ) : (
            <div className="flex size-full items-center justify-center text-black/30 dark:text-white/30">
              <TrendUpIcon weight="bold" className="size-5" />
            </div>
          )}
        </div>
        <div className="min-w-0">
          <p className="truncate text-xs text-black/50 dark:text-white/50">
            {market.title}
          </p>
          <p className="text-sm font-semibold">
            {selected.title}
            <span style={{ color: selColor }}>
              {' · '}
              {side === 'buy' ? t('Buy') : t('Sell')}
            </span>
          </p>
        </div>
      </div>

      {/* Side tabs + order-type dropdown */}
      <div className="flex items-center justify-between gap-4 border-b border-black/8 px-4 dark:border-white/8">
        <div className="flex items-center gap-4">
          {(['buy', 'sell'] as Side[]).map((s) => (
            <button
              key={s}
              type="button"
              onClick={() => setSide(s)}
              className={cn(
                'cursor-pointer border-b-2 pt-1 pb-2.5 text-sm font-semibold capitalize transition-colors',
                side === s
                  ? 'border-foreground text-foreground'
                  : 'border-transparent text-black/40 hover:text-black/60 dark:text-white/40 dark:hover:text-white/60',
              )}
            >
              {s === 'buy' ? t('Buy') : t('Sell')}
            </button>
          ))}
        </div>

        <Select
          value={orderType}
          onValueChange={(v) => {
            const ot = v as OrderType;
            setOrderType(ot);
            if (ot === 'limit' && !price) {
              setPrice(String(clampTick(selected.probability ?? 0.5)));
            }
          }}
        >
          <SelectTrigger className="mb-1.5 h-9 w-auto gap-2 rounded-lg text-sm font-medium">
            <SelectValue />
          </SelectTrigger>
          <SelectContent align="end">
            <SelectItem value="market">{t('Market')}</SelectItem>
            <SelectItem value="limit">{t('Limit')}</SelectItem>
          </SelectContent>
        </Select>
      </div>

      {!isLoggedIn ? (
        <div className="p-4">
          <p className="rounded-xl bg-black/5 p-3 text-center text-sm text-black/50 dark:bg-white/5 dark:text-white/50">
            {t('Log in to place an order.')}
          </p>
        </div>
      ) : (
        <>
          <div className="space-y-5 p-4">
            {/* Outcome selector (CLOB markets are binary) */}
            <div className="grid grid-cols-2 gap-2.5">
              {market.outcomes.map((outcome, i) => {
                const color = outcomeColor(outcome, i);
                const isSelected = selected.id === outcome.id;
                return (
                  <button
                    key={outcome.id}
                    type="button"
                    onClick={() => onSelectOutcome(outcome)}
                    className={cn(
                      'flex cursor-pointer items-center justify-center gap-1.5 rounded-lg px-3 py-3 text-sm font-bold transition-all',
                      isSelected
                        ? 'text-white'
                        : 'bg-secondary text-foreground hover:bg-black/8 dark:hover:bg-white/10',
                    )}
                    style={isSelected ? { backgroundColor: color } : undefined}
                  >
                    <span>{outcome.title}</span>
                    <span
                      className={isSelected ? 'text-white/90' : 'opacity-70'}
                    >
                      {format(outcome.probability ?? 0)}
                    </span>
                  </button>
                );
              })}
            </div>

            {orderType === 'market' ? (
              /* Market: big amount ($) / shares input + quick-add chips */
              <div className="space-y-3">
                <div className="flex items-center justify-between">
                  <span className="text-lg font-medium">
                    {usingAmount ? t('Amount') : t('Shares')}
                  </span>
                  <div className="flex items-center gap-0.5 text-4xl leading-none font-bold">
                    {usingAmount && (
                      <span
                        className={
                          hasValue
                            ? 'text-black/80 dark:text-white/80'
                            : 'text-black/20 dark:text-white/20'
                        }
                      >
                        {symbol}
                      </span>
                    )}
                    <input
                      inputMode="decimal"
                      value={inputVal}
                      onChange={(e) =>
                        setInput(e.target.value.replace(/[^\d.]/g, ''))
                      }
                      placeholder="0"
                      style={{ width: `${Math.max(inputVal.length, 1)}ch` }}
                      className="bg-transparent text-right outline-none placeholder:text-black/20 dark:placeholder:text-white/20"
                    />
                  </div>
                </div>

                <div className="flex justify-end gap-2">
                  {STEPS.map((v) => (
                    <button
                      key={v}
                      type="button"
                      onClick={() => handleStep(v)}
                      className="cursor-pointer rounded-md border border-black/10 px-2.5 py-1 text-sm font-medium text-black/70 transition-colors hover:border-black/20 hover:bg-black/5 dark:border-white/15 dark:text-white/70 dark:hover:bg-white/5"
                    >
                      +{usingAmount ? `${symbol}${v}` : v}
                    </button>
                  ))}
                </div>
              </div>
            ) : (
              <>
                {/* Limit price stepper */}
                <div className="flex items-center justify-between">
                  <span className="text-lg font-medium">
                    {t('Limit price')}
                  </span>
                  <div className="flex items-center rounded-xl border border-black/10 dark:border-white/15">
                    <button
                      type="button"
                      onClick={() => stepPrice(-1)}
                      className="cursor-pointer px-3.5 py-2 text-lg font-medium text-black/60 transition-colors hover:text-black dark:text-white/60 dark:hover:text-white"
                    >
                      −
                    </button>
                    <span className="min-w-[56px] text-center text-base font-semibold tabular-nums">
                      {centsLabel}
                    </span>
                    <button
                      type="button"
                      onClick={() => stepPrice(1)}
                      className="cursor-pointer px-3.5 py-2 text-lg font-medium text-black/60 transition-colors hover:text-black dark:text-white/60 dark:hover:text-white"
                    >
                      +
                    </button>
                  </div>
                </div>

                {/* Shares + step chips */}
                <div className="space-y-3">
                  <div className="flex items-center justify-between">
                    <span className="text-lg font-medium">{t('Shares')}</span>
                    <Input
                      inputMode="decimal"
                      value={shares}
                      onChange={(e) =>
                        setShares(e.target.value.replace(/[^\d.]/g, ''))
                      }
                      placeholder="0"
                      className="h-11 w-28 rounded-xl text-right text-base font-semibold"
                    />
                  </div>

                  <div className="flex justify-end gap-2">
                    {LIMIT_STEPS.map((v) => (
                      <button
                        key={v}
                        type="button"
                        onClick={() => stepShares(v)}
                        className="cursor-pointer rounded-md border border-black/10 px-2.5 py-1 text-sm font-medium text-black/70 transition-colors hover:border-black/20 hover:bg-black/5 dark:border-white/15 dark:text-white/70 dark:hover:bg-white/5"
                      >
                        {v > 0 ? `+${v}` : v}
                      </button>
                    ))}
                  </div>
                </div>

                {/* Expires (time-in-force) */}
                <div className="flex items-center justify-between border-t border-black/8 pt-4 dark:border-white/8">
                  <span className="text-sm text-black/50 dark:text-white/50">
                    {t('Expires')}
                  </span>
                  <Select value={expiry} onValueChange={setExpiry}>
                    <SelectTrigger className="h-8 w-auto gap-1 border-0 bg-transparent px-1 text-sm font-medium shadow-none">
                      <SelectValue />
                    </SelectTrigger>
                    <SelectContent align="end">
                      {EXPIRY_OPTIONS.map((o) => (
                        <SelectItem key={o.value} value={o.value}>
                          {t(o.label)}
                        </SelectItem>
                      ))}
                    </SelectContent>
                  </Select>
                </div>
              </>
            )}
          </div>

          {/* Footer: estimate + submit */}
          <div className="space-y-4 p-4 pt-0">
            {orderType === 'market'
              ? hasValue && (
                  <div className="space-y-2 border-t border-black/8 pt-4 dark:border-white/8">
                    {side === 'buy' && (
                      <div className="flex items-center justify-between text-xs text-black/40 dark:text-white/40">
                        <span>{t('Est. shares')}</span>
                        <span>{outShares.toFixed(2)}</span>
                      </div>
                    )}
                    {notEnoughLiquidity && (
                      <p className="text-xs text-pm-no">
                        {t('Not enough liquidity to fill fully.')}
                      </p>
                    )}
                    <div className="flex items-end justify-between">
                      <div>
                        <p className="flex items-center gap-1.5 text-base font-semibold">
                          {side === 'buy' ? t('To win') : t('You receive')}
                          <span aria-hidden>💵</span>
                        </p>
                        <p className="text-xs text-black/40 dark:text-white/40">
                          {t('Avg. Price')} {format(avgPrice)}
                        </p>
                      </div>
                      <p className="text-3xl leading-none font-bold text-success">
                        {format(primaryValue)}
                      </p>
                    </div>
                  </div>
                )
              : numShares > 0 &&
                numPrice > 0 && (
                  <div className="space-y-1.5 border-t border-black/8 pt-4 dark:border-white/8">
                    <div className="flex items-center justify-between text-sm">
                      <span className="text-black/50 dark:text-white/50">
                        {t('Total')}
                      </span>
                      <span className="font-semibold text-primary">
                        {format(limitTotal)}
                      </span>
                    </div>
                    {side === 'buy' && (
                      <div className="flex items-center justify-between">
                        <span className="flex items-center gap-1.5 text-sm text-black/50 dark:text-white/50">
                          {t('To win')} <span aria-hidden>💵</span>
                        </span>
                        <span className="text-xl leading-none font-bold text-success">
                          {format(numShares)}
                        </span>
                      </div>
                    )}
                  </div>
                )}

            <Button
              variant="primary"
              type="button"
              onClick={submit}
              loading={isPending}
              className="h-12 w-full rounded-xl text-base font-semibold"
            >
              {orderType === 'limit'
                ? side === 'buy'
                  ? t('Place buy order')
                  : t('Place sell order')
                : side === 'buy'
                  ? t('Buy')
                  : t('Sell')}
            </Button>

            <div>
              <h4 className="mb-1 text-sm font-semibold">
                {t('Your open orders')}
              </h4>
              <OpenOrders slug={slug} refreshKey={refreshKey} />
            </div>
          </div>
        </>
      )}
    </Card>
  );
}
