import {
  ContainerOutlined,
  CopyOutlined,
  DeleteOutlined,
  FileTextOutlined,
  FolderOutlined,
  InboxOutlined,
  MailOutlined,
  PlusOutlined,
  ReloadOutlined,
  SearchOutlined,
  SendOutlined,
  SettingOutlined,
  WarningOutlined
} from '@ant-design/icons';
import {
  Alert,
  App as AntApp,
  Badge,
  Button,
  Card,
  Checkbox,
  Descriptions,
  Drawer,
  Form,
  Grid,
  Input,
  InputNumber,
  List,
  Modal,
  Pagination,
  Select,
  Skeleton,
  Space,
  Table,
  Tabs,
  Tag,
  Typography
} from 'antd';
import type { ColumnsType } from 'antd/es/table';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom';

import { CodeBlock } from '../components/common/CodeBlock';
import { EmptyState } from '../components/common/EmptyState';
import { PageHeader } from '../components/common/PageHeader';
import { SectionCard } from '../components/common/SectionCard';
import { StatusPill } from '../components/common/StatusPill';
import { useAppContext } from '../frontend/app-context';
import { useI18n } from '../frontend/i18n/react';
import { detailHistoryLocation, detailHistoryState } from '../frontend/navigation-state';
import { api } from '../frontend/services/api';
import type { Domain, InboundFolder, InboundMailbox, InboundMessage, MailboxClientConfig, RuntimeConfig } from '../frontend/types';
import { useMediaQuery } from '../frontend/use-media-query';

type MailMessage = InboundMessage & { folder?: string };

interface MailboxFormValues {
  localPart: string;
  domain: string;
  password: string;
  displayName?: string;
  quotaMb?: number | null;
  aliases?: string;
  forwardTo?: string;
  keepForwarded?: boolean;
}

const standardFolders = ['INBOX', 'Sent', 'Drafts', 'Trash', 'Junk', 'Archive'];
type MessageTab = 'text' | 'html' | 'raw';

