<script setup lang="ts">
/**
 * 摄像头管理页面
 *
 * 功能：摄像头列表展示、搜索筛选、新增/编辑/删除、拉流控制、ROI 配置跳转、配置导出
 * 后端：WVP 视频平台 StreamProxy API
 */
import type { AiotDeviceApi } from '#/api/aiot/device';

import { computed, onMounted, reactive, ref, watch } from 'vue';
import { useRouter } from 'vue-router';

import { useAppConfig } from '@vben/hooks';
import { Page } from '@vben/common-ui';

import {
  AutoComplete,
  Badge,
  Button,
  Checkbox,
  Form,
  Input,
  message,
  Modal,
  Select,
  Space,
  Table,
  Tag,
} from 'ant-design-vue';

import {
  deleteCamera,
  getCameraList,
  getMediaServerList,
  getRoiByCameraId,
  getSnapUrl,
  pushAllConfig,
  saveCamera,
} from '#/api/aiot/device';

defineOptions({ name: 'AiotDeviceCamera' });

const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);

const router = useRouter();

// ==================== 列表状态 ====================

const loading = ref(false);
const cameraList = ref<AiotDeviceApi.Camera[]>([]);
const roiCounts = ref<Record<string, number>>({});
const cameraStatus = ref<Record<string, boolean | null>>({});
const page = ref(1);
const pageSize = ref(15);
const total = ref(0);
const searchQuery = ref('');

const columns = [
  { title: '摄像头名称', dataIndex: 'cameraName', width: 150 },
  { title: '拉流地址', dataIndex: 'srcUrl', ellipsis: true },
  { title: '边缘设备', dataIndex: 'edgeDeviceId', width: 100 },
  { title: '状态', key: 'status', width: 60, align: 'center' as const },
  { title: 'ROI', key: 'roiCount', width: 80, align: 'center' as const },
  { title: '操作', key: 'actions', width: 240, fixed: 'right' as const },
];

// ==================== 编辑弹窗状态 ====================

const editModalOpen = ref(false);
const editModalTitle = ref('添加摄像头');
const saving = ref(false);
const mediaServerOptions = ref<{ label: string; value: string }[]>([]);
const editForm = reactive<Partial<AiotDeviceApi.Camera>>({
  id: undefined,
  type: 'default',
  cameraName: '',
  app: '',
  stream: '',
  edgeDeviceId: 'edge',
  srcUrl: '',
  timeout: 15,
  rtspType: '0',
  enable: true,
  enableAudio: false,
  enableMp4: false,
  enableDisableNoneReader: true,
  relatesMediaServerId: '',
  ffmpegCmdKey: '',
});

// 从已有摄像头中提取应用名选项
const appOptions = computed(() => {
  // 统计每个应用名下的摄像头数量
  const appCounts = new Map<string, number>();
  cameraList.value.forEach((cam) => {
    if (cam.app) {
      appCounts.set(cam.app, (appCounts.get(cam.app) || 0) + 1);
    }
  });

  // 转换为选项数组，按摄像头数量降序排序
  return Array.from(appCounts.entries())
    .map(([app, count]) => ({
      label: `${app} (${count}个)`,
      value: app,
      count,
    }))
    .sort((a, b) => b.count - a.count);
});

// ==================== 数据加载 ====================

async function loadData() {
  loading.value = true;
  try {
    const res = await getCameraList({
      page: page.value,
      count: pageSize.value,
      query: searchQuery.value || undefined,
    });
    cameraList.value = res.list || [];
    total.value = res.total || 0;
    loadRoiCounts();
    loadCameraStatus();
  } catch {
    message.error('加载摄像头列表失败');
  } finally {
    loading.value = false;
  }
}

function loadRoiCounts() {
  for (const cam of cameraList.value) {
    const cameraCode = cam.cameraCode;
    if (!cameraCode) continue;
    getRoiByCameraId(cameraCode)
      .then((data: any) => {
        const items = Array.isArray(data) ? data : [];
        roiCounts.value = { ...roiCounts.value, [cameraCode]: items.length };
      })
      .catch(() => {});
  }
}

