<script setup lang="ts">
import { Page } from '@vben/common-ui';

import { computed, h, onMounted, onUnmounted, ref } from 'vue';

import { DesktopOutlined } from '@ant-design/icons-vue';
import { Button, Form, InputNumber, message, Modal, Space, Switch, Table, Tag, Tooltip } from 'ant-design-vue';
import dayjs from 'dayjs';

import { requestClient } from '#/api/request';

type SyncClient = {
  dataType?: string;
  device?: string;
  deviceId?: number;
  firstRequestTime?: number;
  groupSequence?: string;
  ip?: string;
  key: string;
  lastRequestTime?: number;
  marketType?: string;
  platform?: string;
  requestCount?: number;
  route?: string;
  version?: string;
};

type SyncClientRow = SyncClient & {
  children?: SyncClientRow[];
  count?: number;
  onlineCount?: number;
  rowKey: string;
  rowType: 'client' | 'dataType' | 'marketType' | 'platform';
  title: string;
};

const CHAR_MAP: Record<string, Record<string, string>> = {
  dataType: {
    1: '列表',
    2: '让球/大小',
    3: '特别投注',
  },
  marketType: {
    0: '早盘',
    1: '今日',
    2: '滚球',
  },
  platform: {
    ay: 'IM',
    hg: '皇冠',
    im: 'IM',
    ob: 'OB',
    ps: '平博',
    tq: 'OB',
  },
};

const MARKET_TYPE_ORDER = ['2', '1', '0'];

const clients = ref<SyncClient[]>([]);
const latestClientVersion = ref('');
const loading = ref(false);
const autoRefresh = ref(true);
const editClient = ref<SyncClient>();
const editDeviceId = ref<number>();
const editVisible = ref(false);
const refreshTimer = ref<ReturnType<typeof setTimeout>>();
const versionRefreshTimer = ref<ReturnType<typeof setTimeout>>();
const deviceWindows = new Map<number, Window>();

const columns = [
  {
    dataIndex: 'title',
    key: 'title',
    title: '分类',
    width: 180,
  },
  {
    align: 'center',
    dataIndex: 'groupSequence',
    key: 'groupSequence',
    title: '分组序列',
    width: 80,
  },
  {
    align: 'center',
    dataIndex: 'requestCount',
    key: 'requestCount',
    title: '请求次数',
    width: 100,
  },
  {
    align: 'center',
    dataIndex: 'lastRequestTime',
    key: 'lastRequestTime',
    title: '最后请求时间',
    width: 180,
  },
  {
    align: 'center',
    dataIndex: 'firstRequestTime',
    key: 'firstRequestTime',
    title: '初次请求时间',
    width: 180,
  },
  {
    align: 'center',
    dataIndex: 'route',
    key: 'route',
    title: '请求接口',
    width: 180,
  },
  {
    align: 'left',
    dataIndex: 'deviceId',
    key: 'deviceId',
    title: '设备',
    width: 160,
  },
  {
    key: 'action',
    title: '操作',
    width: 90,
  },
];

const onlineThreshold = 5 * 60 * 1000;

const onlineCount = computed(() => {
  const now = Date.now();
  return clients.value.filter(item => now - (item.lastRequestTime ?? 0) <= onlineThreshold).length;
});

const formatTime = (time?: number) => {
  return time ? dayjs(time).format('YYYY-MM-DD HH:mm:ss') : '-';
};

const displayValue = (value?: number | string) => {
  return value === undefined || value === '' ? '-' : value;
};

const displayMappedValue = (
  type: keyof typeof CHAR_MAP,
  value?: number | string,
) => {
  if (value === undefined || value === '') {
    return '-';
  }
  return CHAR_MAP[type]?.[String(value)] ?? value;
};

const isClientVersionOutdated = (version?: string) => {
  return !!version && !!latestClientVersion.value && version !== latestClientVersion.value;
};

const getDeviceUrl = (deviceId?: number) => {
  if (!Number.isInteger(deviceId)) {
    return '';
  }
  const payload = btoa(`${deviceId}\x00c\x00mysql`);
  return `https://rdp.long.bid/#/client/${payload}`;
};

