import { useCallback, useMemo, useState, type Key } from 'react';
import { useTranslation } from 'react-i18next';
import {
  Button,
  Card,
  Checkbox,
  Dropdown,
  Input,
  Select,
  Space,
  Switch,
  Table,
  Tag,
  Tooltip,
  type MenuProps,
} from 'antd';
import {
  PlusOutlined,
  MenuOutlined,
  MoreOutlined,
  ExportOutlined,
  ImportOutlined,
  ReloadOutlined,
  InfoCircleOutlined,
  DeleteOutlined,
  SearchOutlined,
} from '@ant-design/icons';

import { HttpUtil } from '@/utils';
import { activateOnKey } from '@/utils/a11y';

import { buildRowActionsMenu } from './RowActions';
import { useInboundColumns } from './useInboundColumns';
import InboundStatsModal from './InboundStatsModal';
import type { DBInboundRecord, GeneralAction, InboundListProps, RowAction } from './types';
import './InboundList.css';

export default function InboundList({
  dbInbounds,
  clientCount,
  lastOnlineMap: _lastOnlineMap,
  inboundSpeed,
  expireDiff,
  trafficDiff,
  pageSize,
  isMobile,
  subEnable,
  nodesById,
  hasActiveNode,
  onAddInbound,
  onGeneralAction,
  onRowAction,
  onBulkDelete,
}: InboundListProps) {
  const { t } = useTranslation();
  const [statsRecord, setStatsRecord] = useState<DBInboundRecord | null>(null);
  const [selectedRowKeys, setSelectedRowKeys] = useState<number[]>([]);
  // Node filter (#4997): 'all' shows everything, 0 is the local-panel
  // sentinel (inbounds without a nodeId), otherwise a node id. Session-only.
  const [nodeFilter, setNodeFilter] = useState<number | 'all'>('all');
  const [searchKey, setSearchKey] = useState('');

  const showNodeFilter = useMemo(
    () => nodesById.size > 0 || dbInbounds.some((ib) => ib.nodeId != null),
    [nodesById, dbInbounds],
  );

  const nodeFilterOptions = useMemo(
    () => [
      { value: 'all' as const, label: t('pages.clients.filters.nodes') },
      { value: 0, label: t('pages.clients.filters.localPanel') },
      ...Array.from(nodesById.values()).map((n) => ({ value: n.id, label: n.name || `#${n.id}` })),
    ],
    [nodesById, t],
  );

  const visibleInbounds = useMemo(() => {
    let list = dbInbounds;
    if (nodeFilter === 0) list = list.filter((ib) => ib.nodeId == null);
    else if (nodeFilter !== 'all') list = list.filter((ib) => ib.nodeId === nodeFilter);
    const q = searchKey.trim().toLowerCase();
    if (!q) return list;
    return list.filter((ib) => (
      (ib.remark || '').toLowerCase().includes(q)
      || String(ib.port).includes(q)
      || (ib.protocol || '').toLowerCase().includes(q)
    ));
  }, [dbInbounds, nodeFilter, searchKey]);

  const onSwitchEnable = useCallback(async (dbInbound: DBInboundRecord, next: boolean) => {
    const previous = dbInbound.enable;
    dbInbound.enable = next;
    try {
      const formData = new FormData();
      formData.append('enable', String(next));
      const msg = await HttpUtil.post(`/panel/api/inbounds/setEnable/${dbInbound.id}`, formData);
      if (!msg?.success) dbInbound.enable = previous;
    } catch {
      dbInbound.enable = previous;
    }
  }, []);

  const hasAnyRemark = useMemo(
    () => dbInbounds.some((i) => typeof i.remark === 'string' && i.remark.trim() !== ''),
    [dbInbounds],
  );

  const hasAnySubSortIndex = useMemo(
    () => dbInbounds.some((i) => (i.subSortIndex ?? 1) > 1),
    [dbInbounds],
  );

  const toggleSelect = useCallback((id: number, checked: boolean) => {
    setSelectedRowKeys((prev) => {
      const next = new Set(prev);
      if (checked) next.add(id); else next.delete(id);
      return Array.from(next);
    });
  }, []);

  const selectAll = useCallback((checked: boolean) => {
    setSelectedRowKeys(checked ? visibleInbounds.map((i) => i.id) : []);
  }, [visibleInbounds]);

  const allSelected = visibleInbounds.length > 0 && selectedRowKeys.length === visibleInbounds.length;
  const someSelected = selectedRowKeys.length > 0 && selectedRowKeys.length < visibleInbounds.length;

  const handleBulkDelete = useCallback(async () => {
    const ok = await onBulkDelete(selectedRowKeys);
    if (ok) setSelectedRowKeys([]);
  }, [onBulkDelete, selectedRowKeys]);

  const columns = useInboundColumns({
    hasAnyRemark,
    hasAnySubSortIndex,
    hasActiveNode,
    nodesById,
    clientCount,
    inboundSpeed,
    subEnable,
    expireDiff,
    trafficDiff,
    onRowAction,
    onSwitchEnable,
  });

  const tableScrollX = useMemo(
    () => columns.reduce((sum, c) => sum + (typeof c.width === 'number' ? c.width : 0), 0),
    [columns],
  );

  const paginationFor = (rows: DBInboundRecord[]) => {
    const size = pageSize > 0 ? pageSize : rows.length || 1;
    return { pageSize: size, showSizeChanger: false, hideOnSinglePage: true };
  };

  const generalActionsMenu: MenuProps = {
    items: [
      { key: 'import', icon: <ImportOutlined />, label: t('pages.inbounds.importInbound') },
      { key: 'export', icon: <ExportOutlined />, label: t('pages.inbounds.export') },
      ...(subEnable
        ? [{ key: 'subs', icon: <ExportOutlined />, label: `${t('pages.inbounds.export')} — ${t('pages.settings.subSettings')}` }]
        : []),
      { key: 'resetInbounds', icon: <ReloadOutlined />, label: t('pages.inbounds.resetAllTraffic') },
    ],
    onClick: ({ key }) => onGeneralAction(key as GeneralAction),
  };

  return (
    <Card
      hoverable
      title={(
        <Space>
          <Button type="primary" onClick={onAddInbound} icon={<PlusOutlined />} aria-label={t('pages.inbounds.addInbound')}>
            {!isMobile && t('pages.inbounds.addInbound')}
          </Button>
          <Dropdown trigger={['click']} menu={generalActionsMenu}>
            <Button type="primary" icon={<MenuOutlined />} aria-label={t('pages.inbounds.generalActions')}>
              {!isMobile && t('pages.inbounds.generalActions')}
            </Button>
          </Dropdown>
          {showNodeFilter && (
            <Select
              value={nodeFilter}
              onChange={(v) => setNodeFilter(v)}
              options={nodeFilterOptions}
              showSearch
              popupMatchSelectWidth={false}
              style={{ minWidth: isMobile ? 90 : 140 }}
              aria-label={t('pages.clients.filters.nodes')}
            />
          )}
          <Input
            value={searchKey}
            onChange={(e) => setSearchKey(e.target.value)}
            placeholder={t('search')}
            allowClear
            prefix={<SearchOutlined />}
            style={{ maxWidth: isMobile ? 110 : 200 }}
            aria-label={t('search')}
          />
          {selectedRowKeys.length > 0 && (
            <>
              <Tag color="blue" closable onClose={() => setSelectedRowKeys([])} style={{ marginInlineEnd: 0 }}>
                {t('pages.inbounds.selectedCount', { count: selectedRowKeys.length })}
              </Tag>
              <Button danger icon={<DeleteOutlined />} onClick={handleBulkDelete} aria-label={t('delete')}>
                {!isMobile && t('delete')}
              </Button>
            </>
          )}
        </Space>
      )}
    >
      <Space orientation="vertical" style={{ width: '100%' }}>
        {isMobile ? (
          <div className="inbound-cards">
            {visibleInbounds.length === 0 ? (
              <div className="card-empty">
                <ImportOutlined style={{ fontSize: 28, opacity: 0.5 }} />
                <div>{t('noData')}</div>
              </div>
            ) : (
              <>
              <div className="card-bulk-bar">
                <Checkbox
                  checked={allSelected}
                  indeterminate={someSelected}
                  onChange={(e) => selectAll(e.target.checked)}
                >
                  {t('pages.inbounds.selectAll')}
                </Checkbox>
                {selectedRowKeys.length > 0 && (
                  <span className="bulk-count">{selectedRowKeys.length}</span>
                )}
              </div>
              {visibleInbounds.map((record) => (
                <div key={record.id} className={`inbound-card${selectedRowKeys.includes(record.id) ? ' is-selected' : ''}`}>
                  <div className="card-head">
                    <Checkbox
                      checked={selectedRowKeys.includes(record.id)}
                      onChange={(e) => toggleSelect(record.id, e.target.checked)}
                    />
                    <span className="card-id">#{record.id}</span>
                    <span className="tag-name">{record.remark}</span>
                    <div className="card-actions">
                      <Tooltip title={t('pages.inbounds.inboundInfo')}>
                        <InfoCircleOutlined
                          className="row-action-trigger"
                          role="button"
                          tabIndex={0}
                          aria-label={t('pages.inbounds.inboundInfo')}
                          onClick={() => setStatsRecord(record)}
                          onKeyDown={activateOnKey(() => setStatsRecord(record))}
                        />
                      </Tooltip>
                      <Switch
                        checked={record.enable}
                        size="small"
                        onChange={(next) => onSwitchEnable(record, next)}
                      />
                      <Dropdown
                        trigger={['click']}
                        placement="bottomRight"
                        menu={{
                          items: buildRowActionsMenu({ record, subEnable, t, isMobile: true, hasClients: (clientCount[record.id]?.clients || 0) > 0 }),
                          onClick: ({ key }) => onRowAction({ key: key as RowAction, dbInbound: record }),
                        }}
                      >
                        <Button type="text" size="small" className="row-action-trigger" icon={<MoreOutlined />} aria-label={t('more')} />
                      </Dropdown>
                    </div>
                  </div>
                </div>
              ))}
              </>
            )}
          </div>
        ) : (
          <Table
            columns={columns}
            dataSource={visibleInbounds}
            rowKey={(r) => r.id}
            rowSelection={{
              selectedRowKeys,
              onChange: (keys: Key[]) => setSelectedRowKeys(keys as number[]),
            }}
            pagination={paginationFor(visibleInbounds)}
            scroll={{ x: tableScrollX, y: 'calc(100vh - 320px)' }}
            virtual
            style={{ marginTop: 10 }}
            size="small"
            locale={{
              emptyText: (
                <div className="card-empty">
                  <ImportOutlined style={{ fontSize: 32, marginBottom: 8 }} />
                  <div>{t('noData')}</div>
                </div>
              ),
            }}
          />
        )}
      </Space>

      <InboundStatsModal
        open={isMobile && !!statsRecord}
        record={statsRecord}
        hasActiveNode={hasActiveNode}
        nodesById={nodesById}
        clientCount={clientCount}
        inboundSpeed={inboundSpeed}
        trafficDiff={trafficDiff}
        expireDiff={expireDiff}
        onClose={() => setStatsRecord(null)}
      />
    </Card>
  );
}
