import { memo, useCallback, useEffect, useMemo, useState, type ReactNode } from 'react';
import { Alert, Button, Card, Collapse, Empty, List, Modal, Popconfirm, Progress, Skeleton, Space, Tabs, Tag, Typography, Upload, message, theme } from 'antd';
import { GithubOutlined, LinkOutlined, UploadOutlined, DownloadOutlined, ReloadOutlined } from '@ant-design/icons';
import { pluginsApi, type PluginItem } from '../api/plugins';
import { getAppByKey, reloadPluginApps } from '../apps/registry';
import { useI18n } from '../i18n';
import { useAppWindows } from '../contexts/AppWindowsContext';
import { getPluginAssetUrl } from '../plugins/runtime';
import { fetchFoxelCoreAppDetail, fetchFoxelCoreApps, downloadFoxelCoreApp, type FoxelCoreApp, type FoxelCoreAppDetail } from '../api/pluginCenter';
import type { UploadFile } from 'antd';
import ReactMarkdown from 'react-markdown';

type InstallStatus = 'pending' | 'installing' | 'success' | 'failed' | 'skipped';
type InstallState = Partial<{
  status: InstallStatus;
  message: string;
  errors: string[];
}>;

type CenterCardProps = {
  iconUrl: string;
  iconAlt: string;
  name: ReactNode;
  version?: ReactNode;
  description?: ReactNode;
  metaLeft?: ReactNode;
  metaRight?: ReactNode;
  pills?: string[];
  footerLeft?: ReactNode;
  footerRight?: ReactNode;
  onIconClick?: () => void;
  onTitleClick?: () => void;
};

function buildCardPills(items: Array<string | null | undefined> | null | undefined, fallback?: string) {
  const cleaned = (items || []).map(v => (v || '').trim()).filter(Boolean);
  if (cleaned.length === 0) return fallback ? [fallback] : [];
  if (cleaned.length <= 2) return cleaned;
  return [cleaned[0], `+${cleaned.length - 1}`];
}

const CenterCard = memo(function CenterCard(props: CenterCardProps) {
  const {
    iconUrl,
    iconAlt,
    name,
    version,
    description,
    metaLeft,
    metaRight,
    pills,
    footerLeft,
    footerRight,
    onIconClick,
    onTitleClick,
  } = props;

  return (
    <div className="fx-center-card">
      <div className="fx-center-card-hero">
        <div
          className="fx-center-card-iconbox"
          style={onIconClick ? { cursor: 'pointer' } : undefined}
          role={onIconClick ? 'button' : undefined}
          tabIndex={onIconClick ? 0 : undefined}
          onClick={onIconClick}
          onKeyDown={onIconClick ? (e) => {
            if (e.key === 'Enter' || e.key === ' ') {
              e.preventDefault();
              onIconClick();
            }
          } : undefined}
        >
          <img
            className="fx-center-card-icon"
            src={iconUrl}
            alt={iconAlt}
            loading="lazy"
            onError={(e) => { (e.currentTarget as HTMLImageElement).src = '/logo.svg'; }}
          />
        </div>
        {pills && pills.length > 0 ? (
          <div className="fx-center-card-pills">
            {pills.slice(0, 2).map(pill => (
              <span key={pill} className="fx-center-pill">
                {pill}
              </span>
            ))}
          </div>
        ) : null}
      </div>

      <div className="fx-center-card-body">
        <div className="fx-center-card-titleRow">
          <div
            className="fx-center-card-title"
            title={typeof name === 'string' ? name : undefined}
            style={onTitleClick ? { cursor: 'pointer' } : undefined}
            role={onTitleClick ? 'button' : undefined}
            tabIndex={onTitleClick ? 0 : undefined}
            onClick={onTitleClick}
            onKeyDown={onTitleClick ? (e) => {
              if (e.key === 'Enter' || e.key === ' ') {
                e.preventDefault();
                onTitleClick();
              }
            } : undefined}
          >
            {name}
          </div>
          {version ? <div className="fx-center-card-version">{version}</div> : null}
        </div>

        {metaLeft || metaRight ? (
          <div className="fx-center-card-metaRow">
            <span className="fx-center-card-metaLeft">{metaLeft}</span>
            <span className="fx-center-card-metaRight">{metaRight}</span>
          </div>
        ) : null}

        {description ? <div className="fx-center-card-desc">{description}</div> : null}
      </div>

      {footerLeft || footerRight ? (
        <div className="fx-center-card-footer">
          <div className="fx-center-card-footerLeft">{footerLeft}</div>
          <div className="fx-center-card-footerRight">{footerRight}</div>
        </div>
      ) : null}
    </div>
  );
});

