<template>
  <div
    ref="fullscreenTargetRef"
    class="run-overview-page"
    :class="{ 'is-fullscreen': isFullscreenLayout }"
  >
    <div class="run-overview-page__floating-tools">
      <el-tooltip
        :content="
          isFullscreen
            ? t('DataCollection.RunOverview.exitFullscreen')
            : t('DataCollection.RunOverview.enterFullscreen')
        "
      >
        <button class="run-overview-page__screenfull" type="button" @click="toggleFullscreen">
          <Icon :icon="isFullscreen ? 'zmdi:fullscreen-exit' : 'zmdi:fullscreen'" color="#344054" />
        </button>
      </el-tooltip>
    </div>

    <OverviewFilterBar
      v-model="queryParams"
      :group-options="groupOptions"
      :device-options="deviceOptions"
      :quick-ranges="quickRanges"
      @quick-range-change="handleQuickRangeChange"
      @query="handleQuery"
      @reset="resetQuery"
    />

    <OverviewMetricCards :metrics="overviewData.metrics" />

    <StatusDistributionChart
      :hourly-status="overviewData.hourlyStatus"
      :summary="overviewData.summary"
      :summary-total-hours="overviewData.summaryTotalHours"
      :statistics-start="queryParams.timeRange[0]"
      :statistics-end="queryParams.timeRange[1]"
    />

    <OperationTimelineChart
      :rows="overviewData.timelineRows"
      :total="overviewData.totalDevices"
      :page-no="pageNo"
      :page-size="pageSize"
      :statistics-start="queryParams.timeRange[0]"
      :statistics-end="queryParams.timeRange[1]"
      @update:page-no="pageNo = $event"
      @update:page-size="handlePageSizeChange"
    />
  </div>
</template>

<script setup lang="ts">
import dayjs from 'dayjs'
import { useFullscreen } from '@vueuse/core'
import { useRoute } from 'vue-router'
import { DeviceOperationOverviewApi } from '@/api/iot/deviceOperationOverview'
import { DeviceLedgerApi, DeviceLedgerVO } from '@/api/mes/deviceledger'
import { OrganizationApi } from '@/api/mes/organization'
import type { DeviceParameterAnalysisNodeVO } from '@/api/mes/organization'
import { handleTree } from '@/utils/tree'
import OverviewFilterBar from './components/OverviewFilterBar.vue'
import OverviewMetricCards from './components/OverviewMetricCards.vue'
import OperationTimelineChart from './components/OperationTimelineChart.vue'
import StatusDistributionChart from './components/StatusDistributionChart.vue'
import { buildDefaultQueryParams } from './mock'
import type {
  OrganizationTreeOption,
  OverviewOption,
  QuickRangeKey,
  RunOverviewData,
  RunOverviewQueryParams
} from './components/types'

defineOptions({ name: 'IotRunOverview' })

const { t } = useI18n()
const route = useRoute()
const fullscreenTargetRef = ref()
const { isFullscreen, toggle: toggleFullscreen } = useFullscreen(fullscreenTargetRef)
const deviceLedgerList = ref<DeviceLedgerVO[]>([])
const groupOptions = ref<OrganizationTreeOption[]>([])
let deviceLedgerRequestSeq = 0

const isEnabledQueryValue = (value: unknown) => {
  const rawValue = Array.isArray(value) ? value[0] : value
  return ['1', 'true', 'yes', 'on'].includes(String(rawValue ?? '').toLowerCase())
}

const isFullscreenLayout = computed(
  () => isFullscreen.value || isEnabledQueryValue(route.query.fullscreen ?? route.query.full)
)

const createDateRangeByQuickKey = (key: QuickRangeKey): [string, string] => {
  const format = 'YYYY-MM-DD HH:mm:ss'
  const now = dayjs()
  if (key === 'today')
    return [now.startOf('day').format(format), now.format(format)]
  if (key === 'yesterday') {
    return [
      dayjs().subtract(1, 'day').startOf('day').format(format),
      dayjs().subtract(1, 'day').endOf('day').format(format)
    ]
  }
  if (key === 'last7Days')
    return [
      now.subtract(6, 'day').startOf('day').format(format),
      now.format(format)
    ]
  if (key === 'last30Days') {
    return [
      now.subtract(29, 'day').startOf('day').format(format),
      now.format(format)
    ]
  }
  return [now.startOf('day').format(format), now.format(format)]
}