const openDevice = (deviceId?: number) => {
  if (!Number.isInteger(deviceId)) {
    return;
  }
  const existingWindow = deviceWindows.get(deviceId);
  if (existingWindow && !existingWindow.closed) {
    existingWindow.focus();
    return;
  }
  const deviceWindow = window.open(getDeviceUrl(deviceId), `rdp-device-${deviceId}`);
  if (deviceWindow) {
    deviceWindows.set(deviceId, deviceWindow);
    deviceWindow.focus();
  }
};

const isOnline = (time?: number) => {
  return !!time && Date.now() - time <= onlineThreshold;
};

const getGroupKey = (value?: string) => {
  return value === undefined || value === '' ? '__empty__' : String(value);
};

const getGroupTitle = (type: keyof typeof CHAR_MAP, value?: string) => {
  return displayMappedValue(type, value);
};

const getLatestTime = (items: SyncClient[]) => {
  return Math.max(...items.map(item => item.lastRequestTime ?? 0));
};

const getRequestCount = (items: SyncClient[]) => {
  return items.reduce((total, item) => total + (item.requestCount ?? 0), 0);
};

const getOnlineCount = (items: SyncClient[]) => {
  return items.filter(item => isOnline(item.lastRequestTime)).length;
};

const createClientRow = (client: SyncClient): SyncClientRow => ({
  ...client,
  rowKey: `client:${client.key}`,
  rowType: 'client',
  title: client.device || '客户端',
});

const createGroupRow = (
  rowType: SyncClientRow['rowType'],
  rowKey: string,
  title: string,
  items: SyncClient[],
  children: SyncClientRow[],
): SyncClientRow => ({
  rowKey,
  key: rowKey,
  rowType,
  title,
  children,
  count: items.length,
  lastRequestTime: getLatestTime(items),
  onlineCount: getOnlineCount(items),
  requestCount: getRequestCount(items),
});

const groupByField = (items: SyncClient[], field: keyof SyncClient) => {
  return items.reduce<Record<string, SyncClient[]>>((groups, item) => {
    const key = getGroupKey(item[field] as string | undefined);
    if (!groups[key]) {
      groups[key] = [];
    }
    groups[key].push(item);
    return groups;
  }, {});
};

const sortGroupEntries = (entries: Array<[string, SyncClient[]]>) => {
  return entries.sort(([keyA], [keyB]) => {
    if (keyA === '__empty__') {
      return 1;
    }
    if (keyB === '__empty__') {
      return -1;
    }
    return keyA.localeCompare(keyB, 'zh-CN', { numeric: true });
  });
};

const sortMarketEntries = (entries: Array<[string, SyncClient[]]>) => {
  return entries.sort(([keyA], [keyB]) => {
    const indexA = MARKET_TYPE_ORDER.indexOf(keyA);
    const indexB = MARKET_TYPE_ORDER.indexOf(keyB);
    if (indexA >= 0 && indexB >= 0) {
      return indexA - indexB;
    }
    if (indexA >= 0) {
      return -1;
    }
    if (indexB >= 0) {
      return 1;
    }
    return keyA.localeCompare(keyB, 'zh-CN', { numeric: true });
  });
};

const sortClientsByGroupSequence = (items: SyncClient[]) => {
  return [...items].sort((a, b) => {
    const sequenceA = getGroupKey(a.groupSequence);
    const sequenceB = getGroupKey(b.groupSequence);
    if (sequenceA === '__empty__') {
      return 1;
    }
    if (sequenceB === '__empty__') {
      return -1;
    }
    return sequenceA.localeCompare(sequenceB, 'zh-CN', { numeric: true });
  });
};

const createPlatformRows = (items: SyncClient[], keyPrefix: string) => {
  const platformGroups = groupByField(items, 'platform');
  return sortGroupEntries(Object.entries(platformGroups)).map(([platform, platformItems]) => {
    return createGroupRow(
      'platform',
      `${keyPrefix}:platform:${platform}`,
      getGroupTitle('platform', platform === '__empty__' ? undefined : platform),
      platformItems,
      sortClientsByGroupSequence(platformItems).map(createClientRow),
    );
  });
};

