import { useCallback, useEffect, useMemo, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import {
  deleteAccount,
  exportAll,
  fetchAccounts,
  importItems,
  logout,
  type AccountDto,
} from '../api/client';
import { IconButton } from '../components/IconButton';
import { IconEdit, IconLink, IconTrash } from '../components/icons';
import { TotpCard } from '../components/TotpCard';
import { buildShareUrl } from '../lib/shareUrl';

function CodeRow({ a, onDelete }: { a: AccountDto; onDelete: (id: string) => void }) {
  const [linkCopied, setLinkCopied] = useState(false);

  async function onCopyShare() {
    try {
      await navigator.clipboard.writeText(buildShareUrl(a.shareToken));
      setLinkCopied(true);
      setTimeout(() => setLinkCopied(false), 1500);
    } catch {
      /* ignore */
    }
  }

  return (
    <TotpCard
      label={a.label}
      issuer={a.issuer}
      code={a.code}
      expiresInSeconds={a.expiresInSeconds}
      period={a.period}
      toolbar={
        <>
          <IconButton label="复制分享链接" copied={linkCopied} onClick={() => void onCopyShare()}>
            <IconLink />
          </IconButton>
          <IconButton
            label="编辑"
            to={`/edit/${encodeURIComponent(a.id)}`}
          >
            <IconEdit />
          </IconButton>
          <IconButton
            label="删除"
            variant="danger"
            onClick={() => {
              if (confirm('确定删除此条目？')) onDelete(a.id);
            }}
          >
            <IconTrash />
          </IconButton>
        </>
      }
    />
  );
}

const UNCATEGORIZED = '__uncategorized__';

type CategoryKey = 'all' | typeof UNCATEGORIZED | string;

function buildCategories(accounts: AccountDto[]): { key: CategoryKey; label: string; count: number }[] {
  const issuerCounts = new Map<string, number>();
  let uncategorized = 0;
  for (const a of accounts) {
    const issuer = a.issuer?.trim();
    if (!issuer) {
      uncategorized += 1;
    } else {
      issuerCounts.set(issuer, (issuerCounts.get(issuer) ?? 0) + 1);
    }
  }
  const items: { key: CategoryKey; label: string; count: number }[] = [
    { key: 'all', label: '全部', count: accounts.length },
  ];
  for (const [issuer, count] of [...issuerCounts.entries()].sort((a, b) =>
    a[0].localeCompare(b[0], 'zh'),
  )) {
    items.push({ key: issuer, label: issuer, count });
  }
  if (uncategorized > 0) {
    items.push({ key: UNCATEGORIZED, label: '未分类', count: uncategorized });
  }
  return items;
}

function filterByCategory(accounts: AccountDto[], key: CategoryKey): AccountDto[] {
  if (key === 'all') return accounts;
  if (key === UNCATEGORIZED) {
    return accounts.filter((a) => !a.issuer?.trim());
  }
  return accounts.filter((a) => a.issuer?.trim() === key);
}

function MenuIcon() {
  return (
    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden>
      <path
        d="M4 7h16M4 12h16M4 17h16"
        stroke="currentColor"
        strokeWidth="2"
        strokeLinecap="round"
      />
    </svg>
  );
}

export function HomePage() {
  const nav = useNavigate();
  const [accounts, setAccounts] = useState<AccountDto[] | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [category, setCategory] = useState<CategoryKey>('all');
  const [drawerOpen, setDrawerOpen] = useState(false);
  const [isMobile, setIsMobile] = useState(
    () => typeof window !== 'undefined' && window.matchMedia('(max-width: 640px)').matches,
  );

  const load = useCallback(async () => {
    try {
      const list = await fetchAccounts();
      setAccounts(list);
      setError(null);
    } catch {
      nav('/login', { replace: true });
    }
  }, [nav]);

  useEffect(() => {
    void load();
  }, [load]);

  useEffect(() => {
    const t = setInterval(() => void load(), 1000);
    return () => clearInterval(t);
  }, [load]);

  async function onLogout() {
    await logout();
    nav('/login', { replace: true });
  }

  async function onExport() {
    try {
      const data = await exportAll();
      const blob = new Blob([JSON.stringify(data, null, 2)], {
        type: 'application/json',
      });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = `sprout2fa-export-${new Date().toISOString().slice(0, 10)}.json`;
      a.click();
      URL.revokeObjectURL(url);
    } catch (e) {
      setError(e instanceof Error ? e.message : '导出失败');
    }
  }

  function onImportPick() {
    const input = document.createElement('input');
    input.type = 'file';
    input.accept = 'application/json,.json';
    input.onchange = () => {
      const file = input.files?.[0];
      if (!file) return;
      const reader = new FileReader();
      reader.onload = async () => {
        try {
          const text = String(reader.result || '');
          const j = JSON.parse(text) as { items?: unknown };
          if (!Array.isArray(j.items)) {
            setError('文件格式无效：缺少 items 数组');
            return;
          }
          const r = await importItems(
            j.items as {
              label: string;
              issuer: string | null;
              secret: string;
              algorithm: string;
              digits: number;
              period: number;
            }[],
          );
          alert(`已导入 ${r.imported} 条`);
          await load();
        } catch (e) {
          setError(e instanceof Error ? e.message : '导入失败');
        }
      };
      reader.readAsText(file);
    };
    input.click();
  }

  const categories = useMemo(
    () => (accounts ? buildCategories(accounts) : []),
    [accounts],
  );

  const visibleAccounts = useMemo(
    () => (accounts ? filterByCategory(accounts, category) : []),
    [accounts, category],
  );

  useEffect(() => {
    if (!accounts) return;
    if (category === 'all') return;
    const stillValid = categories.some((c) => c.key === category);
    if (!stillValid) setCategory('all');
  }, [accounts, categories, category]);

  useEffect(() => {
    if (!drawerOpen) return;
    const prev = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => {
      document.body.style.overflow = prev;
    };
  }, [drawerOpen]);

  useEffect(() => {
    if (!drawerOpen) return;
    const onKey = (e: KeyboardEvent) => {
      if (e.key === 'Escape') setDrawerOpen(false);
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [drawerOpen]);

  useEffect(() => {
    const mq = window.matchMedia('(max-width: 640px)');
    const update = () => {
      setIsMobile(mq.matches);
      if (!mq.matches) setDrawerOpen(false);
    };
    update();
    mq.addEventListener('change', update);
    return () => mq.removeEventListener('change', update);
  }, []);

  function selectCategory(key: CategoryKey) {
    setCategory(key);
    setDrawerOpen(false);
  }

  if (accounts === null) {
    return (
      <main>
        <p className="sub">加载中…</p>
      </main>
    );
  }

  const subText =
    category === 'all'
      ? `${accounts.length} 个账户`
      : `${visibleAccounts.length} / ${accounts.length}`;

  return (
    <main className={`home-page${drawerOpen ? ' drawer-open' : ''}`}>
      <button
        type="button"
        className="drawer-backdrop"
        aria-label="关闭分类菜单"
        tabIndex={drawerOpen ? 0 : -1}
        onClick={() => setDrawerOpen(false)}
      />
      <div className="home-layout">
        <aside
          id="category-sidebar"
          className="category-sidebar"
          aria-label="分类筛选"
          aria-hidden={isMobile && !drawerOpen ? true : undefined}
        >
          <div className="category-sidebar-title">分类</div>
          <nav className="category-nav">
            {categories.map((c) => (
              <button
                key={c.key}
                type="button"
                className={`category-item${category === c.key ? ' active' : ''}`}
                onClick={() => selectCategory(c.key)}
              >
                <span className="category-item-label">{c.label}</span>
                <span className="category-item-count">{c.count}</span>
              </button>
            ))}
          </nav>
        </aside>

        <div className="home-main">
          <div className="top-nav">
            <div className="top-nav-start">
              <button
                type="button"
                className="btn icon drawer-toggle"
                aria-expanded={drawerOpen}
                aria-controls="category-sidebar"
                aria-label={drawerOpen ? '关闭分类' : '打开分类'}
                onClick={() => setDrawerOpen((v) => !v)}
              >
                <MenuIcon />
              </button>
              <div className="top-nav-title">
                <img
                  className="top-nav-logo"
                  src="/logo.png"
                  alt=""
                  width={36}
                  height={36}
                />
                <h1 style={{ margin: 0 }}>验证码</h1>
              </div>
            </div>
            <button type="button" className="btn secondary" onClick={() => void onLogout()}>
              退出
            </button>
          </div>
          <p className="sub">{subText}</p>

          <div className="row-actions">
            <Link className="btn" to="/add">
              添加
            </Link>
            <button type="button" className="btn secondary" onClick={() => void onExport()}>
              导出
            </button>
            <button type="button" className="btn secondary" onClick={onImportPick}>
              导入
            </button>
          </div>

          {error ? <p className="error">{error}</p> : null}

          {accounts.length === 0 ? (
            <div className="card">
              <p>暂无条目，点击「添加」扫码或手动输入。</p>
            </div>
          ) : visibleAccounts.length === 0 ? (
            <div className="card">
              <p>该分类下暂无账户。</p>
            </div>
          ) : (
            visibleAccounts.map((a) => (
              <CodeRow
                key={a.id}
                a={a}
                onDelete={async (id) => {
                  try {
                    await deleteAccount(id);
                    await load();
                  } catch (e) {
                    setError(e instanceof Error ? e.message : '删除失败');
                  }
                }}
              />
            ))
          )}
        </div>
      </div>
    </main>
  );
}