const quickRanges = computed(() => [
  { label: t('DataCollection.RunOverview.quickRange.today'), value: 'today' as const },
  { label: t('DataCollection.RunOverview.quickRange.yesterday'), value: 'yesterday' as const },
  { label: t('DataCollection.RunOverview.quickRange.last7Days'), value: 'last7Days' as const },
  { label: t('DataCollection.RunOverview.quickRange.last30Days'), value: 'last30Days' as const }
  /*  { label: t('DataCollection.RunOverview.quickRange.custom'), value: 'custom' as const }*/
])

const queryParams = ref<RunOverviewQueryParams>(buildDefaultQueryParams())
const pageNo = ref(1)
const pageSize = ref(10)

const extractDeviceParameterAnalysisNodes = (res: any): DeviceParameterAnalysisNodeVO[] => {
  if (Array.isArray(res)) return res
  if (Array.isArray(res?.data)) return res.data
  if (Array.isArray(res?.result)) return res.result
  if (Array.isArray(res?.list)) return res.list
  return []
}

const normalizeDeviceParameterAnalysisTree = (
  nodes: DeviceParameterAnalysisNodeVO[] = []
): OrganizationTreeOption[] => {
  const normalizeNode = (node: DeviceParameterAnalysisNodeVO): any => {
    const rawId = node?.id
    const parentId =
      node?.parentId == null || node?.parentId === ''
        ? 0
        : node.parentId

    return {
      ...node,
      id: rawId,
      name: String(node?.name ?? ''),
      parentId,
      children: Array.isArray(node?.children) ? node.children.map(normalizeNode) : undefined
    }
  }

  const normalizedNodes = Array.isArray(nodes) ? nodes.map(normalizeNode) : []
  const isNestedTree = normalizedNodes.some(
    (node) => Array.isArray(node?.children) && node.children.length > 0
  )
  const orgTree = isNestedTree
    ? normalizedNodes
    : (handleTree(normalizedNodes, 'id', 'parentId', 'children') as any[])

  const toOption = (node: any): OrganizationTreeOption => {
    const orgChildren = Array.isArray(node.children) ? node.children.map(toOption) : []
    const equipmentChildren = Array.isArray(node.equipments)
      ? node.equipments.map((equipment: any) => {
          const parameterChildren = Array.isArray(equipment.parameters)
            ? equipment.parameters.map((param: any) => ({
                id: `param-${equipment.id}-${param.id}`,
                name: String(param?.name ?? param?.code ?? param?.id ?? ''),
                parentId: `equipment-${node.id}-${equipment.id}`,
                lineId: String(node.id),
                filterId: String(param.id),
                deviceId: String(equipment.id),
                paramId: String(param.id),
                nodeType: 'param' as const
              }))
            : []

          return {
            id: `equipment-${node.id}-${equipment.id}`,
            name: String(equipment?.name ?? equipment?.id ?? ''),
            parentId: `org-${node.id}`,
            lineId: String(node.id),
            filterId: String(equipment.id),
            deviceId: String(equipment.id),
            nodeType: 'equipment' as const,
            children: parameterChildren.length ? parameterChildren : undefined
          }
        })
      : []
    const children = [...orgChildren, ...equipmentChildren]

    return {
      id: `org-${node.id}`,
      name: node.name,
      parentId: node.parentId != null && node.parentId !== '' && node.parentId !== 0 ? `org-${node.parentId}` : node.parentId,
      lineId: String(node.id),
      filterId: String(node.id),
      nodeType: 'org',
      children: children.length ? children : undefined
    }
  }

  return Array.isArray(orgTree) ? orgTree.map(toOption) : []
}

const findGroupNode = (groupId?: string): OrganizationTreeOption | undefined => {
  if (!groupId) return undefined
  const stack = [...groupOptions.value]
  while (stack.length > 0) {
    const node = stack.pop()!
    if (String(node.id) === String(groupId)) return node
    if (Array.isArray(node.children)) stack.push(...node.children)
  }
  return undefined
}

const getGroupLineId = (groupId?: string) => {
  if (!groupId) return undefined
  const node = findGroupNode(groupId)
  if (node?.nodeType === 'org' && node.filterId != null) return String(node.filterId)
  const matched = /^org-(.+)$/.exec(String(groupId))
  return matched?.[1] || groupId
}