async function loadCameraStatus() {
  for (const cam of cameraList.value) {
    const cameraCode = cam.cameraCode;
    if (!cameraCode) continue;
    // 使用 fetch HEAD 请求检测截图是否可用（/snap/image 已免认证）
    try {
      const url = `${apiURL}/aiot/device/roi/snap/image?cameraCode=${encodeURIComponent(cameraCode)}`;
      const res = await fetch(url, { method: 'HEAD' });
      cameraStatus.value = { ...cameraStatus.value, [cameraCode]: res.ok };
    } catch {
      cameraStatus.value = { ...cameraStatus.value, [cameraCode]: false };
    }
  }
}

async function loadMediaServers() {
  try {
    const data = await getMediaServerList();
    const list = Array.isArray(data) ? data : [];
    mediaServerOptions.value = [
      { value: '', label: '自动分配' },
      ...list.map((s: any) => ({
        value: s.id,
        label: `${s.id} (${s.ip})`,
      })),
    ];
  } catch {
    mediaServerOptions.value = [{ value: '', label: '自动分配' }];
  }
}

// ==================== 新增 / 编辑 ====================

function resetForm() {
  Object.assign(editForm, {
    id: undefined,
    type: 'default',
    cameraName: '',
    app: '',
    stream: '',
    edgeDeviceId: 'edge',
    srcUrl: '',
    timeout: 15,
    rtspType: '0',
    enable: true,
    enableAudio: false,
    enableMp4: false,
    enableDisableNoneReader: true,
    relatesMediaServerId: '',
    ffmpegCmdKey: '',
  });
}

/**
 * 自动填充流ID编号
 * 根据当前应用名，自动计算下一个可用的编号
 */
function autoFillStreamId() {
  const app = editForm.app;
  if (!app) return;

  // 过滤出同一应用下的摄像头（排除当前编辑的摄像头）
  const sameAppCameras = cameraList.value.filter(
    (c) => c.app === app && c.id !== editForm.id,
  );

  // 获取已用的纯数字编号
  const usedNumbers = sameAppCameras
    .map((c) => c.stream)
    .filter((s) => /^\d+$/.test(s))
    .map((s) => parseInt(s, 10))
    .sort((a, b) => a - b);

  // 找到第一个未使用的编号
  let nextNum = 1;
  for (const num of usedNumbers) {
    if (num === nextNum) {
      nextNum++;
    } else {
      break;
    }
  }

  // 自动填充（3位数字，前导零）
  editForm.stream = String(nextNum).padStart(3, '0');
}

function handleAdd() {
  resetForm();
  editModalTitle.value = '添加摄像头';
  editModalOpen.value = true;
  loadMediaServers();
  // 自动填充流ID
  autoFillStreamId();
}

function handleEdit(row: AiotDeviceApi.Camera) {
  Object.assign(editForm, {
    id: row.id,
    type: row.type || 'default',
    cameraName: row.cameraName || '',
    app: row.app || '',
    stream: row.stream || '',
    edgeDeviceId: row.edgeDeviceId || 'edge',
    cameraCode: row.cameraCode || '',
    srcUrl: row.srcUrl || '',
    timeout: row.timeout ?? 15,
    rtspType: row.rtspType || '0',
    enable: row.enable ?? true,
    enableAudio: row.enableAudio ?? false,
    enableMp4: row.enableMp4 ?? false,
    enableDisableNoneReader: row.enableDisableNoneReader ?? true,
    relatesMediaServerId: row.relatesMediaServerId || '',
    ffmpegCmdKey: row.ffmpegCmdKey || '',
  });
  editModalTitle.value = '编辑摄像头';
  editModalOpen.value = true;
  loadMediaServers();
}