export default function Inbox() {
  const { message } = AntApp.useApp();
  const screens = Grid.useBreakpoint();
  const isDesktop = useMediaQuery('(min-width: 1024px)');
  const { locale, t } = useI18n();
  const { config } = useAppContext();
  const location = useLocation();
  const navigate = useNavigate();
  const params = useParams<{ messageId?: string }>();
  const [searchParams, setSearchParams] = useSearchParams();
  const [mailboxForm] = Form.useForm<MailboxFormValues>();
  const [catchAllForm] = Form.useForm<{ catchAllAddress?: string }>();
  const [domains, setDomains] = useState<Domain[]>([]);
  const [mailboxes, setMailboxes] = useState<InboundMailbox[]>([]);
  const [folders, setFolders] = useState<InboundFolder[]>(fallbackFolders());
  const [messages, setMessages] = useState<MailMessage[]>([]);
  const [total, setTotal] = useState(0);
  const [selectedMessage, setSelectedMessage] = useState<MailMessage | null>(null);
  const [loading, setLoading] = useState(true);
  const [messagesLoading, setMessagesLoading] = useState(false);
  const [detailLoading, setDetailLoading] = useState(false);
  const [loadError, setLoadError] = useState('');
  const [messagesError, setMessagesError] = useState('');
  const [detailError, setDetailError] = useState('');
  const [actionKey, setActionKey] = useState('');
  const [searchDraft, setSearchDraft] = useState(searchParams.get('q') || '');
  const [mailboxOpen, setMailboxOpen] = useState(false);
  const [clientConfig, setClientConfig] = useState<MailboxClientConfig | null>(null);
  const [catchAllDomain, setCatchAllDomain] = useState<Domain | null>(null);
  const pendingDirectClose = useRef<string | null>(null);

  const workspace = searchParams.get('workspace') === 'routing' ? 'routing' : 'messages';
  const selectedMailboxId = Number(searchParams.get('mailboxId') || 0) || null;
  const folder = searchParams.get('folder') || 'INBOX';
  const readFilter = searchParams.get('read') || 'all';
  const query = searchParams.get('q') || '';
  const page = Math.max(1, Number(searchParams.get('page') || 1) || 1);
  const pageSize = 25;
  const routeMessageId = Number(params.messageId || 0) || null;
  const messageTab = normalizeMessageTab(searchParams.get('tab'));
  const selectedMailbox = mailboxes.find((item) => item.id === selectedMailboxId) || null;

  const loadBase = useCallback(async () => {
    setLoading(true);
    setLoadError('');
    try {
      const [domainResult, mailboxResult] = await Promise.all([
        api.domains(), api.inboundMailboxes()
      ]);
      setDomains(domainResult.domains || []);
      setMailboxes(mailboxResult.mailboxes || []);
    } catch (error) {
      setLoadError(error instanceof Error ? error.message : t('common.error'));
    } finally {
      setLoading(false);
    }
  }, [t]);

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

  useEffect(() => {
    if (!mailboxes.length) return;
    if (routeMessageId && (!searchParams.has('mailboxId') || !searchParams.has('folder'))) return;
    if (selectedMailboxId && mailboxes.some((item) => item.id === selectedMailboxId)) return;
    const next = new URLSearchParams(searchParams);
    next.set('mailboxId', String(mailboxes[0].id));
    next.set('folder', 'INBOX');
    next.set('page', '1');
    setSearchParams(next, { replace: true });
  }, [mailboxes, routeMessageId, searchParams, selectedMailboxId, setSearchParams]);

  const loadFolders = useCallback(async () => {
    if (!selectedMailboxId) {
      setFolders(fallbackFolders());
      return;
    }
    try {
      const result = await api.inboundFolders(selectedMailboxId);
      setFolders(result.folders?.length ? result.folders : fallbackFolders(selectedMailbox || undefined));
    } catch {
      setFolders(fallbackFolders(selectedMailbox || undefined));
    }
  }, [selectedMailbox, selectedMailboxId]);

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

  const loadMessages = useCallback(async () => {
    if (!selectedMailboxId || workspace !== 'messages') {
      setMessages([]);
      setTotal(0);
      return;
    }
    setMessagesLoading(true);
    setMessagesError('');
    try {
      const result = await api.inboundMessages({
        mailboxId: selectedMailboxId,
        folder,
        page,
        pageSize,
        q: query || undefined,
        read: readFilter === 'read' ? true : readFilter === 'unread' ? false : undefined
      });
      setMessages(result.messages || []);
      setTotal(result.total ?? result.messages?.length ?? 0);
    } catch (error) {
      setMessagesError(error instanceof Error ? error.message : t('common.error'));
    } finally {
      setMessagesLoading(false);
    }
  }, [folder, page, query, readFilter, selectedMailboxId, t, workspace]);

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

  useEffect(() => {
    if (!routeMessageId) {
      setSelectedMessage(null);
      setDetailError('');
      return;
    }
    let active = true;
    setDetailLoading(true);
    setDetailError('');
    void api.inboundMessage(routeMessageId)
      .then(async (result) => {
        if (!active) return;
        const detail = result.message as MailMessage | null;
        if (!detail) {
          setDetailError(t('inbox.messageNotFound'));
          return;
        }
        setSelectedMessage(detail);
        if (!detail.read) {
          await api.markInboundMessageRead(detail.id, true);
          if (!active) return;
          setSelectedMessage({ ...detail, read: true });
          setMessages((items) => items.map((item) => item.id === detail.id ? { ...item, read: true } : item));
          void loadFolders();
        }
      })
      .catch((error) => {
        if (active) setDetailError(error instanceof Error ? error.message : t('inbox.detailLoadFailed'));
      })
      .finally(() => {
        if (active) setDetailLoading(false);
      });
    return () => { active = false; };
  }, [loadFolders, routeMessageId, t]);

  useEffect(() => {
    if (!routeMessageId || selectedMessage?.id !== routeMessageId) return;
    if (searchParams.has('mailboxId') && searchParams.has('folder')) return;
    const next = new URLSearchParams(searchParams);
    next.set('mailboxId', String(selectedMessage.mailboxId));
    next.set('folder', selectedMessage.folder || 'INBOX');
    setSearchParams(next, { replace: true });
  }, [routeMessageId, searchParams, selectedMessage, setSearchParams]);

  useEffect(() => {
    const target = pendingDirectClose.current;
    if (!target || detailHistoryState(location.state)?.origin === 'direct') return;
    pendingDirectClose.current = null;
    navigate(target, { replace: true });
  }, [location.key, location.state, navigate]);

  function updateSearch(patch: Record<string, string | number | null>) {
    const next = new URLSearchParams(searchParams);
    Object.entries(patch).forEach(([key, value]) => {
      if (value === null || value === '') next.delete(key);
      else next.set(key, String(value));
    });
    setSearchParams(next);
  }

  function switchWorkspace(key: string) {
    const next = new URLSearchParams(searchParams);
    if (key === 'routing') next.set('workspace', 'routing');
    else next.delete('workspace');
    const suffix = next.toString();
    navigate(`/inbox${suffix ? `?${suffix}` : ''}`);
  }

  function selectMailbox(id: number) {
    const next = new URLSearchParams(searchParams);
    next.set('mailboxId', String(id));
    next.set('folder', 'INBOX');
    next.set('page', '1');
    const suffix = next.toString();
    navigate(`/inbox${suffix ? `?${suffix}` : ''}`);
  }

  function selectFolder(name: string) {
    const next = new URLSearchParams(searchParams);
    next.set('folder', name);
    next.set('page', '1');
    const suffix = next.toString();
    navigate(`/inbox${suffix ? `?${suffix}` : ''}`);
  }

  function openMessage(item: MailMessage) {
    setSelectedMessage(item);
    const suffix = searchParams.toString();
    const target = `/inbox/messages/${item.id}${suffix ? `?${suffix}` : ''}`;
    const historyState = detailHistoryState(location.state);
    if (historyState) {
      navigate(target, {
        state: detailHistoryLocation(historyState.listPath, historyState.depth + 1, historyState.origin)
      });
      return;
    }
    if (routeMessageId) {
      navigate(target, { replace: true });
      return;
    }
    navigate(target, {
      state: detailHistoryLocation(`${location.pathname}${location.search}`, 1)
    });
  }

  function closeMessage() {
    const historyState = detailHistoryState(location.state);
    if (historyState?.origin === 'list') {
      navigate(-historyState.depth);
      return;
    }
    const detail = selectedMessage?.id === routeMessageId ? selectedMessage : null;
    const listPath = inboxListPath(searchParams, detail);
    if (historyState?.origin === 'direct') {
      pendingDirectClose.current = listPath;
      navigate(-historyState.depth);
      return;
    }
    navigate(listPath, { replace: true });
  }

  function changeMessageTab(tab: MessageTab) {
    const next = new URLSearchParams(searchParams);
    if (tab === 'text') next.delete('tab');
    else next.set('tab', tab);
    const historyState = detailHistoryState(location.state);
    const detail = selectedMessage?.id === routeMessageId ? selectedMessage : null;
    const search = next.toString();
    navigate(
      { pathname: location.pathname, search: search ? `?${search}` : '' },
      {
        state: detailHistoryLocation(
          historyState?.listPath || inboxListPath(searchParams, detail),
          (historyState?.depth || 0) + 1,
          historyState?.origin || 'direct'
        )
      }
    );
  }

  async function copyValue(value: string) {
    if (!value) return;
    await navigator.clipboard.writeText(value);
    message.success(t('common.copied'));
  }

  function openCreateMailbox() {
    mailboxForm.resetFields();
    mailboxForm.setFieldsValue({ domain: domains[0]?.domain, password: generateMailboxPassword(), quotaMb: 1024, keepForwarded: true });
    setMailboxOpen(true);
  }

  async function createMailbox() {
    const values = await mailboxForm.validateFields();
    setActionKey('mailbox:create');
    try {
      const result = await api.createInboundMailbox({
        address: `${values.localPart}@${values.domain}`,
        displayName: values.displayName,
        password: values.password,
        aliases: values.aliases,
        forwardTo: values.forwardTo,
        keepForwarded: values.keepForwarded,
        quotaMb: values.quotaMb
      });
      message.success(t('actions.inboundMailboxCreated'));
      setMailboxOpen(false);
      mailboxForm.resetFields();
      setClientConfig(result.clientConfig || buildMailboxClientConfig(result.mailbox, config, values.password));
      await loadBase();
    } catch (error) {
      message.error(error instanceof Error ? error.message : t('common.error'));
    } finally {
      setActionKey('');
    }
  }

  function openCatchAll(domain: Domain) {
    setCatchAllDomain(domain);
    catchAllForm.setFieldsValue({ catchAllAddress: domain.catchAllAddress || '' });
  }

  async function saveCatchAll() {
    if (!catchAllDomain) return;
    const values = await catchAllForm.validateFields();
    setActionKey(`catch-all:${catchAllDomain.id}`);
    try {
      const result = await api.patchDomain(catchAllDomain.id, { catchAllAddress: String(values.catchAllAddress || '').trim() });
      setDomains((items) => items.map((item) => item.id === result.domain.id ? result.domain : item));
      setCatchAllDomain(null);
      message.success(t('actions.domainSaved'));
    } catch (error) {
      message.error(error instanceof Error ? error.message : t('common.error'));
    } finally {
      setActionKey('');
    }
  }

  const mailboxColumns: ColumnsType<InboundMailbox> = [
    {
      title: t('inbox.mailboxAddress'), dataIndex: 'address', render: (value: string, item) => <Space direction="vertical" size={0}><Typography.Text strong>{value}</Typography.Text>{item.displayName ? <Typography.Text type="secondary">{item.displayName}</Typography.Text> : null}</Space>
    },
    { title: t('common.status'), dataIndex: 'status', width: 120, render: (value: string) => <StatusPill tone={value === 'active' ? 'success' : 'warning'}>{value}</StatusPill> },
    { title: t('inbox.forwardTo'), dataIndex: 'forwardTo', render: (value: string[], item) => value?.length ? <Space direction="vertical" size={2}><Typography.Text>{value.join(', ')}</Typography.Text><Tag>{item.keepForwarded ? t('inbox.keepForwarded') : t('inbox.forwardOnly')}</Tag></Space> : '—' },
    { title: t('inbox.unread'), dataIndex: 'unreadCount', width: 90 },
    { title: t('inbox.messageCount'), dataIndex: 'messageCount', width: 100 },
    {
      title: t('common.actions'), width: 230, render: (_, item) => <Space wrap><Button icon={<SettingOutlined />} onClick={() => setClientConfig(buildMailboxClientConfig(item, config))}>{t('inbox.clientConfig')}</Button><Button icon={<MailOutlined />} onClick={() => navigate(`/integrations/webhooks?mailboxId=${item.id}`)}>{t('inbox.mailboxWebhooks')}</Button></Space>
    }
  ];

  const routeColumns: ColumnsType<Domain> = [
    { title: t('domains.domain'), dataIndex: 'domain', render: (value: string) => <Typography.Text strong>{value}</Typography.Text> },
    { title: t('inbox.catchAllAddress'), dataIndex: 'catchAllAddress', render: (value?: string) => value ? <Tag color={value === '/dev/null' ? 'default' : 'blue'}>{value}</Tag> : <Tag>{t('inbox.catchAllDisabled')}</Tag> },
    { title: t('common.actions'), width: 130, render: (_, domain) => <Button icon={<SettingOutlined />} onClick={() => openCatchAll(domain)}>{t('common.edit')}</Button> }
  ];

  if (loading) return <SectionCard><Skeleton active paragraph={{ rows: 12 }} /></SectionCard>;

  return (
    <Space direction="vertical" size={20} className="full-width">
      <PageHeader
        title={t('inbox.title')}
        subtitle={t('inbox.subtitle')}
        extra={workspace === 'routing' ? <Button type="primary" icon={<PlusOutlined />} disabled={!domains.length} onClick={openCreateMailbox} style={{ minHeight: 44 }}>{t('inbox.createMailbox')}</Button> : null}
      />
      {loadError ? <Alert type="error" showIcon message={loadError} action={<Button icon={<ReloadOutlined />} onClick={() => void loadBase()}>{t('common.refresh')}</Button>} /> : null}
      {config?.submission?.inboundEnabled === false ? <Alert type="warning" showIcon message={t('inbox.inboundDisabled')} /> : null}
      <Tabs
        activeKey={workspace}
        onChange={switchWorkspace}
        items={[
          { key: 'messages', label: <Space><MailOutlined />{locale.startsWith('en') ? 'Mail' : '邮件'}</Space> },
          { key: 'routing', label: <Space><SettingOutlined />{locale.startsWith('en') ? 'Mailboxes & routing' : '邮箱与路由'}</Space> }
        ]}
      />
      {workspace === 'messages' ? (
        mailboxes.length ? (
          <div style={{ display: 'grid', gridTemplateColumns: isDesktop ? '220px minmax(320px, 380px) minmax(0, 1fr)' : screens.md ? '220px minmax(0, 1fr)' : 'minmax(0, 1fr)', gap: 16, minWidth: 0 }}>
            {screens.md ? <FolderPane mailboxes={mailboxes} selectedMailboxId={selectedMailboxId} folders={folders} activeFolder={folder} onMailbox={selectMailbox} onFolder={selectFolder} locale={locale} /> : null}
            <Card styles={{ body: { padding: 0, minWidth: 0 } }}>
              <div style={{ padding: 12, borderBottom: '1px solid #EAECF0' }}>
                {!screens.md ? (
                  <Space direction="vertical" size={8} className="full-width" style={{ marginBottom: 8 }}>
                    <Select value={selectedMailboxId || undefined} onChange={selectMailbox} options={mailboxes.map((item) => ({ value: item.id, label: item.address }))} className="full-width" aria-label={t('inbox.mailboxFilter')} />
                    <Select value={folder} onChange={selectFolder} options={folders.map((item) => ({ value: item.name, label: `${folderLabel(item.name, locale)} (${item.unreadCount})` }))} className="full-width" aria-label={locale.startsWith('en') ? 'Folder' : '文件夹'} />
                  </Space>
                ) : null}
                <Space.Compact block>
                  <Input value={searchDraft} allowClear prefix={<SearchOutlined />} placeholder={t('inbox.searchPlaceholder')} onChange={(event) => setSearchDraft(event.target.value)} onPressEnter={() => updateSearch({ q: searchDraft.trim(), page: 1 })} />
                  <Button aria-label={t('common.refresh')} icon={<ReloadOutlined />} loading={messagesLoading} onClick={() => void loadMessages()} />
                </Space.Compact>
                <Select
                  value={readFilter}
                  onChange={(value) => updateSearch({ read: value === 'all' ? null : value, page: 1 })}
                  style={{ width: '100%', marginTop: 8 }}
                  aria-label={locale.startsWith('en') ? 'Read state' : '阅读状态'}
                  options={[{ value: 'all', label: locale.startsWith('en') ? 'All mail' : '全部邮件' }, { value: 'unread', label: t('inbox.unread') }, { value: 'read', label: t('inbox.read') }]}
                />
              </div>
              {messagesError ? <Alert type="error" showIcon message={messagesError} action={<Button onClick={() => void loadMessages()}>{t('common.refresh')}</Button>} /> : null}
              <MessageList items={messages} loading={messagesLoading} activeId={routeMessageId} onOpen={openMessage} locale={locale} t={t} />
              {total > pageSize ? <div style={{ padding: 12, display: 'flex', justifyContent: 'center' }}><Pagination size="small" current={page} pageSize={pageSize} total={total} showSizeChanger={false} onChange={(value) => updateSearch({ page: value })} /></div> : null}
            </Card>
            {isDesktop ? <Card styles={{ body: { padding: 20, minWidth: 0 } }}><MessageDetail message={selectedMessage} loading={detailLoading} error={detailError} activeTab={messageTab} onTabChange={changeMessageTab} onCopy={copyValue} t={t} /></Card> : null}
          </div>
        ) : <EmptyState description={locale.startsWith('en') ? 'No receiving mailbox has been created yet.' : '尚未创建收信邮箱。'} action={<Button icon={<PlusOutlined />} disabled={!domains.length} onClick={() => { switchWorkspace('routing'); openCreateMailbox(); }}>{t('inbox.createMailbox')}</Button>} />
      ) : (
        <Space direction="vertical" size={20} className="full-width">
          <SectionCard title={t('inbox.mailboxes')} extra={<StatusPill tone="neutral">{mailboxes.length}</StatusPill>}>
            {mailboxes.length ? (screens.md ? <Table rowKey="id" columns={mailboxColumns} dataSource={mailboxes} scroll={{ x: 940 }} /> : <List dataSource={mailboxes} renderItem={(item) => <List.Item><Card size="small" className="full-width" title={item.address}><Space direction="vertical" className="full-width"><Typography.Text type="secondary">{item.messageCount} {t('inbox.messageCount')} · {item.unreadCount} {t('inbox.unread')}</Typography.Text><Space wrap><Button onClick={() => setClientConfig(buildMailboxClientConfig(item, config))}>{t('inbox.clientConfig')}</Button><Button onClick={() => navigate(`/integrations/webhooks?mailboxId=${item.id}`)}>{t('inbox.mailboxWebhooks')}</Button></Space></Space></Card></List.Item>} />) : <EmptyState description={t('inbox.noDomain')} action={<Button icon={<PlusOutlined />} disabled={!domains.length} onClick={openCreateMailbox}>{t('inbox.createMailbox')}</Button>} />}
          </SectionCard>
          <SectionCard title={t('inbox.domainRoutes')} extra={<StatusPill tone="neutral">{domains.length}</StatusPill>}>
            {domains.length ? (screens.md ? <Table rowKey="id" columns={routeColumns} dataSource={domains} /> : <List dataSource={domains} renderItem={(domain) => <List.Item actions={[<Button key="edit" onClick={() => openCatchAll(domain)}>{t('common.edit')}</Button>]}><List.Item.Meta title={domain.domain} description={domain.catchAllAddress || t('inbox.catchAllDisabled')} /></List.Item>} />) : <EmptyState description={t('inbox.noDomain')} action={<Button onClick={() => navigate('/domains?create=1')}>{t('common.addDomain')}</Button>} />}
          </SectionCard>
        </Space>
      )}

      {!isDesktop ? <Drawer title={t('inbox.messageDetail')} width={screens.md ? 680 : '100%'} open={Boolean(routeMessageId)} onClose={closeMessage}><MessageDetail message={selectedMessage} loading={detailLoading} error={detailError} activeTab={messageTab} onTabChange={changeMessageTab} onCopy={copyValue} t={t} /></Drawer> : null}

      <Drawer title={t('inbox.createMailbox')} width={560} open={mailboxOpen} onClose={() => setMailboxOpen(false)} destroyOnHidden footer={<Space style={{ display: 'flex', justifyContent: 'flex-end' }}><Button onClick={() => setMailboxOpen(false)}>{t('common.cancel')}</Button><Button type="primary" loading={actionKey === 'mailbox:create'} onClick={() => void createMailbox()}>{t('inbox.createMailbox')}</Button></Space>}>
        {!domains.length ? <Alert type="warning" showIcon message={t('inbox.noDomain')} /> : (
          <Form form={mailboxForm} layout="vertical">
            <Space.Compact block>
              <Form.Item name="localPart" label={t('inbox.localPart')} rules={[{ required: true, message: t('inbox.localPartRequired') }, { pattern: /^[^@\s]+$/, message: t('inbox.localPartInvalid') }]} style={{ flex: 1 }}><Input autoComplete="off" /></Form.Item>
              <Form.Item name="domain" label={t('domains.domain')} rules={[{ required: true, message: t('inbox.domainRequired') }]} style={{ minWidth: 220 }}><Select options={domains.map((domain) => ({ value: domain.domain, label: `@${domain.domain}` }))} /></Form.Item>
            </Space.Compact>
            <Form.Item name="displayName" label={t('inbox.displayName')}><Input /></Form.Item>
            <Form.Item name="password" label={t('inbox.password')} rules={[{ required: true, message: t('inbox.passwordRequired') }, { min: 8, message: t('inbox.passwordMin') }]}><Input.Password autoComplete="new-password" /></Form.Item>
            <Button onClick={() => mailboxForm.setFieldValue('password', generateMailboxPassword())}>{t('inbox.generatePassword')}</Button>
            <Form.Item name="quotaMb" label={`${t('inbox.quotaMb')} (MB)`} style={{ marginTop: 20 }}><InputNumber min={1} className="full-width" /></Form.Item>
            <Form.Item name="aliases" label={t('inbox.aliases')} extra={t('inbox.aliasesExtra')}><Input.TextArea rows={3} /></Form.Item>
            <Form.Item name="forwardTo" label={t('inbox.forwardTo')} extra={t('inbox.forwardToExtra')}><Input.TextArea rows={3} /></Form.Item>
            <Form.Item name="keepForwarded" valuePropName="checked"><Checkbox>{t('inbox.keepForwarded')}</Checkbox></Form.Item>
          </Form>
        )}
      </Drawer>

      <Modal title={t('inbox.catchAllTitle')} open={Boolean(catchAllDomain)} confirmLoading={actionKey.startsWith('catch-all:')} onCancel={() => setCatchAllDomain(null)} onOk={() => void saveCatchAll()}>
        <Form form={catchAllForm} layout="vertical"><Form.Item name="catchAllAddress" label={t('inbox.catchAllAddress')} extra={t('inbox.catchAllExtra')}><Input placeholder="catchall@example.com / /dev/null" /></Form.Item></Form>
      </Modal>

      <Modal title={t('inbox.clientConfig')} open={Boolean(clientConfig)} width={720} footer={<Button type="primary" onClick={() => setClientConfig(null)}>{t('common.confirm')}</Button>} onCancel={() => setClientConfig(null)}>
        {clientConfig ? <ClientConfigView config={clientConfig} onCopy={copyValue} t={t} /> : null}
      </Modal>

    </Space>
  );
}