const getDeviceLedgerRequestParams = (groupId?: string) => {
  const node = findGroupNode(groupId)
  if (!node) return groupId ? { deviceLine: getGroupLineId(groupId) } : {}
  if (node.nodeType === 'equipment' && node.filterId != null) return { dvId: node.filterId }
  if (node.nodeType === 'param' && node.deviceId != null) return { dvId: node.deviceId }
  if (node.filterId != null) return { deviceLine: node.filterId }
  return {}
}

const collectGroupIds = (groupId?: string) => {
  if (!groupId) return undefined
  const selectedNode = findGroupNode(groupId)
  if (selectedNode?.nodeType === 'equipment' && selectedNode.filterId != null) {
    return new Set([String(selectedNode.filterId)])
  }
  if (selectedNode?.nodeType === 'param' && selectedNode.deviceId != null) {
    return new Set([String(selectedNode.deviceId)])
  }

  const ids = new Set<string>()
  const lineId = getGroupLineId(groupId)
  const stack = [...groupOptions.value]

  while (stack.length > 0) {
    const node = stack.pop()!
    if (String(node.id) === groupId || String(node.filterId ?? '') === lineId) {
      const collectStack = [node]
      while (collectStack.length > 0) {
        const current = collectStack.pop()!
        if (current.nodeType === 'org' && current.filterId != null) ids.add(String(current.filterId))
        if (Array.isArray(current.children)) collectStack.push(...current.children)
      }
      break
    }
    if (Array.isArray(node.children)) stack.push(...node.children)
  }

  return ids
}

const hasIotDeviceId = (item: DeviceLedgerVO) => String(item.dvId ?? '').trim() !== ''

const getDeviceOptionLabel = (item: DeviceLedgerVO) => {
  const deviceName = String(item.deviceName ?? '').trim()
  const deviceCode = String(item.deviceCode ?? '').trim()
  return deviceName || deviceCode || String(item.iotDeviceName ?? item.iotDeviceCode ?? '').trim()
}

const collectDeviceOptionsByGroup = (groupId?: string) => {
  const deviceMap = new Map<string, OverviewOption>()
  const groupIds = collectGroupIds(groupId)
  const selectedNode = findGroupNode(groupId)
  deviceLedgerList.value.forEach((item) => {
    if (groupIds) {
      const targetId =
        selectedNode?.nodeType === 'equipment' || selectedNode?.nodeType === 'param'
          ? String(item.dvId ?? '')
          : String(item.deviceLine ?? '')
      if (!groupIds.has(targetId)) return
    }
    const deviceId = String(item.dvId ?? '').trim()
    if (!deviceId || deviceMap.has(deviceId)) return
    deviceMap.set(deviceId, {
      label: getDeviceOptionLabel(item),
      value: deviceId
    })
  })
  return Array.from(deviceMap.values())
}
const deviceOptions = computed(() => collectDeviceOptionsByGroup(queryParams.value.groupId))
const overviewData = ref<RunOverviewData>({
  metrics: [],
  hourlyStatus: [],
  summary: [],
  summaryTotalHours: 0,
  timelineRows: [],
  totalDevices: 0
})

const currentDeviceIds = computed(() =>
  queryParams.value.deviceId.length > 0
    ? queryParams.value.deviceId
    : deviceOptions.value.map((item) => item.value)
)

const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'
const IOT_DEVICE_TYPE = 2

const normalizeDateTime = (value: string, fallback: string) => {
  const parsed = dayjs(value)
  if (!parsed.isValid()) return fallback
  const now = dayjs()
  return (parsed.isAfter(now) ? now : parsed).format(DATE_TIME_FORMAT)
}

const normalizeTimeRange = (timeRange: [string, string]): [string, string] => {
  const nowText = dayjs().format(DATE_TIME_FORMAT)
  let start = normalizeDateTime(timeRange?.[0], dayjs().startOf('day').format(DATE_TIME_FORMAT))
  let end = normalizeDateTime(timeRange?.[1], nowText)
  if (dayjs(start).isAfter(dayjs(end))) start = end
  return [start, end]
}

const syncSafeTimeRange = () => {
  const safeTimeRange = normalizeTimeRange(queryParams.value.timeRange)
  if (
    safeTimeRange[0] !== queryParams.value.timeRange[0] ||
    safeTimeRange[1] !== queryParams.value.timeRange[1]
  ) {
    queryParams.value = {
      ...queryParams.value,
      timeRange: safeTimeRange
    }
  }
  return safeTimeRange
}