async function handleSave() {
  if (!editForm.cameraName?.trim()) {
    message.warning('请输入摄像头名称');
    return;
  }
  if (!editForm.srcUrl?.trim()) {
    message.warning('请输入拉流地址');
    return;
  }
  // app/stream 为 ZLM 内部字段，自动填充默认值即可
  if (!editForm.app) editForm.app = 'default';
  if (!editForm.stream) editForm.stream = '001';
  saving.value = true;
  try {
    await saveCamera({ ...editForm });
    message.success(editForm.id ? '编辑成功' : '添加成功');
    editModalOpen.value = false;
    // 新增摄像头成功后自动触发首次截图预热（非阻塞）
    if (!editForm.id) {
      loadData().then(() => {
        const saved = cameraList.value.find(
          (c) => c.app === editForm.app && c.stream === editForm.stream,
        );
        if (saved?.cameraCode) {
          getSnapUrl(saved.cameraCode, true).catch(() => {});
        }
      });
    } else {
      loadData();
    }
  } catch (err: any) {
    message.error(err?.message || '保存失败');
  } finally {
    saving.value = false;
  }
}

// ==================== 删除 ====================

function handleDelete(row: AiotDeviceApi.Camera) {
  Modal.confirm({
    title: '删除确认',
    content: `确定删除摄像头 ${row.cameraName || row.cameraCode || row.stream} ？`,
    okType: 'danger',
    async onOk() {
      try {
        await deleteCamera(row.id!);
        message.success('删除成功');
        loadData();
      } catch {
        message.error('删除失败');
      }
    },
  });
}

// ==================== ROI 配置跳转 ====================

function handleRoiConfig(row: AiotDeviceApi.Camera) {
  router.push({
    path: '/aiot/device/roi',
    query: {
      cameraCode: row.cameraCode,
      srcUrl: row.srcUrl,
    },
  });
}

// ==================== 同步全局配置 ====================

const syncing = ref(false);

async function handleSyncAll() {
  syncing.value = true;
  try {
    const res = await pushAllConfig();
    message.success(
      `同步完成：ROI ${res.rois ?? 0} 条，算法绑定 ${res.binds ?? 0} 条`,
    );
  } catch {
    message.error('同步全局配置失败');
  } finally {
    syncing.value = false;
  }
}

// ==================== 分页 ====================

function handlePageChange(p: number, size: number) {
  page.value = p;
  pageSize.value = size;
  loadData();
}

// ==================== 初始化 ====================

// 监听应用名变化，自动填充流ID
watch(
  () => editForm.app,
  () => {
    autoFillStreamId();
  },
);

onMounted(() => {
  loadData();
});
</script>