function FolderPane({ mailboxes, selectedMailboxId, folders, activeFolder, onMailbox, onFolder, locale }: { mailboxes: InboundMailbox[]; selectedMailboxId: number | null; folders: InboundFolder[]; activeFolder: string; onMailbox: (id: number) => void; onFolder: (name: string) => void; locale: string }) {
  return (
    <Card styles={{ body: { padding: 8 } }}>
      <Select value={selectedMailboxId || undefined} onChange={onMailbox} options={mailboxes.map((item) => ({ value: item.id, label: item.address }))} className="full-width" style={{ marginBottom: 12 }} />
      <Space direction="vertical" size={2} className="full-width">
        {folders.map((item) => (
          <Button key={item.name} type={activeFolder === item.name ? 'primary' : 'text'} icon={folderIcon(item.name)} onClick={() => onFolder(item.name)} style={{ width: '100%', minHeight: 44, display: 'flex', alignItems: 'center', justifyContent: 'flex-start' }}>
            <span style={{ flex: 1, textAlign: 'left' }}>{folderLabel(item.name, locale)}</span>
            {item.unreadCount ? <Badge count={item.unreadCount} size="small" /> : <Typography.Text type="secondary">{item.messageCount}</Typography.Text>}
          </Button>
        ))}
      </Space>
    </Card>
  );
}