const PluginsPage = memo(function PluginsPage() {
  const [data, setData] = useState<PluginItem[]>([]);
  const [installing, setInstalling] = useState(false);
  const [loading, setLoading] = useState(false);
  const [q, setQ] = useState('');
  const [tab, setTab] = useState<'installed' | 'discover'>('installed');
  const [fileList, setFileList] = useState<UploadFile[]>([]);
  const [installModalOpen, setInstallModalOpen] = useState(false);
  const [installDone, setInstallDone] = useState(false);
  const [installStopReason, setInstallStopReason] = useState<string | undefined>(undefined);
  const [installFileState, setInstallFileState] = useState<Record<string, InstallState>>({});
  
  // 应用发现相关状态
  const [discoveryApps, setDiscoveryApps] = useState<FoxelCoreApp[]>([]);
  const [discoveryLoading, setDiscoveryLoading] = useState(false);
  const [discoveryError, setDiscoveryError] = useState<string | undefined>(undefined);
  const [discoverySearch, setDiscoverySearch] = useState('');
  const [downloadingApps, setDownloadingApps] = useState<Set<string>>(new Set());

  // 应用详情弹窗状态
  const [detailOpen, setDetailOpen] = useState(false);
  const [detailKey, setDetailKey] = useState<string | null>(null);
  const [detailSummary, setDetailSummary] = useState<FoxelCoreApp | null>(null);
  const [detailLoading, setDetailLoading] = useState(false);
  const [detailError, setDetailError] = useState<string | undefined>(undefined);
  const [detailApp, setDetailApp] = useState<FoxelCoreAppDetail | null>(null);
  const [detailReloadId, setDetailReloadId] = useState(0);
  
  const { token } = theme.useToken();
  const { t, lang } = useI18n();
  const { openApp } = useAppWindows();

  const reload = useCallback(async () => {
    try { setLoading(true); setData(await pluginsApi.list()); } finally { setLoading(false); }
  }, []);

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

  // 加载应用发现列表
  const loadDiscoveryApps = useCallback(async () => {
    try {
      setDiscoveryLoading(true);
      setDiscoveryError(undefined);
      const apps = await fetchFoxelCoreApps();
      setDiscoveryApps(apps);
    } catch (e: any) {
      setDiscoveryError(e?.message || t('Failed to load apps'));
    } finally {
      setDiscoveryLoading(false);
    }
  }, [t]);

  useEffect(() => {
    if (tab === 'discover' && discoveryApps.length === 0 && !discoveryError) {
      void loadDiscoveryApps();
    }
  }, [discoveryApps.length, discoveryError, loadDiscoveryApps, tab]);

  const closeDetailModal = () => {
    setDetailOpen(false);
    setDetailKey(null);
    setDetailSummary(null);
    setDetailLoading(false);
    setDetailError(undefined);
    setDetailApp(null);
  };

  const openDiscoveryAppDetail = useCallback((app: FoxelCoreApp) => {
    setDetailOpen(true);
    setDetailKey(app.key);
    setDetailSummary(app);
    setDetailError(undefined);
    setDetailApp(null);
  }, []);

  useEffect(() => {
    if (!detailOpen || !detailKey) return;

    let cancelled = false;
    const currentKey = detailKey;

    const run = async () => {
      setDetailLoading(true);
      setDetailError(undefined);
      try {
        const app = await fetchFoxelCoreAppDetail(currentKey);
        if (cancelled) return;
        setDetailApp(app);
      } catch (e: any) {
        if (cancelled) return;
        setDetailError(e?.message || t('Load failed'));
      } finally {
        if (!cancelled) setDetailLoading(false);
      }
    };

    void run();

    return () => {
      cancelled = true;
    };
  }, [detailKey, detailOpen, detailReloadId, t]);

  const resetInstallUi = () => {
    setInstallDone(false);
    setInstallStopReason(undefined);
    setInstallFileState({});
  };

  const closeInstallModal = () => {
    if (installing) return;
    setInstallModalOpen(false);
    setFileList([]);
    resetInstallUi();
  };

  const removeSelectedFile = (uid: string) => {
    if (installing || installDone) return;
    setFileList(prev => {
      const next = prev.filter(f => f.uid !== uid);
      if (next.length === 0) {
        setInstallModalOpen(false);
        resetInstallUi();
      }
      return next;
    });
    setInstallFileState(prev => {
      const next = { ...prev };
      delete next[uid];
      return next;
    });
  };

  const handleInstall = async () => {
    if (fileList.length === 0) {
      message.warning(t('Please select a .foxpkg file'));
      return;
    }

    const setState = (uid: string, patch: InstallState) => {
      setInstallFileState(prev => ({
        ...prev,
        [uid]: { ...(prev[uid] || {}), ...patch },
      }));
    };

    try {
      setInstalling(true);
      setInstallDone(false);
      setInstallStopReason(undefined);

      let stopReason: string | undefined;
      for (let i = 0; i < fileList.length; i++) {
        const file = fileList[i];
        const uid = file.uid;
        setState(uid, { status: 'installing' });

        if (!file.originFileObj) {
          stopReason = t('Invalid file');
          setState(uid, { status: 'failed', message: stopReason });
          for (let j = i + 1; j < fileList.length; j++) {
            setState(fileList[j].uid, { status: 'skipped' });
          }
          break;
        }

        try {
          const result = await pluginsApi.install(file.originFileObj);
          if (result.success) {
            setState(uid, {
              status: 'success',
              message: result.message || t('Installed successfully'),
              errors: result.errors,
            });
            continue;
          }

          stopReason = result.message || t('Installation failed');
          setState(uid, {
            status: 'failed',
            message: stopReason,
            errors: result.errors,
          });
          for (let j = i + 1; j < fileList.length; j++) {
            setState(fileList[j].uid, { status: 'skipped' });
          }
          break;
        } catch (e: any) {
          stopReason = e?.message || t('Installation failed');
          setState(uid, { status: 'failed', message: stopReason });
          for (let j = i + 1; j < fileList.length; j++) {
            setState(fileList[j].uid, { status: 'skipped' });
          }
          break;
        }
      }

      await reload();
      await reloadPluginApps();

      setInstallStopReason(stopReason);
      setInstallDone(true);
      if (stopReason) {
        message.error(stopReason);
      } else {
        message.success(t('Installed successfully'));
      }
    } catch (e: any) {
      const msg = e?.message || t('Installation failed');
      setInstallStopReason(msg);
      setInstallDone(true);
      message.error(msg);
    } finally {
      setInstalling(false);
    }
  };

  /**
   * 解析插件图标 URL
   */
  const resolvePluginIcon = (p: PluginItem): string => {
    if (!p.icon) return '/logo.svg';
    if (p.icon.startsWith('http://') || p.icon.startsWith('https://') || p.icon.startsWith('/')) {
      return p.icon;
    }
    return getPluginAssetUrl(p.key, p.icon);
  };

  /**
   * 按当前语言解析插件文案（name/description）
   */
  const resolvePluginTexts = useCallback((p: PluginItem): { name?: string; description?: string } => {
    const i18n = (p.manifest as any)?.i18n as any;
    const entry = i18n?.[lang] as any;
    return {
      name: entry?.name || p.name || undefined,
      description: entry?.description || p.description || undefined,
    };
  }, [lang]);

  const filtered = useMemo(() => {
    const s = q.trim().toLowerCase();
    if (!s) return data;
    return data.filter(p => (
      (resolvePluginTexts(p).name || '').toLowerCase().includes(s)
      || (p.author || '').toLowerCase().includes(s)
      || (p.key || '').toLowerCase().includes(s)
      || (resolvePluginTexts(p).description || '').toLowerCase().includes(s)
      || (p.supported_exts || []).some(e => e.toLowerCase().includes(s))
    ));
  }, [data, q, resolvePluginTexts]);

  // 过滤应用发现列表
  const filteredDiscoveryApps = useMemo(() => {
    const s = discoverySearch.trim().toLowerCase();
    if (!s) return discoveryApps;
    return discoveryApps.filter(app => {
      const name = (lang === 'zh' ? app.name.zh : app.name.en).toLowerCase();
      const desc = (lang === 'zh' ? app.description.zh : app.description.en).toLowerCase();
      const author = (app.author || '').toLowerCase();
      const tags = lang === 'zh' ? app.tags.zh : app.tags.en;
      return (
        name.includes(s) ||
        desc.includes(s) ||
        author.includes(s) ||
        tags.some(tag => tag.toLowerCase().includes(s)) ||
        app.key.toLowerCase().includes(s)
      );
    });
  }, [discoveryApps, discoverySearch, lang]);

  // 检查应用是否已安装
  const isAppInstalled = (appKey: string) => {
    return data.some(p => p.key === appKey);
  };

  // 下载并安装应用
  const handleDownloadAndInstall = async (app: Pick<FoxelCoreApp, 'key' | 'version' | 'downloadUrl'>) => {
    if (downloadingApps.has(app.key)) return;
    
    try {
      setDownloadingApps(prev => new Set(prev).add(app.key));
      message.loading({ content: t('Downloading'), key: app.key, duration: 0 });
      
      const file = await downloadFoxelCoreApp(app);
      message.destroy(app.key);
      message.loading({ content: t('Installing'), key: app.key, duration: 0 });
      
      const result = await pluginsApi.install(file);
      message.destroy(app.key);
      
      if (result.success) {
        message.success(t('Installed successfully'));
        await reload();
        await reloadPluginApps();
      } else {
        message.error(result.message || t('Installation failed'));
      }
    } catch (e: any) {
      message.destroy(app.key);
      message.error(e?.message || t('Installation failed'));
    } finally {
      setDownloadingApps(prev => {
        const next = new Set(prev);
        next.delete(app.key);
        return next;
      });
    }
  };

  const renderCard = (p: PluginItem) => {
    const texts = resolvePluginTexts(p);
    const icon = resolvePluginIcon(p);
    const name = texts.name || `${t('Plugin')} ${p.key}`;
    const appKey = `plugin:${p.key}`;
    const app = getAppByKey(appKey);
    const canOpenApp = !!p.open_app;
    return (
      <CenterCard
        key={p.key}
        iconUrl={icon}
        iconAlt={name}
        name={name}
        version={p.version || undefined}
        description={texts.description || t('No description')}
        metaLeft={p.author || undefined}
        metaRight={p.key}
        pills={buildCardPills(p.supported_exts, t('Any'))}
        footerLeft={(p.github || p.website) ? (
          <div className="fx-center-card-links">
            {p.github ? (
              <a className="fx-center-card-iconLink" href={p.github || undefined} target="_blank" rel="noreferrer" title="GitHub" aria-label="GitHub">
                <GithubOutlined />
              </a>
            ) : null}
            {p.website ? (
              <a className="fx-center-card-iconLink" href={p.website || undefined} target="_blank" rel="noreferrer" title={t('Website')} aria-label={t('Website')}>
                <LinkOutlined />
              </a>
            ) : null}
          </div>
        ) : null}
        footerRight={
          <div className="fx-center-card-actions">
            <Button
              type="primary"
              size="small"
              disabled={!canOpenApp}
              onClick={async () => {
                let target = app || getAppByKey(appKey);
                if (!target) {
                  await reloadPluginApps();
                  target = getAppByKey(appKey);
                }
                if (target?.openAppComponent) openApp(target);
              }}
            >
              {t('Open App')}
            </Button>
            <Popconfirm title={t('Confirm delete this plugin?')} onConfirm={async () => { await pluginsApi.remove(p.key); await reload(); await reloadPluginApps(); }}>
              <Button type="text" danger size="small">{t('Uninstall')}</Button>
            </Popconfirm>
          </div>
        }
      />
    );
  };

  return (
    <div style={{ height: 'calc(100vh - 88px)', display: 'flex', flexDirection: 'column', minHeight: 0, overflow: 'hidden' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12, flexWrap: 'wrap' }}>
        <Upload
          accept=".foxpkg"
          multiple
          disabled={installing}
          fileList={fileList}
          onChange={({ fileList: newFileList }) => {
            setFileList(newFileList);
            if (newFileList.length > 0) {
              setInstallModalOpen(true);
              resetInstallUi();
            }
          }}
          beforeUpload={() => false}
          showUploadList={false}
        >
          <Button icon={<UploadOutlined />} type="primary">{t('Install Plugin')}</Button>
        </Upload>
        {tab === 'installed' && <Button onClick={reload} loading={loading} disabled={installing}>{t('Refresh')}</Button>}
        <div style={{ marginLeft: 'auto' }} />
      </div>

      <Tabs
        activeKey={tab}
        onChange={(k) => setTab(k as any)}
        className="plugins-tabs"
        items={[
          {
            key: 'installed',
            label: t('Installed'),
            children: (
              <div style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column' }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
                  <input
                    type="text"
                    placeholder={t('Search name/author/extension')}
                    value={q}
                    onChange={e => setQ(e.target.value)}
                    style={{
                      maxWidth: 360,
                      padding: '4px 11px',
                      borderRadius: 6,
                      border: `1px solid ${token.colorBorder}`,
                      outline: 'none',
                      background: token.colorBgContainer,
                      color: token.colorText,
                    }}
                  />
                </div>
                <div style={{ flex: 1, minHeight: 0, overflow: 'auto', padding: 4 }}>
                  {loading ? (
                    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 320px))', gap: 12, justifyContent: 'start' }}>
                      {Array.from({ length: 6 }).map((_, i) => (
                        <Card key={i} style={{ borderRadius: 10 }}>
                          <Skeleton active avatar paragraph={{ rows: 3 }} />
                        </Card>
                      ))}
                    </div>
                  ) : filtered.length === 0 ? (
                    <Empty description={t('No plugins')} />
                  ) : (
                    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 320px))', gap: 12, justifyContent: 'start' }}>
                      {filtered.map(renderCard)}
                    </div>
                  )}
                </div>
              </div>
            )
          },
          {
            key: 'discover',
            label: t('Discover'),
            children: (
              <div style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column' }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
                  <input
                    type="text"
                    placeholder={t('Search apps')}
                    value={discoverySearch}
                    onChange={e => setDiscoverySearch(e.target.value)}
                    style={{
                      maxWidth: 360,
                      padding: '4px 11px',
                      borderRadius: 6,
                      border: `1px solid ${token.colorBorder}`,
                      outline: 'none',
                      background: token.colorBgContainer,
                      color: token.colorText,
                    }}
                  />
                  <Button
                    icon={<ReloadOutlined />}
                    onClick={loadDiscoveryApps}
                    loading={discoveryLoading}
                  >
                    {t('Refresh')}
                  </Button>
                </div>
                <div style={{ flex: 1, minHeight: 0, overflow: 'auto', padding: 4 }}>
                  {discoveryLoading && discoveryApps.length === 0 ? (
                    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 320px))', gap: 12, justifyContent: 'start' }}>
                      {Array.from({ length: 6 }).map((_, i) => (
                        <Card key={i} style={{ borderRadius: 10 }}>
                          <Skeleton active avatar paragraph={{ rows: 3 }} />
                        </Card>
                      ))}
                    </div>
                  ) : discoveryError ? (
                    <Empty description={discoveryError}>
                      <Button onClick={loadDiscoveryApps}>{t('Refresh')}</Button>
                    </Empty>
                  ) : filteredDiscoveryApps.length === 0 ? (
                    <Empty description={discoveryApps.length === 0 ? t('No results') : t('No results')} />
                  ) : (
                    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 320px))', gap: 12, justifyContent: 'start' }}>
                      {filteredDiscoveryApps.map(app => {
                        const name = lang === 'zh' ? app.name.zh : app.name.en;
                        const description = lang === 'zh' ? app.description.zh : app.description.en;
                        const tags = lang === 'zh' ? app.tags.zh : app.tags.en;
                        const iconUrl = `https://foxel.cc/api/apps/${encodeURIComponent(app.key)}/icon`;
                        const installed = isAppInstalled(app.key);
                        const downloading = downloadingApps.has(app.key);
                        const approvedDate = new Date(app.approvedAt).toLocaleDateString(lang === 'zh' ? 'zh-CN' : 'en-US');
                        const onOpenDetail = () => openDiscoveryAppDetail(app);
                        
                        return (
                          <CenterCard
                            key={app.key}
                            iconUrl={iconUrl}
                            iconAlt={name}
                            name={name}
                            version={app.version || undefined}
                            description={description}
                            metaLeft={app.author || undefined}
                            metaRight={lang === 'zh' ? `审核于 ${approvedDate}` : `Approved ${approvedDate}`}
                            pills={buildCardPills(tags)}
                            onIconClick={onOpenDetail}
                            onTitleClick={onOpenDetail}
                            footerLeft={app.website ? (
                              <a className="fx-center-card-iconLink" href={app.website} target="_blank" rel="noreferrer" title={t('Website')} aria-label={t('Website')}>
                                <LinkOutlined />
                              </a>
                            ) : null}
                            footerRight={installed ? (
                              <span className="fx-center-pill fx-center-pill-success">{t('Installed already')}</span>
                            ) : (
                              <Button
                                type="primary"
                                size="small"
                                icon={<DownloadOutlined />}
                                loading={downloading}
                                onClick={() => handleDownloadAndInstall(app)}
                              >
                                {downloading ? t('Downloading') : t('Download and Install')}
                              </Button>
                            )}
                          />
                        );
                      })}
                    </div>
                  )}
                </div>
              </div>
            )
          }
        ]}
      />

      <Modal
        title={(() => {
          if (detailApp) {
            return `${lang === 'zh' ? detailApp.latest.name.zh : detailApp.latest.name.en} · ${t('Details')}`;
          }
          if (detailSummary) {
            return `${lang === 'zh' ? detailSummary.name.zh : detailSummary.name.en} · ${t('Details')}`;
          }
          return t('Details');
        })()}
        open={detailOpen}
        onCancel={closeDetailModal}
        width={860}
        footer={(() => {
          const key = detailKey || detailApp?.key || detailSummary?.key;
          const installed = key ? isAppInstalled(key) : false;
          const downloading = key ? downloadingApps.has(key) : false;
          const downloadUrl = detailApp?.latest.downloadUrl || detailSummary?.downloadUrl;
          const version = detailApp?.latest.version || detailSummary?.version;

          return (
            <Space>
              <Button onClick={closeDetailModal}>{t('Close')}</Button>
              {installed ? (
                <Button disabled>{t('Installed already')}</Button>
              ) : (
                <Button
                  type="primary"
                  size="middle"
                  icon={<DownloadOutlined />}
                  loading={downloading}
                  disabled={!key || !downloadUrl || !version || detailLoading}
                  onClick={() => {
                    if (!key || !downloadUrl || !version) return;
                    void handleDownloadAndInstall({ key, version, downloadUrl });
                  }}
                >
                  {t('Download and Install')}
                </Button>
              )}
            </Space>
          );
        })()}
      >
        <div style={{ maxHeight: '70vh', overflowY: 'auto', paddingRight: 6 }}>
          {detailLoading && !detailApp ? (
            <Skeleton active avatar paragraph={{ rows: 8 }} />
          ) : detailError ? (
            <Empty description={detailError}>
              <Button
                onClick={() => {
                  if (!detailKey) return;
                  setDetailApp(null);
                  setDetailError(undefined);
                  setDetailReloadId(v => v + 1);
                }}
              >
                {t('Refresh')}
              </Button>
            </Empty>
          ) : detailApp ? (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
              <div style={{ display: 'flex', gap: 12, alignItems: 'flex-start' }}>
                <div style={{ width: 64, height: 64, borderRadius: 14, overflow: 'hidden', border: `1px solid ${token.colorBorder}` }}>
                  <img
                    src={`https://foxel.cc/api/apps/${encodeURIComponent(detailApp.key)}/icon`}
                    alt={lang === 'zh' ? detailApp.latest.name.zh : detailApp.latest.name.en}
                    style={{ width: '100%', height: '100%', objectFit: 'contain', background: token.colorBgContainer }}
                    loading="lazy"
                    onError={(e) => { (e.currentTarget as HTMLImageElement).src = '/logo.svg'; }}
                  />
                </div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 12 }}>
                    <Typography.Title level={5} style={{ margin: 0 }}>
                      {lang === 'zh' ? detailApp.latest.name.zh : detailApp.latest.name.en}
                    </Typography.Title>
                    <Typography.Text type="secondary">
                      {t('Version')}: {detailApp.latest.version}
                    </Typography.Text>
                  </div>
                  <div style={{ marginTop: 6, display: 'flex', flexWrap: 'wrap', gap: 10, color: token.colorTextTertiary, fontSize: 12 }}>
                    <span>{detailApp.latest.author}</span>
                    <span>{detailApp.key}</span>
                    <span>
                      {lang === 'zh'
                        ? `审核于 ${new Date(detailApp.latest.approvedAt).toLocaleDateString('zh-CN')}`
                        : `Approved ${new Date(detailApp.latest.approvedAt).toLocaleDateString('en-US')}`}
                    </span>
                    {detailApp.latest.website ? (
                      <a href={detailApp.latest.website} target="_blank" rel="noreferrer" style={{ color: token.colorTextTertiary }}>
                        <LinkOutlined style={{ marginRight: 6 }} />
                        {t('Website')}
                      </a>
                    ) : null}
                  </div>
                  <div style={{ marginTop: 10 }}>
                    <Space size={[4, 4]} wrap>
                      {(lang === 'zh' ? detailApp.latest.tags.zh : detailApp.latest.tags.en).map(tag => (
                        <Tag key={tag}>{tag}</Tag>
                      ))}
                    </Space>
                  </div>
                </div>
              </div>

              <Tabs
                defaultActiveKey="details"
                items={[
                  {
                    key: 'details',
                    label: t('Details'),
                    children: (
                      <Typography.Paragraph style={{ marginBottom: 0, whiteSpace: 'pre-wrap' }}>
                        {lang === 'zh' ? detailApp.latest.description.zh : detailApp.latest.description.en}
                      </Typography.Paragraph>
                    ),
                  },
                  {
                    key: 'versions',
                    label: t('Version'),
                    children: (
                      <Collapse
                        size="small"
                        items={detailApp.versions.map(v => {
                          const approvedDate = new Date(v.approvedAt).toLocaleDateString(lang === 'zh' ? 'zh-CN' : 'en-US');
                          return {
                            key: v.version,
                            label: (
                              <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10 }}>
                                <span style={{ fontVariantNumeric: 'tabular-nums' }}>{v.version}</span>
                                <span style={{ color: token.colorTextTertiary, fontSize: 12 }}>
                                  {lang === 'zh' ? `审核于 ${approvedDate}` : `Approved ${approvedDate}`}
                                </span>
                              </div>
                            ),
                            children: v.releaseNotesMd ? (
                              <div style={{ fontSize: 13, lineHeight: 1.7 }}>
                                <ReactMarkdown>{v.releaseNotesMd}</ReactMarkdown>
                              </div>
                            ) : (
                              <Typography.Text type="secondary">
                                {lang === 'zh' ? '暂无更新记录' : 'No release notes'}
                              </Typography.Text>
                            ),
                          };
                        })}
                        style={{ background: 'transparent' }}
                      />
                    ),
                  },
                ]}
              />
            </div>
          ) : (
            <Empty description={t('No results')} />
          )}
        </div>
      </Modal>

      <Modal
        title={t('Confirm Install')}
        open={installModalOpen}
        onCancel={closeInstallModal}
        maskClosable={!installing}
        closable={!installing}
        width={640}
        footer={(() => {
          if (fileList.length === 0) {
            return <Button type="primary" onClick={closeInstallModal}>{t('Close')}</Button>;
          }
          if (installing) {
            return <Button type="primary" loading>{t('Installing')}</Button>;
          }
          if (installDone) {
            return <Button type="primary" onClick={closeInstallModal}>{t('Close')}</Button>;
          }
          return (
            <Space>
              <Button onClick={closeInstallModal}>{t('Cancel')}</Button>
              <Button type="primary" onClick={handleInstall}>{t('Confirm Install')}</Button>
            </Space>
          );
        })()}
      >
        {fileList.length === 0 ? (
          <Empty description={t('Please select a .foxpkg file')} />
        ) : (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
            <div>
              <Typography.Text>{t('Selected {count} files', { count: fileList.length })}</Typography.Text>
              <div style={{ marginTop: 4 }}>
                <Typography.Text type="secondary">{t('Installation will stop on first failure')}</Typography.Text>
              </div>
            </div>

            {installStopReason ? (
              <Alert type="error" showIcon message={installStopReason} />
            ) : null}

            <Progress
              percent={(() => {
                const finished = fileList.filter(f => {
                  const status = installFileState[f.uid]?.status;
                  return status === 'success' || status === 'failed';
                }).length;
                return fileList.length === 0 ? 0 : Math.round((finished / fileList.length) * 100);
              })()}
              status={installStopReason ? 'exception' : (installDone ? 'success' : (installing ? 'active' : 'normal'))}
              format={() => {
                const finished = fileList.filter(f => {
                  const status = installFileState[f.uid]?.status;
                  return status === 'success' || status === 'failed';
                }).length;
                return `${finished}/${fileList.length}`;
              }}
            />

            <List
              dataSource={fileList}
              bordered
              renderItem={(file) => {
                const st = (installFileState[file.uid]?.status || 'pending') as InstallStatus;
                const msg = installFileState[file.uid]?.message;
                const errors = installFileState[file.uid]?.errors || [];

                const statusText = (() => {
                  switch (st) {
                    case 'pending': return t('Pending');
                    case 'installing': return t('Installing');
                    case 'success': return t('Success');
                    case 'failed': return t('Failed');
                    case 'skipped': return t('Skipped');
                    default: return t('Pending');
                  }
                })();

                const statusColor = (() => {
                  switch (st) {
                    case 'installing': return 'blue';
                    case 'success': return 'green';
                    case 'failed': return 'red';
                    case 'skipped': return 'orange';
                    default: return 'default';
                  }
                })();

                return (
                  <List.Item
                    actions={[
                      (!installing && !installDone) ? (
                        <Button key="remove" type="link" danger onClick={() => removeSelectedFile(file.uid)}>
                          {t('Remove')}
                        </Button>
                      ) : null,
                      <Tag key="status" color={statusColor}>{statusText}</Tag>,
                    ].filter(Boolean) as any}
                  >
                    <List.Item.Meta
                      title={file.name}
                      description={
                        (msg || errors.length > 0) ? (
                          <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                            {msg ? (
                              <Typography.Text type={st === 'failed' ? 'danger' : (st === 'success' ? 'success' : 'secondary')}>
                                {msg}
                              </Typography.Text>
                            ) : null}
                            {errors.length > 0 ? (
                              <Typography.Text type={st === 'failed' ? 'danger' : 'secondary'} style={{ fontSize: 12 }}>
                                {errors.join('; ')}
                              </Typography.Text>
                            ) : null}
                          </div>
                        ) : null
                      }
                    />
                  </List.Item>
                );
              }}
            />
          </div>
        )}
      </Modal>
    </div>
  );
});

export default PluginsPage;