<template>
  <Page auto-content-height>
    <div style="padding: 16px">
      <!-- 搜索栏 -->
      <div
        style="
          display: flex;
          align-items: center;
          justify-content: space-between;
          margin-bottom: 16px;
        "
      >
        <div style="display: flex; align-items: center; gap: 8px">
          <Input
            v-model:value="searchQuery"
            placeholder="搜索摄像头名称/地址"
            style="width: 250px"
            allow-clear
            @press-enter="loadData"
          />
          <Button type="primary" @click="loadData">查询</Button>
        </div>
        <Space>
          <Button type="primary" :loading="syncing" @click="handleSyncAll">
            同步全局配置
          </Button>
          <Button type="primary" @click="handleAdd">添加摄像头</Button>
        </Space>
      </div>

      <!-- 表格 -->
      <Table
        :columns="columns"
        :data-source="cameraList"
        :loading="loading"
        :pagination="{
          current: page,
          pageSize,
          total,
          showSizeChanger: true,
          pageSizeOptions: ['15', '25', '50'],
          showTotal: (t: number) => `共 ${t} 条`,
          onChange: handlePageChange,
        }"
        :scroll="{ x: 1000 }"
        row-key="id"
        bordered
        size="middle"
      >
        <template #bodyCell="{ column, record }">
          <template v-if="column.key === 'status'">
            <Badge
              :status="
                cameraStatus[record.cameraCode] === null ||
                cameraStatus[record.cameraCode] === undefined
                  ? 'default'
                  : cameraStatus[record.cameraCode]
                    ? 'success'
                    : 'error'
              "
            />
          </template>
          <template v-else-if="column.key === 'roiCount'">
            <Badge
              :count="roiCounts[record.cameraCode] ?? 0"
              :number-style="{ backgroundColor: '#1677ff' }"
              show-zero
            />
          </template>
          <template v-else-if="column.key === 'actions'">
            <Space>
              <Button
                type="primary"
                size="small"
                @click="handleRoiConfig(record)"
              >
                ROI配置
              </Button>
              <Button size="small" @click="handleEdit(record)">编辑</Button>
              <Button size="small" danger @click="handleDelete(record)">
                删除
              </Button>
            </Space>
          </template>
        </template>
      </Table>
    </div>

    <!-- 新增/编辑弹窗 -->
    <Modal
      v-model:open="editModalOpen"
      :title="editModalTitle"
      :width="560"
      :confirm-loading="saving"
      @ok="handleSave"
    >
      <Form
        layout="horizontal"
        :label-col="{ span: 6 }"
        :wrapper-col="{ span: 16 }"
        style="margin-top: 16px"
      >
        <Form.Item label="代理类型">
          <Select
            v-model:value="editForm.type"
            :options="[
              { value: 'default', label: '默认' },
              { value: 'ffmpeg', label: 'FFmpeg' },
            ]"
          />
        </Form.Item>
        <Form.Item v-if="editForm.id" label="摄像头编码">
          <Input :value="editForm.cameraCode" disabled />
        </Form.Item>
        <Form.Item label="摄像头名称" required>
          <Input
            v-model:value="editForm.cameraName"
            placeholder="请输入摄像头名称，如：大堂吧台、室外停车场"
          />
        </Form.Item>
        <Form.Item label="边缘设备">
          <Select
            v-model:value="editForm.edgeDeviceId"
            :options="[
              { value: 'edge', label: 'edge（主站）' },
              { value: 'edge_002', label: 'edge_002（梦中心）' },
            ]"
            placeholder="选择绑定的边缘设备"
          />
        </Form.Item>
        <Form.Item label="拉流地址" required>
          <Input
            v-model:value="editForm.srcUrl"
            placeholder="rtsp://... 或 rtmp://..."
          />
        </Form.Item>
        <Form.Item label="超时(秒)">
          <Input
            v-model:value="editForm.timeout"
            type="number"
            placeholder="15"
          />
        </Form.Item>
        <Form.Item label="RTSP拉流方式">
          <Select
            v-model:value="editForm.rtspType"
            :options="[
              { value: '0', label: 'TCP' },
              { value: '1', label: 'UDP' },
              { value: '2', label: '组播' },
            ]"
          />
        </Form.Item>
        <Form.Item label="媒体服务器">
          <Select
            v-model:value="editForm.relatesMediaServerId"
            :options="mediaServerOptions"
            placeholder="自动分配"
          />
        </Form.Item>
        <Form.Item
          v-if="editForm.type === 'ffmpeg'"
          label="FFmpeg命令模板"
        >
          <Input
            v-model:value="editForm.ffmpegCmdKey"
            placeholder="默认留空"
          />
        </Form.Item>
        <Form.Item label="选项" :wrapper-col="{ offset: 6, span: 16 }">
          <div style="display: flex; flex-wrap: wrap; gap: 16px">
            <Checkbox v-model:checked="editForm.enable">启用</Checkbox>
            <Checkbox v-model:checked="editForm.enableAudio">
              开启音频
            </Checkbox>
            <Checkbox v-model:checked="editForm.enableMp4">
              MP4录制
            </Checkbox>
            <Checkbox v-model:checked="editForm.enableDisableNoneReader">
              无人观看时停止
            </Checkbox>
          </div>
        </Form.Item>
      </Form>
    </Modal>
  </Page>
</template>