function MessageList({ items, loading, activeId, onOpen, locale, t }: { items: MailMessage[]; loading: boolean; activeId: number | null; onOpen: (item: MailMessage) => void; locale: string; t: (key: string) => string }) {
  if (loading) return <div style={{ padding: 16 }}><Skeleton active paragraph={{ rows: 8 }} /></div>;
  if (!items.length) return <EmptyState description={locale.startsWith('en') ? 'No messages match this folder and filter.' : '当前文件夹和筛选条件下没有邮件。'} />;
  return (
    <List dataSource={items} split renderItem={(item) => (
      <List.Item style={{ padding: 0 }}>
        <button
          type="button"
          aria-label={`${item.subject || t('inbox.noSubject')} · ${item.sender}`}
          onClick={() => onOpen(item)}
          style={{ width: '100%', minHeight: 88, padding: '12px 16px', border: 0, textAlign: 'left', background: activeId === item.id ? '#EEF2FF' : item.read ? '#FFFFFF' : '#F8FAFF', cursor: 'pointer' }}
        >
          <Space direction="vertical" size={4} style={{ width: '100%' }}>
            <Space style={{ width: '100%', justifyContent: 'space-between' }}>
              <Typography.Text strong={!item.read} ellipsis style={{ maxWidth: '65%' }}>{item.sender || '—'}</Typography.Text>
              <Typography.Text type="secondary" style={{ fontSize: 12 }}>{formatCompactTime(item.receivedAt)}</Typography.Text>
            </Space>
            <Typography.Text strong={!item.read} ellipsis>{item.subject || t('inbox.noSubject')}</Typography.Text>
            <Typography.Text type="secondary" ellipsis>{item.preview || '—'}</Typography.Text>
          </Space>
        </button>
      </List.Item>
    )} />
  );
}