const buildOverviewRequestParams = () => {
  const [startTime, endTime] = syncSafeTimeRange()
  return {
    ids: currentDeviceIds.value.join(','),
    deviceType: IOT_DEVICE_TYPE,
    startTime,
    endTime,
    timelinePageNo: pageNo.value,
    timelinePageSize: pageSize.value
  }
}

const refreshData = async () => {
  const response = await DeviceOperationOverviewApi.getRunOverview(buildOverviewRequestParams())
  overviewData.value = {
    metrics: response?.metrics || [],
    hourlyStatus: response?.hourlyStatus || [],
    summary: response?.summary || [],
    summaryTotalHours: response?.summaryTotalHours || 0,
    timelineRows: response?.timelineRows || [],
    totalDevices: response?.totalDevices || 0
  }
  const maxPage = Math.max(1, Math.ceil(overviewData.value.totalDevices / pageSize.value))
  if (pageNo.value > maxPage) pageNo.value = maxPage
}

const resetToFirstPageAndRefresh = () => {
  if (pageNo.value !== 1) {
    pageNo.value = 1
    return
  }
  void refreshData()
}

const handleQuickRangeChange = (key: QuickRangeKey) => {
  queryParams.value = {
    ...queryParams.value,
    quickRange: key,
    timeRange: createDateRangeByQuickKey(key)
  }
  pageNo.value = 1
}

const handleQuery = () => {
  resetToFirstPageAndRefresh()
}

const resetQuery = () => {
  queryParams.value = buildDefaultQueryParams()
  pageSize.value = 10
  resetToFirstPageAndRefresh()
}

const handlePageSizeChange = (size: number) => {
  pageSize.value = size
  pageNo.value = 1
  void refreshData()
}

watch(pageNo, () => {
  void refreshData()
})

const getDeviceLedgerOptions = async (groupId?: string) => {
  const requestSeq = ++deviceLedgerRequestSeq
  const ledgerList = await DeviceLedgerApi.getDeviceLedgerList(getDeviceLedgerRequestParams(groupId))
  if (requestSeq !== deviceLedgerRequestSeq) return
  deviceLedgerList.value = Array.isArray(ledgerList) ? ledgerList.filter(hasIotDeviceId) : []
}

const getFilterOptions = async () => {
  const deviceLineTree = await OrganizationApi.deviceParameterAnalysis({
    keyword: undefined,
    deviceType: IOT_DEVICE_TYPE,
    showDevices: 1,
    filterNoDeviceLine: 1
  })
  groupOptions.value = normalizeDeviceParameterAnalysisTree(
    extractDeviceParameterAnalysisNodes(deviceLineTree)
  )
  await getDeviceLedgerOptions(queryParams.value.groupId)
}

watch(
  () => queryParams.value.groupId,
  (groupId) => {
    void getDeviceLedgerOptions(groupId)
  }
)
watch(
  deviceOptions,
  (options) => {
    if (queryParams.value.deviceId.length === 0) return
    const validDeviceIds = queryParams.value.deviceId.filter((deviceId) =>
      options.some((item) => item.value === deviceId)
    )
    if (validDeviceIds.length !== queryParams.value.deviceId.length) {
      queryParams.value = {
        ...queryParams.value,
        deviceId: validDeviceIds
      }
    }
  },
  { immediate: true }
)

onMounted(async () => {
  await getFilterOptions()
  await refreshData()
})
</script>

<style scoped lang="scss">
.run-overview-page {
  position: relative;
  min-width: 0;
  //padding-top: 8px;
}

.run-overview-page.is-fullscreen {
  position: fixed;
  inset: 0;
  z-index: 2000;
  width: 100vw;
  height: 100vh;
  padding: 16px;
  overflow: auto;
  background: #f5f7fa;
}

.run-overview-page__floating-tools {
  position: absolute;
  top: 8px;
  right: 12px;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 8px;
}

.run-overview-page__screenfull {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  background: #fff;
  border: 1px solid #e4e7ec;
  border-radius: 8px;
  box-shadow: 0 4px 14px rgba(15, 23, 42, 0.08);
  cursor: pointer;
  transition: all 0.2s ease;
}

.run-overview-page__screenfull:hover {
  background: #f8fbff;
  border-color: #bfd3ff;
  box-shadow: 0 8px 18px rgba(61, 132, 255, 0.12);
}
</style>