const groupedClients = computed<SyncClientRow[]>(() => {
  const listClients = clients.value.filter(item => String(item.dataType) === '1');
  const marketClients = clients.value.filter(item => String(item.dataType) !== '1');
  const rows: SyncClientRow[] = [];

  if (listClients.length) {
    rows.push(createGroupRow(
      'dataType',
      'data:1',
      getGroupTitle('dataType', '1'),
      listClients,
      createPlatformRows(listClients, 'data:1'),
    ));
  }

  const marketGroups = groupByField(marketClients, 'marketType');

  const marketRows = sortMarketEntries(Object.entries(marketGroups)).map(([marketType, marketItems]) => {
    const dataGroups = groupByField(marketItems, 'dataType');
    const dataChildren = sortGroupEntries(Object.entries(dataGroups)).map(([dataType, dataItems]) => {
      return createGroupRow(
        'dataType',
        `market:${marketType}:data:${dataType}`,
        getGroupTitle('dataType', dataType === '__empty__' ? undefined : dataType),
        dataItems,
        createPlatformRows(dataItems, `market:${marketType}:data:${dataType}`),
      );
    });

    return createGroupRow(
      'marketType',
      `market:${marketType}`,
      getGroupTitle('marketType', marketType === '__empty__' ? undefined : marketType),
      marketItems,
      dataChildren,
    );
  });

  return [...rows, ...marketRows];
});

const expandedRowKeys = computed(() => {
  const keys: string[] = [];
  const collectKeys = (rows: SyncClientRow[]) => {
    rows.forEach((row) => {
      if (row.children?.length) {
        keys.push(row.rowKey);
        collectKeys(row.children);
      }
    });
  };
  collectKeys(groupedClients.value);
  return keys;
});

const renderExpandIcon = () => h('span', { class: 'hidden-expand-icon' });

const clearRefreshTimer = () => {
  if (refreshTimer.value) {
    clearTimeout(refreshTimer.value);
    refreshTimer.value = undefined;
  }
};

const clearVersionRefreshTimer = () => {
  if (versionRefreshTimer.value) {
    clearTimeout(versionRefreshTimer.value);
    versionRefreshTimer.value = undefined;
  }
};

const scheduleRefresh = () => {
  clearRefreshTimer();
  if (!autoRefresh.value) {
    return;
  }
  refreshTimer.value = setTimeout(fetchClients, 30 * 1000);
};

const scheduleVersionRefresh = () => {
  clearVersionRefreshTimer();
  versionRefreshTimer.value = setTimeout(fetchLatestClientVersion, 60 * 1000);
};

const fetchClients = async () => {
  loading.value = true;
  try {
    const data = await requestClient.get<SyncClient[]>('/pstery/get_clients');
    clients.value = data ?? [];
  }
  catch (error) {
    console.error('Failed to fetch sync clients:', error);
    message.error('获取数据同步客户端失败');
  }
  finally {
    loading.value = false;
    scheduleRefresh();
  }
};

const fetchLatestClientVersion = async () => {
  try {
    const response = await fetch('https://bwh.841024.xyz/chrome_update/versions?names=events_sync');
    const data = await response.json();
    const version = data?.data?.[0];
    if (data?.state === 1 && typeof version === 'string') {
      latestClientVersion.value = version;
    }
  }
  catch (error) {
    console.error('Failed to fetch latest client version:', error);
  }
  finally {
    scheduleVersionRefresh();
  }
};

const openEditClient = (client: SyncClientRow) => {
  editClient.value = client;
  editDeviceId.value = client.deviceId;
  editVisible.value = true;
};

const saveClient = async () => {
  if (!editClient.value?.key) {
    return;
  }
  try {
    await requestClient.post('/pstery/update_client', {
      deviceId: editDeviceId.value,
      key: editClient.value.key,
    });
    message.success('保存成功');
    editVisible.value = false;
    await fetchClients();
  }
  catch (error) {
    console.error('Failed to save sync client:', error);
    message.error('保存客户端信息失败');
  }
};

const deleteClient = (client: SyncClientRow) => {
  Modal.confirm({
    cancelText: '取消',
    content: `确定删除客户端 ${client.title} 吗？`,
    okText: '删除',
    okType: 'danger',
    title: '删除客户端',
    onOk: async () => {
      try {
        await requestClient.post('/pstery/delete_client', { key: client.key });
        message.success('删除成功');
        await fetchClients();
      }
      catch (error) {
        console.error('Failed to delete sync client:', error);
        message.error('删除客户端失败');
      }
    },
  });
};