function MessageDetail({ message, loading, error, activeTab, onTabChange, onCopy, t }: { message: MailMessage | null; loading: boolean; error: string; activeTab: MessageTab; onTabChange: (tab: MessageTab) => void; onCopy: (value: string) => void; t: (key: string) => string }) {
  if (loading) return <Skeleton active paragraph={{ rows: 12 }} />;
  if (error) return <Alert type="error" showIcon message={error} />;
  if (!message) return <EmptyState description={t('inbox.messageDetail')} />;
  return (
    <Space direction="vertical" size={20} className="full-width">
      <div><Typography.Title level={4} style={{ marginBottom: 4 }}>{message.subject || t('inbox.noSubject')}</Typography.Title><Typography.Text type="secondary">{formatOptionalTime(message.receivedAt)}</Typography.Text></div>
      <Descriptions column={1} size="small">
        <Descriptions.Item label={t('inbox.sender')}>{message.sender || '—'}</Descriptions.Item>
        <Descriptions.Item label={t('inbox.recipients')}>{message.recipients.join(', ') || '—'}</Descriptions.Item>
        <Descriptions.Item label={t('logs.messageId')}><Typography.Text code copyable={{ onCopy: () => onCopy(message.messageId) }}>{message.messageId || '—'}</Typography.Text></Descriptions.Item>
      </Descriptions>
      <Tabs activeKey={activeTab} onChange={(key) => onTabChange(normalizeMessageTab(key))} items={[
        { key: 'text', label: t('inbox.textBody'), children: message.textBody ? <pre className="inbox-message-body">{message.textBody}</pre> : <EmptyState description={t('inbox.noTextBody')} /> },
        { key: 'html', label: t('inbox.htmlBody'), children: message.htmlBody ? <CodeBlock value={message.htmlBody} onCopy={onCopy} /> : <EmptyState description={t('inbox.noHtmlBody')} /> },
        { key: 'raw', label: t('inbox.rawMessage'), children: message.rawMessage ? <CodeBlock value={message.rawMessage} onCopy={onCopy} /> : <EmptyState description={t('inbox.noRawMessage')} /> }
      ]} />
    </Space>
  );
}

