import { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Alert, Input, Modal, Select, Space, Spin, Table, Tag, Typography, message } from 'antd';
import type { ColumnsType } from 'antd/es/table';

import { HttpUtil } from '@/utils';
import { formatInboundLabel } from '@/lib/inbounds/label';
import type { DBInbound } from '@/models/dbinbound';

interface AttachExistingClientsModalProps {
  open: boolean;
  target: DBInbound | null;
  onClose: () => void;
  onAttached?: () => void;
}

interface BulkAttachResult {
  attached?: string[];
  skipped?: string[];
  errors?: string[];
}

interface ClientRow {
  email: string;
  group: string;
  enable: boolean;
  alreadyAttached: boolean;
}

interface RawClient {
  email?: string;
  group?: string;
  enable?: boolean;
  inboundIds?: number[] | null;
}

export default function AttachExistingClientsModal({
  open,
  target,
  onClose,
  onAttached,
}: AttachExistingClientsModalProps) {
  const { t } = useTranslation();
  const [messageApi, messageContextHolder] = message.useMessage();
  const [loading, setLoading] = useState(false);
  const [saving, setSaving] = useState(false);
  const [clientRows, setClientRows] = useState<ClientRow[]>([]);
  const [selectedEmails, setSelectedEmails] = useState<string[]>([]);
  const [search, setSearch] = useState('');
  const [groupFilter, setGroupFilter] = useState<string | undefined>(undefined);

  // Reset during render, not in an effect, so the first frame is already clean.
  const openTarget = open ? target : null;
  const [syncedTarget, setSyncedTarget] = useState(openTarget);
  if (openTarget !== syncedTarget) {
    setSyncedTarget(openTarget);
    if (openTarget) {
      setLoading(true);
      setSearch('');
      setGroupFilter(undefined);
    }
  }

  useEffect(() => {
    if (!open || !target) return;
    let cancelled = false;
    HttpUtil.get('/panel/api/clients/list', undefined, { silent: true })
      .then((msg) => {
        if (cancelled) return;
        const list = Array.isArray(msg?.obj) ? (msg.obj as RawClient[]) : [];
        const rows: ClientRow[] = list
          .map((c) => ({
            email: (c?.email || '').trim(),
            group: (c?.group || '').trim(),
            enable: c?.enable !== false,
            alreadyAttached: Array.isArray(c?.inboundIds) && c.inboundIds.includes(target.id),
          }))
          .filter((r) => r.email);
        setClientRows(rows);
        setSelectedEmails(rows.filter((r) => !r.alreadyAttached).map((r) => r.email));
      })
      .finally(() => {
        if (!cancelled) setLoading(false);
      });
    return () => {
      cancelled = true;
    };
  }, [open, target]);

  const groupOptions = useMemo(() => {
    const set = new Set<string>();
    for (const r of clientRows) if (r.group) set.add(r.group);
    return [...set].sort((a, b) => a.localeCompare(b)).map((g) => ({ value: g, label: g }));
  }, [clientRows]);

  const attachableCount = useMemo(
    () => clientRows.filter((r) => !r.alreadyAttached).length,
    [clientRows],
  );

  const filteredRows = useMemo(() => {
    const q = search.trim().toLowerCase();
    return clientRows.filter((r) => {
      if (groupFilter && r.group !== groupFilter) return false;
      if (!q) return true;
      return r.email.toLowerCase().includes(q) || r.group.toLowerCase().includes(q);
    });
  }, [clientRows, search, groupFilter]);

  const columns: ColumnsType<ClientRow> = useMemo(
    () => [
      {
        title: t('pages.inbounds.email'),
        dataIndex: 'email',
        key: 'email',
        ellipsis: true,
      },
      {
        title: t('pages.clients.group'),
        dataIndex: 'group',
        key: 'group',
        width: 150,
        ellipsis: true,
        render: (group: string) =>
          group ? (
            <Tag color="geekblue">{group}</Tag>
          ) : (
            <span style={{ color: 'rgba(0,0,0,0.45)' }}>—</span>
          ),
      },
      {
        title: t('enable'),
        key: 'status',
        width: 140,
        render: (_v, row) => {
          if (row.alreadyAttached)
            return <Tag color="default">{t('pages.inbounds.attachExistingStatusAttached')}</Tag>;
          return row.enable ? (
            <Tag color="success">{t('enable')}</Tag>
          ) : (
            <Tag>{t('pages.inbounds.attachClientsStatusDisabled')}</Tag>
          );
        },
      },
    ],
    [t],
  );

  async function submit() {
    if (!target || selectedEmails.length === 0) return;
    setSaving(true);
    try {
      const msg = await HttpUtil.post(
        '/panel/api/clients/bulkAttach',
        { emails: selectedEmails, inboundIds: [target.id] },
        { headers: { 'Content-Type': 'application/json' } },
      );
      if (!msg?.success) {
        messageApi.error(msg?.msg || t('somethingWentWrong'));
        return;
      }
      const result = (msg.obj || {}) as BulkAttachResult;
      const attached = result.attached?.length ?? 0;
      const skipped = result.skipped?.length ?? 0;
      const errors = result.errors?.length ?? 0;
      if (errors > 0) {
        messageApi.warning(
          t('pages.inbounds.attachClientsResultMixed', { attached, skipped, errors }),
        );
      } else {
        messageApi.success(t('pages.inbounds.attachClientsResult', { attached, skipped }));
      }
      onAttached?.();
      onClose();
    } finally {
      setSaving(false);
    }
  }

  const noClients = !loading && clientRows.length === 0;

  return (
    <Modal
      open={open}
      onCancel={onClose}
      onOk={submit}
      okButtonProps={{ disabled: selectedEmails.length === 0, loading: saving }}
      okText={t('pages.inbounds.attachClients')}
      cancelText={t('cancel')}
      title={t('pages.inbounds.attachExistingTitle', {
        remark: formatInboundLabel(target?.tag, target?.remark),
      })}
      width={680}
    >
      {messageContextHolder}
      <Typography.Paragraph type="secondary">
        {t('pages.inbounds.attachExistingDesc', { count: attachableCount })}
      </Typography.Paragraph>

      {noClients ? (
        <Alert type="info" showIcon title={t('pages.inbounds.attachExistingNoClients')} />
      ) : (
        <Spin spinning={loading}>
          <Space orientation="vertical" size="small" style={{ width: '100%' }}>
            <Space style={{ width: '100%', justifyContent: 'space-between' }} wrap>
              <Space wrap>
                <Input.Search
                  allowClear
                  aria-label={t('search')}
                  value={search}
                  onChange={(e) => setSearch(e.target.value)}
                  placeholder={t('pages.inbounds.attachClientsSearchPlaceholder')}
                  style={{ width: 260 }}
                />
                {groupOptions.length > 0 && (
                  <Select
                    allowClear
                    aria-label={t('pages.clients.group')}
                    value={groupFilter}
                    onChange={(v) => setGroupFilter(v)}
                    options={groupOptions}
                    placeholder={t('pages.clients.group')}
                    style={{ minWidth: 160 }}
                    showSearch={{ optionFilterProp: 'label' }}
                  />
                )}
              </Space>
              <Typography.Text type="secondary">
                {t('pages.inbounds.attachClientsSelectedCount', {
                  selected: selectedEmails.length,
                  total: attachableCount,
                })}
              </Typography.Text>
            </Space>
            <Table<ClientRow>
              size="small"
              rowKey="email"
              columns={columns}
              dataSource={filteredRows}
              pagination={false}
              scroll={{ y: 280 }}
              rowSelection={{
                selectedRowKeys: selectedEmails,
                onChange: (keys) => setSelectedEmails(keys as string[]),
                getCheckboxProps: (row) => ({ disabled: row.alreadyAttached }),
                preserveSelectedRowKeys: true,
              }}
            />
          </Space>
        </Spin>
      )}
    </Modal>
  );
}
