import { useEffect, useMemo, useState } from 'react';
import { Link, Navigate } from 'react-router-dom';
import { api, formatCredits, creditsToCentsString, centsToCreditsString, compareCents, toCentsBigInt } from '../api';
import { useAuth } from '../AuthContext';
import ItemTile from '../components/ItemTile';
import { onInventoryChanged } from '../inventoryEvents';
import { getSocket, onSocketSessionOk } from '../socket';
import { loadInventorySort, saveInventorySort } from '../userPrefs';

const PAGE_SIZE = 50;

function formatVaultCountdown(ms) {
  if (ms == null || !Number.isFinite(ms)) return '—';
  const totalSec = Math.max(0, Math.ceil(ms / 1000));
  const m = Math.floor(totalSec / 60);
  const s = totalSec % 60;
  return `${m}:${String(s).padStart(2, '0')}`;
}

export default function Dashboard() {
  const { user, loading, patchUser } = useAuth();
  const [inventory, setInventory] = useState([]);
  const [vault, setVault] = useState(null);
  const [vaultMessage, setVaultMessage] = useState('');
  const [error, setError] = useState('');
  const [sort, setSort] = useState('value');
  const [page, setPage] = useState(1);
  const [selected, setSelected] = useState([]);
  const [busy, setBusy] = useState(false);
  const [maxValue, setMaxValue] = useState('10');
  const [autoSellEnabled, setAutoSellEnabled] = useState(false);
  const [settingsBusy, setSettingsBusy] = useState(false);
  const [now, setNow] = useState(() => Date.now());

  useEffect(() => {
    if (!user?.id) return;
    const saved = loadInventorySort(user.id);
    if (saved) setSort(saved);
  }, [user?.id]);

  useEffect(() => {
    if (!user) return;
    setAutoSellEnabled(Boolean(user.autoSellEnabled));
    if (user.autoSellThresholdCents != null) {
      setMaxValue(centsToCreditsString(user.autoSellThresholdCents));
    }
  }, [user?.id, user?.autoSellEnabled, user?.autoSellThresholdCents]);

  const load = async () => {
    const inv = await api.inventory();
    setInventory(inv.inventory);
    setVault(inv.vault || null);
  };

  useEffect(() => {
    if (!user || user.role === 'admin') return;
    let cancelled = false;
    (async () => {
      try {
        await load();
      } catch (err) {
        if (!cancelled) setError(err.message);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [user]);

  useEffect(() => {
    if (!user || user.role === 'admin') return undefined;
    return onInventoryChanged(() => {
      load().catch(() => {});
    });
  }, [user]);

  useEffect(() => {
    if (!user || user.role === 'admin') return undefined;
    const socket = getSocket();
    const onYield = (payload) => {
      if (payload?.user) patchUser(payload.user);
      else if (payload?.balance != null) patchUser({ balance: payload.balance });
      if (payload?.vault) setVault(payload.vault);
      const credited = toCentsBigInt(payload?.credited);
      if (credited > 0n) {
        setVaultMessage(`Vault yield +${formatCredits(payload.credited)} cr`);
      }
      load().catch(() => {});
    };
    const onSession = () => {
      load().catch(() => {});
    };
    socket.on('vault:yield', onYield);
    const offSession = onSocketSessionOk(onSession);
    return () => {
      socket.off('vault:yield', onYield);
      offSession();
    };
  }, [user, patchUser]);

  useEffect(() => {
    if (!(vault?.used > 0)) return undefined;
    const id = setInterval(() => setNow(Date.now()), 1000);
    return () => clearInterval(id);
  }, [vault?.used]);

  useEffect(() => {
    if (!(vault?.used > 0) || !vault?.nextYieldAt) return undefined;
    const dueAt = new Date(vault.nextYieldAt).getTime();
    const delay = Math.max(500, dueAt - Date.now() + 800);
    const t = setTimeout(() => {
      load().catch(() => {});
    }, delay);
    return () => clearTimeout(t);
  }, [vault?.nextYieldAt, vault?.used]);

  const vaultCountdownMs = useMemo(() => {
    if (!(vault?.used > 0) || !vault?.nextYieldAt) return null;
    return new Date(vault.nextYieldAt).getTime() - now;
  }, [vault?.used, vault?.nextYieldAt, now]);

  const vaultTickLabel = useMemo(() => {
    const ms = vault?.tickMs ?? (vault?.mode === 'offline' ? 600_000 : 300_000);
    return `${Math.round(ms / 60_000)} min`;
  }, [vault?.tickMs, vault?.mode]);

  const sorted = useMemo(() => {
    const list = inventory.filter((inv) => !inv.staked);
    if (sort === 'value') {
      list.sort((a, b) =>
        compareCents(
          b.valueCents ?? b.item?.valueCents ?? b.item?.marketValue,
          a.valueCents ?? a.item?.valueCents ?? a.item?.marketValue
        )
      );
    } else {
      list.sort((a, b) => new Date(b.obtainedAt) - new Date(a.obtainedAt));
    }
    return list;
  }, [inventory, sort]);

  const totalPages = Math.max(1, Math.ceil(sorted.length / PAGE_SIZE));
  const currentPage = Math.min(page, totalPages);
  const pageItems = useMemo(() => {
    const start = (currentPage - 1) * PAGE_SIZE;
    return sorted.slice(start, start + PAGE_SIZE);
  }, [sorted, currentPage]);

  useEffect(() => {
    setPage(1);
  }, [sort]);

  useEffect(() => {
    if (page > totalPages) setPage(totalPages);
  }, [page, totalPages]);

  const selectedValue = useMemo(() => {
    let sum = 0n;
    for (const inv of inventory) {
      if (!selected.includes(inv.id)) continue;
      sum += toCentsBigInt(inv.valueCents ?? inv.item?.valueCents);
    }
    return sum.toString();
  }, [inventory, selected]);

  const thresholdCents = useMemo(() => creditsToCentsString(maxValue), [maxValue]);

  const belowThreshold = useMemo(() => {
    if (thresholdCents == null) return [];
    return inventory.filter((inv) => {
      if (inv.favorite || inv.staked) return false;
      return compareCents(inv.valueCents ?? inv.item?.valueCents, thresholdCents) < 0;
    });
  }, [inventory, thresholdCents]);

  const selectedItems = useMemo(
    () => inventory.filter((inv) => selected.includes(inv.id)),
    [inventory, selected]
  );

  const stakedItems = useMemo(() => inventory.filter((inv) => inv.staked), [inventory]);

  const selectedHasFavorite = selectedItems.some((inv) => inv.favorite);
  const selectedAllFavorite =
    selectedItems.length > 0 && selectedItems.every((inv) => inv.favorite);
  const selectedCanSell = selectedItems.some((inv) => !inv.favorite && !inv.staked);
  const selectedStaked = selectedItems.filter((inv) => inv.staked);
  const selectedUnstaked = selectedItems.filter((inv) => !inv.staked);
  const vaultFree = Math.max(0, (vault?.slots ?? 3) - (vault?.used ?? 0));
  const canStake = selectedUnstaked.length > 0 && selectedUnstaked.length <= vaultFree;
  const canUnstake = selectedStaked.length > 0;

  const belowValue = useMemo(() => {
    let sum = 0n;
    for (const inv of belowThreshold) {
      sum += toCentsBigInt(inv.valueCents ?? inv.item?.valueCents);
    }
    return sum.toString();
  }, [belowThreshold]);

  const toggle = (id) => {
    setSelected((prev) =>
      prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]
    );
  };

  const sellIds = async (ids) => {
    if (!ids.length) return;
    setBusy(true);
    setError('');
    try {
      const data = await api.sellInventory(ids);
      patchUser({ balance: data.user.balance });
      setSelected((prev) => prev.filter((id) => !ids.includes(id)));
      await load();
    } catch (err) {
      setError(err.message);
    } finally {
      setBusy(false);
    }
  };

  const sell = async () => {
    const ids = selectedItems
      .filter((inv) => !inv.favorite && !inv.staked)
      .map((inv) => inv.id);
    if (!ids.length) return;
    await sellIds(ids);
  };

  const stakeSelected = async () => {
    const ids = selectedUnstaked.map((inv) => inv.id);
    if (!ids.length) return;
    setBusy(true);
    setError('');
    setVaultMessage('');
    try {
      const data = await api.vaultStake(ids);
      if (data.user) patchUser({ balance: data.user.balance });
      setSelected([]);
      setVaultMessage(`Staked ${ids.length} item${ids.length === 1 ? '' : 's'} in vault`);
      await load();
    } catch (err) {
      setError(err.message);
    } finally {
      setBusy(false);
    }
  };

  const unstakeSelected = async () => {
    const ids = selectedStaked.map((inv) => inv.id);
    if (!ids.length) return;
    setBusy(true);
    setError('');
    setVaultMessage('');
    try {
      const data = await api.vaultUnstake(ids);
      if (data.user) patchUser({ balance: data.user.balance });
      setSelected([]);
      const yieldPart =
        data.yieldCredited > 0
          ? ` · +${formatCredits(data.yieldCredited)} cr yield`
          : '';
      setVaultMessage(
        `Unstaked ${ids.length} item${ids.length === 1 ? '' : 's'}${yieldPart}`
      );
      await load();
    } catch (err) {
      setError(err.message);
    } finally {
      setBusy(false);
    }
  };

  const setFavorite = async (favorite) => {
    if (!selected.length) return;
    setBusy(true);
    setError('');
    try {
      await api.setInventoryFavorite(selected, favorite);
      await load();
    } catch (err) {
      setError(err.message);
    } finally {
      setBusy(false);
    }
  };

  const sellBelow = async () => {
    const ids = belowThreshold.map((inv) => inv.id);
    if (!ids.length || thresholdCents == null) return;
    const favSkipped = inventory.filter(
      (inv) =>
        inv.favorite &&
        compareCents(inv.valueCents ?? inv.item?.valueCents, thresholdCents) < 0
    ).length;
    const ok = window.confirm(
      `Sell ${ids.length} item${ids.length === 1 ? '' : 's'} below ${formatCredits(thresholdCents)} cr for ${formatCredits(belowValue)} cr?` +
        (favSkipped ? ` (${favSkipped} favorite item${favSkipped === 1 ? '' : 's'} excluded)` : '')
    );
    if (!ok) return;
    await sellIds(ids);
  };

  const saveAutoSell = async (enabled, thresholdCredits) => {
    setSettingsBusy(true);
    setError('');
    try {
      const data = await api.updateAutoSell({ enabled, thresholdCredits });
      patchUser(data.user);
    } catch (err) {
      setError(err.message);
    } finally {
      setSettingsBusy(false);
    }
  };

  const onAutoSellToggle = async (enabled) => {
    setAutoSellEnabled(enabled);
    if (creditsToCentsString(maxValue) == null) return;
    await saveAutoSell(enabled, maxValue);
  };

  const onThresholdBlur = async () => {
    const cents = creditsToCentsString(maxValue);
    if (cents == null) return;
    const savedCents = String(user?.autoSellThresholdCents ?? 1000);
    if (cents === savedCents && autoSellEnabled === Boolean(user?.autoSellEnabled)) {
      return;
    }
    await saveAutoSell(autoSellEnabled, maxValue);
  };

  if (loading) return <p className="muted">Loading…</p>;
  if (!user) return <Navigate to="/login" replace />;
  if (user.role === 'admin') return <Navigate to="/admin" replace />;

  return (
    <div>
      {vaultMessage && <div className="ok-msg">{vaultMessage}</div>}

      {stakedItems.length > 0 && (
        <section className="section">
          <div className="vault-panel admin-block">
            <div className="section-head">
              <div>
                <h2>Vault</h2>
              </div>
              <div className="vault-timer" title={`Shared ${vaultTickLabel} tick (${vault?.mode || 'online'})`}>
                <span className="muted">Next payment</span>
                <strong className={vaultCountdownMs != null && vaultCountdownMs <= 0 ? 'gold' : ''}>
                  {vaultCountdownMs != null && vaultCountdownMs <= 0
                    ? 'paying…'
                    : formatVaultCountdown(vaultCountdownMs)}
                </strong>
              </div>
            </div>
            <div className="vault-stats">
              <span>
                Slots <strong>{vault?.used ?? 0}/{vault?.slots ?? 3}</strong>
              </span>
              <span>
                Online / tick{' '}
                <strong className="gold">{formatCredits(vault?.onlinePerTick ?? 0)} cr</strong>
              </span>
              <span>
                Offline / tick{' '}
                <strong>{formatCredits(vault?.offlinePerTick ?? 0)} cr</strong>
              </span>
            </div>
            <div className="grid vault-grid">
              {stakedItems.map((inv) => (
                <ItemTile
                  key={`vault-${inv.id}`}
                  item={inv.item}
                  favorite={inv.favorite}
                  staked
                  selected={selected.includes(inv.id)}
                  onClick={() => toggle(inv.id)}
                />
              ))}
            </div>
          </div>
        </section>
      )}

      <section className="section">
        <div className="section-head inv-section-head">
          <h1>Inventory</h1>
          <div className="inv-toolbar-left">
            <div className="sort-toggle" role="group" aria-label="Sort inventory">
              <button
                type="button"
                className={sort === 'value' ? 'active' : ''}
                onClick={() => {
                  setSort('value');
                  saveInventorySort(user.id, 'value');
                }}
              >
                Highest value
              </button>
              <button
                type="button"
                className={sort === 'recent' ? 'active' : ''}
                onClick={() => {
                  setSort('recent');
                  saveInventorySort(user.id, 'recent');
                }}
              >
                Most recent
              </button>
            </div>
            {sorted.length > 0 && (
              <span className="muted inv-count">
                {sorted.length} item{sorted.length === 1 ? '' : 's'}
              </span>
            )}
          </div>
        </div>

        {selected.length > 0 && (
          <div className="inv-toolbar">
            <div className="row-actions">
              <button
                type="button"
                className="btn danger"
                onClick={sell}
                disabled={busy || !selectedCanSell}
                title={
                  selectedHasFavorite || selectedStaked.length
                    ? 'Favorites and vault items cannot be sold'
                    : undefined
                }
              >
                Sell
              </button>
              <span className="muted">
                {selected.length} selected ·{' '}
                <span className="item-price">
                  {formatCredits(selectedValue)} cr
                </span>
                {selectedHasFavorite ? ' · includes favorites' : ''}
              </span>
              <button
                type="button"
                className="btn ghost"
                onClick={() => setSelected([])}
                disabled={busy}
              >
                Clear
              </button>
              {!selectedAllFavorite && (
                <button
                  type="button"
                  className="btn secondary"
                  onClick={() => setFavorite(true)}
                  disabled={busy}
                >
                  <span className="btn-ico" aria-hidden="true">
                    ★
                  </span>
                  Favorite
                </button>
              )}
              {selectedItems.some((inv) => inv.favorite) && (
                <button
                  type="button"
                  className="btn ghost"
                  onClick={() => setFavorite(false)}
                  disabled={busy}
                >
                  <span className="btn-ico" aria-hidden="true">
                    ★
                  </span>
                  Unfavorite
                </button>
              )}
              {canStake && (
                <button
                  type="button"
                  className="btn secondary"
                  onClick={stakeSelected}
                  disabled={busy}
                >
                  <span className="btn-ico btn-ico-vault" aria-hidden="true">
                    V
                  </span>
                  Stake ({selectedUnstaked.length})
                </button>
              )}
              {canUnstake && (
                <button
                  type="button"
                  className="btn secondary"
                  onClick={unstakeSelected}
                  disabled={busy}
                >
                  <span className="btn-ico btn-ico-vault" aria-hidden="true">
                    V
                  </span>
                  Unstake ({selectedStaked.length})
                </button>
              )}
            </div>
          </div>
        )}

        <div className="inv-bulk-sell">
          {sorted.length > 0 && (
            <>
              <button
                type="button"
                className="btn"
                disabled={busy || belowThreshold.length === 0}
                onClick={sellBelow}
              >
                Sell below
              </button>
              <label className="inv-bulk-label">
                <input
                  type="number"
                  min="0"
                  step="0.01"
                  value={maxValue}
                  disabled={busy || settingsBusy}
                  onChange={(e) => setMaxValue(e.target.value)}
                  onBlur={onThresholdBlur}
                  aria-label="Maximum market value in credits"
                />
                <span className="muted">cr</span>
              </label>
              <span className="muted inv-bulk-preview">
                {belowThreshold.length} item{belowThreshold.length === 1 ? '' : 's'} ·{' '}
                <span className="item-price">
                  {formatCredits(belowValue)} cr
                </span>
              </span>
            </>
          )}
          {sorted.length === 0 && (
            <label className="inv-bulk-label">
              Threshold
              <input
                type="number"
                min="0"
                step="0.01"
                value={maxValue}
                disabled={busy || settingsBusy}
                onChange={(e) => setMaxValue(e.target.value)}
                onBlur={onThresholdBlur}
                aria-label="Auto-sell threshold in credits"
              />
              <span className="muted">cr</span>
            </label>
          )}
          <div className="inv-auto-sell-box">
            <label className="inv-auto-sell-toggle">
              <input
                type="checkbox"
                checked={autoSellEnabled}
                disabled={busy || settingsBusy}
                onChange={(e) => onAutoSellToggle(e.target.checked)}
              />
              <span>Auto-sell</span>
            </label>
            <span className="inv-auto-sell-at muted">
              sell below {formatCredits(creditsToCentsString(maxValue) ?? 0)} cr
            </span>
          </div>
        </div>

        {error && <div className="error">{error}</div>}

        {sorted.length === 0 ? (
          <div className="empty">
            {stakedItems.length > 0 ? (
              'All items are in the vault.'
            ) : (
              <>
                Empty for now —{' '}
                <Link to="/cases" style={{ color: 'var(--accent)' }}>
                  open a case
                </Link>
                .
              </>
            )}
          </div>
        ) : (
          <>
            <div className="grid inv-grid">
              {pageItems.map((inv) => (
                <ItemTile
                  key={inv.id}
                  item={inv.item}
                  favorite={inv.favorite}
                  selected={selected.includes(inv.id)}
                  onClick={() => toggle(inv.id)}
                />
              ))}
            </div>
            {totalPages > 1 && (
              <div className="inv-pager">
                <button
                  type="button"
                  className="btn ghost"
                  disabled={currentPage <= 1}
                  onClick={() => setPage((p) => Math.max(1, p - 1))}
                >
                  Previous
                </button>
                <span className="muted">
                  Page {currentPage} / {totalPages}
                </span>
                <button
                  type="button"
                  className="btn ghost"
                  disabled={currentPage >= totalPages}
                  onClick={() => setPage((p) => Math.min(totalPages, p + 1))}
                >
                  Next
                </button>
              </div>
            )}
          </>
        )}
      </section>
    </div>
  );
}