function normalizeMessageTab(value: string | null): MessageTab {
  return value === 'html' || value === 'raw' ? value : 'text';
}

function inboxListPath(searchParams: URLSearchParams, message: MailMessage | null) {
  const next = new URLSearchParams(searchParams);
  next.delete('tab');
  if (message && (!next.has('mailboxId') || !next.has('folder'))) {
    next.set('mailboxId', String(message.mailboxId));
    next.set('folder', message.folder || 'INBOX');
  }
  const suffix = next.toString();
  return `/inbox${suffix ? `?${suffix}` : ''}`;
}

function ClientConfigView({ config, onCopy, t }: { config: MailboxClientConfig; onCopy: (value: string) => void; t: (key: string) => string }) {
  const sections = [
    { key: 'imap', label: t('inbox.incomingConfig'), value: config.incoming },
    ...(config.pop3 ? [{ key: 'pop3', label: t('inbox.pop3Config'), value: config.pop3 }] : []),
    { key: 'smtp', label: t('inbox.outgoingConfig'), value: config.outgoing }
  ];
  return (
    <Space direction="vertical" size={16} className="full-width">
      <Alert type="info" showIcon message={t('inbox.clientConfigHelpSummary')} />
      <Descriptions bordered column={1} size="small"><Descriptions.Item label={t('inbox.configUsername')}>{configValue(config.username, onCopy)}</Descriptions.Item><Descriptions.Item label={t('inbox.configPassword')}>{config.password ? configValue(config.password, onCopy) : <Typography.Text type="secondary">{t('inbox.passwordNotShown')}</Typography.Text>}</Descriptions.Item></Descriptions>
      <Tabs items={sections.map((section) => ({ key: section.key, label: section.label, children: <Descriptions bordered column={1} size="small"><Descriptions.Item label={t('inbox.configHost')}>{configValue(section.value.host, onCopy)}</Descriptions.Item><Descriptions.Item label={t('inbox.configPort')}>{configValue(section.value.port, onCopy)}</Descriptions.Item><Descriptions.Item label={t('inbox.configSecurity')}>{section.value.security}</Descriptions.Item><Descriptions.Item label={t('inbox.configAuthMethod')}>{section.value.authMethod}</Descriptions.Item></Descriptions> }))} />
    </Space>
  );
}