const handleAutoRefreshChange = () => {
  scheduleRefresh();
};

onMounted(() => {
  fetchLatestClientVersion();
  fetchClients();
});

onUnmounted(() => {
  clearRefreshTimer();
  clearVersionRefreshTimer();
});
</script>

<template>
  <Page title="数据同步">
    <div class="data-sync-toolbar">
      <Space>
        <Button type="primary" :loading="loading" @click="fetchClients">刷新</Button>
        <span class="summary">
          在线 {{ onlineCount }} / 总计 {{ clients.length }}
        </span>
      </Space>
      <Space>
        <span class="auto-label">自动刷新</span>
        <Switch v-model:checked="autoRefresh" @change="handleAutoRefreshChange" />
      </Space>
    </div>

    <Table
      :columns="columns"
      :data-source="groupedClients"
      :expanded-row-keys="expandedRowKeys"
      :expand-icon="renderExpandIcon"
      :loading="loading"
      :pagination="false"
      :row-key="record => record.rowKey"
      bordered
      size="small"
      :scroll="{ x: 900 }"
    >
      <template #bodyCell="{ column, record, text }">
        <template v-if="column.key === 'title'">
          <Space>
            <Tag v-if="record.rowType === 'client'" :color="isOnline(record.lastRequestTime) ? 'green' : 'red'">
              {{ isOnline(record.lastRequestTime) ? '在线' : '离线' }}
            </Tag>
            <span>{{ record.title }}</span>
            <span
              v-if="record.rowType === 'client' && record.version"
              class="version-text"
              :class="{ outdated: isClientVersionOutdated(record.version) }"
            >
              {{ record.version }}
            </span>
            <span v-if="record.rowType !== 'client'" class="group-count">
              {{ record.onlineCount ?? 0 }} / {{ record.count ?? 0 }}
            </span>
          </Space>
        </template>
        <template v-else-if="column.key === 'lastRequestTime'">
          <span v-if="record.rowType === 'client'">{{ formatTime(record.lastRequestTime) }}</span>
          <span v-else>-</span>
        </template>
        <template v-else-if="column.key === 'firstRequestTime'">
          <span v-if="record.rowType === 'client'">{{ formatTime(record.firstRequestTime) }}</span>
          <span v-else>-</span>
        </template>
        <template v-else-if="record.rowType !== 'client'">
          -
        </template>
        <template v-else-if="column.key === 'action'">
          <Space>
            <Button size="small" type="link" @click="openEditClient(record)">编辑</Button>
            <Button danger size="small" type="link" @click="deleteClient(record)">删除</Button>
          </Space>
        </template>
        <template v-else-if="column.key === 'requestCount'">
          {{ record.requestCount ?? 0 }}
        </template>
        <template v-else-if="column.key === 'deviceId'">
          <Space>
            <Tooltip v-if="Number.isInteger(record.deviceId)" title="打开设备">
              <Button
                class="device-link"
                size="small"
                type="link"
                @click="openDevice(record.deviceId)"
              >
                <DesktopOutlined />
              </Button>
            </Tooltip>
            <span v-else class="device-placeholder"></span>
            <span>{{ displayValue(record.ip) }}</span>
          </Space>
        </template>
        <template v-else>
          {{ displayValue(text) }}
        </template>
      </template>
    </Table>

    <Modal
      v-model:visible="editVisible"
      title="编辑客户端"
      ok-text="保存"
      cancel-text="取消"
      @ok="saveClient"
    >
      <Form layout="vertical">
        <Form.Item label="设备ID">
          <InputNumber
            v-model:value="editDeviceId"
            :min="0"
            :precision="0"
            style="width: 100%"
          />
        </Form.Item>
      </Form>
    </Modal>
  </Page>
</template>

<style scoped>
.data-sync-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

.summary,
.auto-label {
  color: hsl(var(--muted-foreground));
}

.group-count {
  color: hsl(var(--muted-foreground));
  font-size: 12px;
}

.version-text {
  color: hsl(var(--muted-foreground));
  font-size: 12px;
}

.version-text.outdated {
  color: #fa8c16;
}

.device-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  font-size: 16px;
  padding: 0;
}

.device-placeholder {
  display: inline-block;
  width: 24px;
}

:deep(.hidden-expand-icon) {
  display: none;
}
</style>