function configValue(value: string | number, onCopy: (value: string) => void) {
  return <Space><Typography.Text code>{value}</Typography.Text><Button aria-label="Copy" icon={<CopyOutlined />} onClick={() => void onCopy(String(value))} /></Space>;
}

function fallbackFolders(mailbox?: InboundMailbox): InboundFolder[] {
  return standardFolders.map((name) => ({ name, specialUse: folderSpecialUse(name), messageCount: name === 'INBOX' ? mailbox?.messageCount || 0 : 0, unreadCount: name === 'INBOX' ? mailbox?.unreadCount || 0 : 0 }));
}

function folderSpecialUse(name: string) {
  const values: Record<string, string | null> = {
    INBOX: null,
    Sent: '\\Sent',
    Drafts: '\\Drafts',
    Trash: '\\Trash',
    Junk: '\\Junk',
    Archive: '\\Archive'
  };
  return values[name] ?? null;
}

function folderLabel(name: string, locale: string) {
  if (locale.startsWith('en')) return name;
  return { INBOX: '收件箱', Sent: '已发送', Drafts: '草稿', Trash: '已删除', Junk: '垃圾邮件', Archive: '归档' }[name] || name;
}

function folderIcon(name: string) {
  if (name === 'INBOX') return <InboxOutlined />;
  if (name === 'Sent') return <SendOutlined />;
  if (name === 'Drafts') return <FileTextOutlined />;
  if (name === 'Trash') return <DeleteOutlined />;
  if (name === 'Junk') return <WarningOutlined />;
  if (name === 'Archive') return <ContainerOutlined />;
  return <FolderOutlined />;
}

function buildMailboxClientConfig(mailbox: InboundMailbox, config: RuntimeConfig | null, password = ''): MailboxClientConfig {
  const smtpPort = preferredPort(config?.submission?.ports || [], [587, 465]);
  const imapPort = preferredPort(config?.mailAccess?.imap.ports || [], [993, 143]);
  const pop3Port = preferredPort(config?.mailAccess?.pop3.ports || [], [995, 110]);
  const accessHost = config?.mailAccess?.host || config?.submission?.host || config?.mailHostname || mailbox.domain;
  return {
    username: mailbox.address,
    password,
    incoming: { protocol: 'IMAP', host: accessHost, port: imapPort?.port || 143, security: imapPort?.protocol || 'IMAP + STARTTLS', authMethod: 'Normal password', username: mailbox.address, password },
    pop3: { protocol: 'POP3', host: accessHost, port: pop3Port?.port || 110, security: pop3Port?.protocol || 'POP3 + STLS', authMethod: 'Normal password', username: mailbox.address, password },
    outgoing: { protocol: 'SMTP', host: config?.submission?.host || config?.mailHostname || mailbox.domain, port: smtpPort?.port || 587, security: smtpPort?.protocol || 'SMTP + STARTTLS', authMethod: 'Normal password', username: mailbox.address, password }
  };
}

function preferredPort(ports: Array<{ port: number; protocol: string }>, preferred: number[]) {
  for (const port of preferred) {
    const match = ports.find((item) => item.port === port);
    if (match) return match;
  }
  return ports[0] || null;
}

function generateMailboxPassword() {
  const bytes = new Uint8Array(14);
  globalThis.crypto.getRandomValues(bytes);
  return Array.from(bytes, (value) => (value % 36).toString(36)).join('');
}

function formatCompactTime(value: string) {
  const date = new Date(value);
  const now = new Date();
  return date.toDateString() === now.toDateString() ? date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : date.toLocaleDateString();
}

function formatOptionalTime(value?: string | null) {
  return value ? new Date(value).toLocaleString() : '—';
}
