<template>
  <div class="pasture-management">
    <yak-ol-map
      :key="pastureMapKey"
      ref="mapSceneRef"
      class="pasture-ol-map-layer"
      :active="props.active"
      :tile-url-template="pastureMapTileUrlTemplate"
      :center="pastureOlCenter"
      :initial-zoom="16"
      :max-zoom="21"
      :overlay-layer="pastureMapLayer"
      @ready="handlePastureOlReady"
      @map-click="handlePastureMapClick"
    />
    <div class="pasture-map-scrim"></div>
    <div v-if="!mapReady" class="pasture-map-loading">影像地图加载中</div>

    <div class="pasture-management-ui">
      <div class="pasture-left">
        <div class="pasture-side-stack is-left">
          <m-card class="pasture-side-card left-card" title="牧户区域统计" :width="sideCardWidth" :height="sideCardHeight">
            <business-panel-renderer :panel="householdColumnPanel" compact />
          </m-card>

          <m-card class="pasture-side-card left-card" title="草场面积分布" :width="sideCardWidth" :height="sideCardHeight">
            <business-panel-renderer :panel="grasslandColumnPanel" compact />
          </m-card>

          <m-card class="pasture-side-card left-card pasture-video-card" title="牧场实时监控" :width="sideCardWidth" :height="sideCardHeight">
            <div class="pasture-video-monitor">
              <button
                class="pasture-video-nav pasture-video-prev"
                type="button"
                :disabled="pastureVideoRows.length <= 1"
                aria-label="上一个监控点"
                @click="showPrevPastureVideo"
              >
                ‹
              </button>
              <div
                :key="currentPastureVideo.key"
                :ref="setPastureVideoScreenRef"
                class="pasture-video-screen"
                :class="{ 'is-playing': pastureVideoPlaying, 'is-loading': pastureVideoLoading }"
                :style="pastureVideoScreenStyle(currentPastureVideo)"
                @click="playCurrentPastureVideo"
              >
                <div :id="pastureVideoElementId" class="pasture-video-player"></div>
                <div v-if="!pastureVideoPlaying" class="pasture-video-scan"></div>
                <div v-if="!pastureVideoPlaying && currentPastureVideo.hdPlayUrl" class="pasture-video-play"></div>
                <div v-if="pastureVideoLoading" class="pasture-video-loading">加载中</div>
                <div v-if="pastureVideoError" class="pasture-video-error">{{ pastureVideoError }}</div>
                <div class="pasture-video-point-name">{{ currentPastureVideo.title }}</div>
              </div>
              <button
                class="pasture-video-nav pasture-video-next"
                type="button"
                :disabled="pastureVideoRows.length <= 1"
                aria-label="下一个监控点"
                @click="showNextPastureVideo"
              >
                ›
              </button>
            </div>
          </m-card>

          <m-card class="pasture-side-card left-card" title="牧户上线率" :width="sideCardWidth" :height="sideCardHeight">
            <business-panel-renderer :panel="onlinePanel" compact />
          </m-card>
        </div>
      </div>

      <div class="pasture-right">
        <div class="pasture-side-stack is-right">
          <m-card class="pasture-side-card right-card" title="牧户年龄统计" :width="sideCardWidth" :height="sideCardHeight">
            <business-panel-renderer :panel="agePanel" compact />
          </m-card>

          <m-card class="pasture-side-card right-card" title="牧户性别统计" :width="sideCardWidth" :height="sideCardHeight">
            <business-panel-renderer :panel="genderPanel" compact />
          </m-card>

          <m-card class="pasture-side-card right-card pasture-list-card" title="牧户列表" :width="sideCardWidth" :height="doubleCardHeight">
            <business-panel-renderer :panel="pastureListPanel" compact @row-select="handlePastureSelect" />
          </m-card>
        </div>
      </div>

      <div class="pasture-map-layer-tools">
        <layer-panel
          v-model="activeLayerKey"
          v-model:selected-legend-key="selectedLegendKey"
          topic-key="grassland"
          :layers="pastureLayerOptions"
          :legends="pastureMapLayer?.legends || []"
          :width="232"
          :height="430"
        />
      </div>

      <div v-if="grasslandFeaturePanelVisible" class="pasture-map-feature-info" :style="grasslandFeaturePanelStyle">
        <feature-info-panel
          :feature="selectedGrasslandFeature"
          :empty="grasslandFeatureEmpty"
          :loading="grasslandFeatureLoading"
          :width="460"
          :height="grasslandFeaturePanelHeight"
          compact-grassland
        />
      </div>
    </div>
  </div>
</template>

<script setup>
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue"
import EZUIKit from "ezuikit-js"
import mCard from "@/components/mCard/index.vue"
import YakOlMap from "@/views/yakManagement/YakOlMap.vue"
import LayerPanel from "@/views/forestGrassWet/components/LayerPanel.vue"
import BusinessPanelRenderer from "@/views/forestGrassWet/components/BusinessPanelRenderer.vue"
import FeatureInfoPanel from "@/views/forestGrassWet/components/FeatureInfoPanel.vue"
import { getHongyuanTownshipNameByCode, hongyuanTownshipDisplayOrder, hongyuanTownshipsGeoJsonRaw, isHongyuanTownshipName } from "@/config/townshipBoundaries"
import { getHomeMonitorings } from "@/services/homeVideoApi"
import { getGrasslandClickInfo, getPastureGrasslandsById, getPastureProfile, getPasturesDetailsById } from "@/services/pastureApi"
import liveSceneImage from "@/assets/images/gd-live/live-scene-1.png"
import {
  pastureAgeRows,
  pastureCoreRows,
  pastureFallbackProfile,
  pastureGenderRows,
  pastureGrasslandRows,
  pastureHouseholdTotal,
  pastureListRows,
  pastureMapRows,
  pastureOnlineRows,
  pastureTopStats,
  pastureTownRows,
} from "./data"

const props = defineProps({
  active: {
    type: Boolean,
    default: true,
  },
})
const emit = defineEmits(["module-stats", "loading-progress", "map-ready"])
const sideCardWidth = 398
const cardGap = 12
const screenViewportHeight = ref(typeof window === "undefined" ? 1080 : window.innerHeight)
const sidePanelTop = 128
const sidePanelBottomGap = 8
const sideCardMinHeight = 227
const sideCardHeight = computed(() => {
  const viewportHeight = Math.max(1080, Number(screenViewportHeight.value) || 1080)
  const fittedHeight = Math.floor((viewportHeight - sidePanelTop - sidePanelBottomGap - cardGap * 3) / 4)
  return Math.max(sideCardMinHeight, fittedHeight)
})
const doubleCardHeight = computed(() => sideCardHeight.value * 2 + cardGap)
const mapSceneRef = ref(null)
const mapReady = ref(false)
const pastureMapKey = ref(0)
const selectedGrasslandFeature = ref(null)
const grasslandFeatureEmpty = ref(false)
const grasslandFeatureLoading = ref(false)
const grasslandFeaturePoint = ref(null)
const apiProfile = ref(pastureFallbackProfile)
const pastureRows = ref(pastureListRows)
const pastureBoundaryLayerKey = "pasture-grassland-boundary"
const activeLayerKey = ref(pastureBoundaryLayerKey)
const selectedLegendKey = ref("")
const pastureTopStatNames = ["牧户数量", "户均牦牛头数", "户均草地面积", "联户数量"]
const pasturePalette = ["#23f6c8", "#35d8ff", "#f0d96a", "#76e36b", "#ff9a5c", "#a59cff", "#20e5c4"]
const pastureVideoElementId = `pasture-video-player-${Math.random().toString(36).slice(2)}`
const pastureVideoLoadTimeoutMs = 12000
const pastureMapTileUrlTemplate = "https://qkl-map.oss-cn-chengdu.aliyuncs.com/hy-result/{z}/{x}/{y}.png"
const pastureOlCenter = [103.07984, 33.029058]
const pastureBoundarySldBody = `<?xml version="1.0" encoding="UTF-8"?><StyledLayerDescriptor version="1.0.0" xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><NamedLayer><Name>t_grassland_result_caodi</Name><UserStyle><Title>pasture boundary</Title><FeatureTypeStyle><Rule><PolygonSymbolizer><Fill><CssParameter name="fill">#FFE35A</CssParameter><CssParameter name="fill-opacity">0</CssParameter></Fill><Stroke><CssParameter name="stroke">#FFE35A</CssParameter><CssParameter name="stroke-width">2.4</CssParameter><CssParameter name="stroke-opacity">0.98</CssParameter></Stroke></PolygonSymbolizer></Rule></FeatureTypeStyle></UserStyle></NamedLayer></StyledLayerDescriptor>`
const emptyPastureVideo = {
  key: "pasture-empty-video",
  title: "暂无在线可播放摄像头",
  town: "红原县",
  scene: "牧场监控",
  device: "",
  channel: "",
  cameraCount: 0,
  status: "待接入",
  onlineRate: 0,
  updateTime: "",
  operator: "等待在线摄像头",
  progress: 0,
  image: liveSceneImage,
  playUrl: "",
  hdPlayUrl: "",
  token: "",
}
const pastureVideoScreenRef = ref(null)
const pastureVideoRows = ref([])
const activePastureVideoManualIndex = ref(0)
const pastureVideoPlaying = ref(false)
const pastureVideoLoading = ref(false)
const pastureVideoError = ref("")
let destroyed = false
let pastureEnrichToken = 0
let pastureEnrichTimer = null
let pastureVideoPlayer = null
let pastureVideoPlayerReady = false
let pastureVideoPlayRun = 0
let pastureVideoLoadTimeoutTimer = null
let grasslandFeatureRequestId = 0
const pastureFallbackTownshipById = {
  "4028abc18eecd2af018eecdc44b7086b": "瓦切镇",
  "402881828b486d54018b48741f590270": "查尔玛乡",
  "402881828b486d54018b48740ed7024a": "麦洼乡",
  "402881828b486d54018b48732e1c0044": "安曲镇",
  "4028abc18eec9b97018eeca1538900db": "瓦切镇",
  "402881828b486d54018b48740fb1024c": "安曲镇",
}
const pastureFallbackTownshipByName = {
  "尼尕尔让": "瓦切镇",
  "更迫尕": "查尔玛乡",
  "王西卓玛": "麦洼乡",
  "扎花克周": "安曲镇",
  "恰波": "瓦切镇",
  "热布丹": "安曲镇",
  "求甲": "阿木乡",
  "罗尔旦": "阿木乡",
  "扎尔江": "阿木乡",
  "达尔基": "阿木乡",
  "尼科": "瓦切镇",
  "班洛": "阿木乡",
  "拉木共": "阿木乡",
  "尕尔玛": "瓦切镇",
  "尕尔丹": "安曲镇",
  "泽旺甲": "瓦切镇",
  "阿尕": "安曲镇",
  "陈理": "瓦切镇",
  "贡秋卓玛": "阿木乡",
  "更多": "瓦切镇",
  "阿泽": "瓦切镇",
  "达勒错": "色地镇",
  "泽朗罗日": "安曲镇",
  "仁青": "瓦切镇",
  "贡多": "阿木乡",
  "泽周": "刷经寺镇",
  "泽科": "瓦切镇",
  "扎巴": "阿木乡",
  "泽真": "瓦切镇",
  "索拉姐": "瓦切镇",
  "尕让罗周": "瓦切镇",
  "尕让拉姆": "瓦切镇",
  "更让尼玛": "阿木乡",
  "向秋卓玛": "瓦切镇",
  "东科": "阿木乡",
  "扑瓦甲": "阿木乡",
}
const pastureTownshipFeatures = normalizeTownshipFeatures(hongyuanTownshipsGeoJsonRaw)

const pastureLayerOptions = computed(() => [
  {
    key: pastureBoundaryLayerKey,
    sourceType: "wms",
    renderMode: "image",
    layerName: "ne:t_grassland_result_caodi",
    name: "草场边界",
    styles: "",
    sldBody: pastureBoundarySldBody,
    color: "#FFE35A",
    fillColor: "rgba(255, 227, 90, 0.04)",
    strokeColor: "#FFE35A",
    glowColor: "rgba(255, 227, 90, 0.34)",
    zIndex: 35,
    opacity: 0.96,
    queryBuffer: 8,
    mapView: {
      targetLngLat: [103.07984, 33.029058],
      distanceScale: 0.66,
      targetLocalZ: 0.78,
      duration: 0.85,
    },
    legends: [
      {
        key: "pasture-grassland-boundary",
        name: "草场边界",
        fillColor: "#FFE35A",
        strokeColor: "#FFE35A",
        selectable: false,
      },
    ],
  },
])
const basePastureMapLayer = computed(() =>
  activeLayerKey.value ? pastureLayerOptions.value.find((layer) => layer.key === activeLayerKey.value) || null : null
)
const selectedLegend = computed(() =>
  (basePastureMapLayer.value?.legends || []).find((item) => item.key === selectedLegendKey.value) || null
)
const pastureMapLayer = computed(() => {
  const layer = basePastureMapLayer.value
  if (!layer) return null
  if (!selectedLegend.value?.cqlFilter) return layer
  return {
    ...layer,
    cqlFilter: selectedLegend.value.cqlFilter,
    activeLegend: selectedLegend.value,
    name: selectedLegend.value.name || layer.name,
  }
})
const grasslandFeaturePanelVisible = computed(() => Boolean(grasslandFeaturePoint.value))
const grasslandFeaturePanelHeight = computed(() => {
  if (grasslandFeatureLoading.value || grasslandFeatureEmpty.value || !selectedGrasslandFeature.value) return 186
  const rowCount = estimateGrasslandFeatureRowCount(selectedGrasslandFeature.value)
  return Math.min(500, Math.max(320, 86 + Math.min(rowCount, 13) * 34))
})
const grasslandFeaturePanelStyle = computed(() => {
  if (!grasslandFeaturePoint.value) return {}
  const panelWidth = 460
  const panelHeight = grasslandFeaturePanelHeight.value
  const margin = 24
  const point = toPastureScreenLocalPoint(grasslandFeaturePoint.value)
  const xOffset = point.x > 1420 ? -panelWidth - 28 : 28
  const x = Math.min(Math.max(point.x + xOffset, margin), 1920 - panelWidth - margin)
  const y = Math.min(Math.max(point.y - panelHeight - 72, 188), 1080 - panelHeight - 132)
  return {
    left: `${x}px`,
    top: `${y}px`,
    right: "auto",
    bottom: "auto",
  }
})
const coreRows = computed(() => normalizeCoreRows(apiProfile.value.core).length ? normalizeCoreRows(apiProfile.value.core) : pastureCoreRows)
const townRows = computed(() => scaleRowsToTargetTotal(
  normalizeTownRows(apiProfile.value.distribution).length ? normalizeTownRows(apiProfile.value.distribution) : pastureTownRows,
  pastureHouseholdTotal,
))
const onlineRows = computed(() => {
  const normalizedRows = normalizeOnlineRows(apiProfile.value.online)
  return completePastureOnlineRows(normalizedRows.length ? normalizedRows : pastureOnlineRows)
})
const ageRows = computed(() => scaleRowsToTargetTotal(
  normalizeAgeRows(apiProfile.value.age).length ? normalizeAgeRows(apiProfile.value.age) : pastureAgeRows,
  pastureHouseholdTotal,
))
const genderRows = computed(() => scaleRowsToTargetTotal(
  normalizeGenderRows(apiProfile.value.age).length ? normalizeGenderRows(apiProfile.value.age) : pastureGenderRows,
  pastureHouseholdTotal,
))
const grasslandRows = computed(() => {
  return completePastureGrasslandRows(pastureGrasslandRows)
})
const mapRanking = computed(() => ({
  title: "牧户分布排行",
  metricLabel: "牧户",
  unit: "户",
  hideMapLabels: false,
  hideTownNames: false,
  showBars: false,
  showPointMarkers: false,
  showMissingTownStats: false,
  rows: (townRows.value.length ? townRows.value : pastureMapRows)
    .map((item) => {
      const value = Number(item.value)
      if (!item.name || !Number.isFinite(value) || value <= 0) return null
      return {
        name: item.name,
        value,
        unit: item.unit || "户",
      }
    })
    .filter(Boolean),
}))

const townPanel = computed(() => ({
  key: "pasture-town",
  type: "bar",
  variant: "distribution",
  rows: townRows.value,
}))
const grasslandPanel = computed(() => ({
  key: "pasture-grassland-area",
  type: "heat",
  rows: grasslandRows.value,
}))
const householdColumnPanel = computed(() => ({
  key: "pasture-household-column",
  type: "column",
  scrollable: false,
  rows: normalizeVisualRows(townRows.value).slice(0, 10).map((item, index) => ({
    ...item,
    unit: item.unit || "户",
    color: pasturePalette[index % pasturePalette.length],
  })),
}))
const grasslandColumnPanel = computed(() => ({
  key: "pasture-grassland-column",
  type: "bar",
  variant: "raceBar",
  rows: grasslandRows.value.map((item, index) => ({
    ...item,
    unit: item.unit || "万亩",
    color: pasturePalette[(index + 1) % pasturePalette.length],
  })),
}))
const onlinePanel = computed(() => ({
  key: "pasture-online",
  type: "line",
  rows: onlineRows.value,
}))
const agePanel = computed(() => ({
  key: "pasture-age",
  type: "column",
  variant: "age",
  rows: ageRows.value,
}))
const genderPanel = computed(() => ({
  key: "pasture-gender",
  type: "donut",
  rows: genderRows.value,
}))
const pastureListPanel = computed(() => ({
  key: "pasture-herdsman-list",
  type: "projectTable",
  searchPlaceholder: "搜索牧户 / 乡镇",
  emptyText: "无匹配牧户",
  maxRows: 300,
  searchKeys: ["name", "herdsmanName", "herdsman_name", "phone", "phonenumber", "idCard", "id_card", "township", "id", "grasslandArea", "searchText"],
  columns: [
    { key: "name", label: "牧户名称", field: "name", width: "30%" },
    { key: "grasslandArea", label: "草场面积", field: "grasslandArea", width: "28%", align: "right", strong: true, format: "number", unit: "亩" },
    { key: "township", label: "区域", field: "township", width: "24%" },
    { key: "locate", label: "定位", field: "locateText", width: "18%", align: "center", fallback: "定位", icon: "locate" },
  ],
  rows: pastureRows.value,
}))
const activePastureVideoIndex = computed(() => {
  const length = pastureVideoRows.value.length
  if (!length) return 0
  return activePastureVideoManualIndex.value % length
})
const currentPastureVideo = computed(() => pastureVideoRows.value[activePastureVideoIndex.value] || emptyPastureVideo)

function normalizeVisualRows(rows = []) {
  return (Array.isArray(rows) ? rows : [])
    .map((item, index) => ({
      ...item,
      key: item.key || item.code || item.name || `${index}`,
      name: item.name || item.areaName || item.area_name || item.townName || "未标注",
      value: Number(item.value ?? item.number ?? item.count ?? item.num) || 0,
      unit: item.unit || "",
    }))
    .filter((item) => item.name && Number.isFinite(item.value) && item.value >= 0)
}

function scaleRowsToTargetTotal(rows = [], targetTotal = pastureHouseholdTotal) {
  const sourceRows = Array.isArray(rows) ? rows : []
  const sourceTotal = sourceRows.reduce((sum, item) => sum + (Number(item.value) || 0), 0)
  if (!sourceRows.length || !sourceTotal || !targetTotal || sourceTotal === targetTotal) return sourceRows
  const scaledRows = sourceRows.map((item) => ({
    ...item,
    value: Math.round(((Number(item.value) || 0) / sourceTotal) * targetTotal),
  }))
  const delta = targetTotal - scaledRows.reduce((sum, item) => sum + (Number(item.value) || 0), 0)
  if (delta && scaledRows.length) {
    const targetIndex = scaledRows.reduce((bestIndex, item, index) => (
      Number(item.value) > Number(scaledRows[bestIndex]?.value || 0) ? index : bestIndex
    ), 0)
    scaledRows[targetIndex].value += delta
  }
  return scaledRows
}

function updateScreenViewportHeight() {
  screenViewportHeight.value = typeof window === "undefined" ? 1080 : window.innerHeight
}

onMounted(() => {
  updateScreenViewportHeight()
  window.addEventListener("resize", updateScreenViewportHeight)
  emitPastureTopStats()
  initPastureMap()
  loadPastureProfile()
  loadPastureMonitorings()
})

onBeforeUnmount(() => {
  destroyed = true
  window.removeEventListener("resize", updateScreenViewportHeight)
  stopPastureVideoPlayer()
  if (pastureEnrichTimer) window.clearTimeout(pastureEnrichTimer)
  pastureEnrichToken += 1
})

watch(
  () => props.active,
  (active) => {
    if (!active) stopPastureVideoPlayer({ resetError: true })
    if (!mapSceneRef.value) return
    if (active) {
      mapSceneRef.value.refreshView?.()
    } else {
      if (pastureEnrichTimer) window.clearTimeout(pastureEnrichTimer)
      pastureEnrichTimer = null
      pastureEnrichToken += 1
    }
  },
)

async function loadPastureMonitorings() {
  try {
    const rows = await getHomeMonitorings()
    if (destroyed) return
    pastureVideoRows.value = rows
    activePastureVideoManualIndex.value = 0
    pastureVideoError.value = rows.length ? "" : "暂无在线可播放摄像头"
  } catch (error) {
    if (destroyed) return
    pastureVideoRows.value = []
    pastureVideoError.value = "监控接口暂不可用"
    window.setTimeout(() => {
      if (!destroyed && pastureVideoError.value === "监控接口暂不可用") pastureVideoError.value = ""
    }, 2400)
  }
}

function setPastureVideoScreenRef(el) {
  pastureVideoScreenRef.value = el || null
}

function pastureVideoScreenStyle(live) {
  const image = live?.image || liveSceneImage
  return pastureVideoPlaying.value
    ? undefined
    : {
        backgroundImage: `linear-gradient(180deg, rgba(0, 16, 26, 0.08), rgba(0, 16, 26, 0.32)), url(${image})`,
      }
}

function showPrevPastureVideo() {
  stepPastureVideo(-1)
}

function showNextPastureVideo() {
  stepPastureVideo(1)
}

function stepPastureVideo(step) {
  const length = pastureVideoRows.value.length
  if (!length) return
  stopPastureVideoPlayer({ resetError: true })
  activePastureVideoManualIndex.value = (activePastureVideoIndex.value + step + length) % length
}

async function playCurrentPastureVideo() {
  const live = currentPastureVideo.value
  if (!live?.hdPlayUrl || !live?.token) {
    pastureVideoError.value = "暂无在线可播放摄像头"
    return
  }
  if (pastureVideoPlaying.value || pastureVideoLoading.value) return

  if (pastureVideoPlayer) stopPastureVideoPlayer({ resetError: true })
  pastureVideoError.value = ""
  pastureVideoLoading.value = true
  await nextTick()

  const playRun = ++pastureVideoPlayRun
  let player = null
  startPastureVideoLoadTimeout(playRun)
  try {
    const screenRect = pastureVideoScreenRef.value?.getBoundingClientRect?.()
    const playerWidth = Math.max(300, Math.floor(screenRect?.width || 340))
    const playerHeight = Math.max(80, Math.floor(screenRect?.height || 120))
    player = new EZUIKit.EZUIKitPlayer({
      id: pastureVideoElementId,
      accessToken: live.token,
      url: live.hdPlayUrl,
      audio: 0,
      width: playerWidth,
      height: playerHeight,
      themeData: buildPastureEzvizThemeData(),
      handleError(error) {
        if (playRun !== pastureVideoPlayRun) return
        clearPastureVideoLoadTimeout()
        console.warn("Pasture EZUIKit player error", error)
        pastureVideoError.value = "视频加载失败"
        pastureVideoLoading.value = false
        pastureVideoPlaying.value = false
        pastureVideoPlayRun += 1
        pastureVideoPlayer = null
        pastureVideoPlayerReady = false
        clearPastureVideoElement()
      },
      handleSuccess() {
        if (playRun !== pastureVideoPlayRun) return
        clearPastureVideoLoadTimeout()
        pastureVideoError.value = ""
        pastureVideoPlayerReady = true
        pastureVideoLoading.value = false
      },
      fullScreenChangeCallBack() {},
    })
    if (playRun !== pastureVideoPlayRun) {
      stopPastureEzvizPlayer(player)
      clearPastureVideoElement()
      return
    }
    pastureVideoPlayer = player
    pastureVideoPlaying.value = true
  } catch (error) {
    clearPastureVideoLoadTimeout()
    console.warn("Pasture EZUIKit init error", error)
    pastureVideoError.value = "视频加载失败"
    stopPastureVideoPlayer()
  }
}

function stopPastureVideoPlayer(options = {}) {
  const { clearElement = true, resetError = false } = options
  clearPastureVideoLoadTimeout()
  const player = pastureVideoPlayer
  pastureVideoPlayer = null
  pastureVideoPlayerReady = false
  pastureVideoPlayRun += 1
  if (player) stopPastureEzvizPlayer(player)
  if (clearElement) clearPastureVideoElement()
  pastureVideoPlaying.value = false
  pastureVideoLoading.value = false
  if (resetError) pastureVideoError.value = ""
}

function startPastureVideoLoadTimeout(playRun) {
  clearPastureVideoLoadTimeout()
  pastureVideoLoadTimeoutTimer = window.setTimeout(() => {
    if (playRun !== pastureVideoPlayRun || !pastureVideoLoading.value) return
    pastureVideoError.value = "视频加载超时"
    stopPastureVideoPlayer({ resetError: false })
  }, pastureVideoLoadTimeoutMs)
}

function clearPastureVideoLoadTimeout() {
  if (!pastureVideoLoadTimeoutTimer) return
  window.clearTimeout(pastureVideoLoadTimeoutTimer)
  pastureVideoLoadTimeoutTimer = null
}

function stopPastureEzvizPlayer(player) {
  if (!player || typeof player.stop !== "function") return
  try {
    const stopped = player.stop()
    if (stopped && typeof stopped.catch === "function") {
      stopped.catch(() => {})
    }
  } catch (error) {
    console.warn("Pasture EZUIKit stop error", error)
  }
}

function clearPastureVideoElement() {
  if (typeof document === "undefined") return
  const container = document.getElementById(pastureVideoElementId)
  if (container) {
    container.replaceChildren()
  }
}

function buildPastureEzvizThemeData() {
  return {
    header: {
      color: "#1890ff",
      activeColor: "#ffffff",
      backgroundColor: "#000000",
      btnList: [
        { iconId: "deviceID", part: "left", defaultActive: 0, memo: "顶部设备序列号", isrender: 0 },
        { iconId: "deviceName", part: "left", defaultActive: 0, memo: "顶部设备名称", isrender: 0 },
        { iconId: "cloudRec", part: "right", defaultActive: 0, memo: "云存储", isrender: 0 },
        { iconId: "rec", part: "right", defaultActive: 0, memo: "SD卡回放", isrender: 0 },
      ],
    },
    footer: {
      color: "#ffffff",
      activeColor: "#1890ff",
      backgroundColor: "#00000021",
      btnList: [
        { iconId: "play", part: "left", defaultActive: 1, memo: "播放", isrender: 1 },
        { iconId: "capturePicture", part: "left", defaultActive: 0, memo: "截屏按钮", isrender: 0 },
        { iconId: "sound", part: "left", defaultActive: 0, memo: "声音按钮", isrender: 1 },
        { iconId: "pantile", part: "left", defaultActive: 0, memo: "云台控制按钮", isrender: 1 },
        { iconId: "recordvideo", part: "left", defaultActive: 0, memo: "录制按钮", isrender: 0 },
        { iconId: "zoom", part: "left", defaultActive: 0, memo: "电子放大", isrender: 1 },
        { iconId: "hd", part: "right", defaultActive: 0, memo: "清晰度切换按钮", isrender: 1 },
        { iconId: "expend", part: "right", defaultActive: 0, memo: "全局全屏按钮", isrender: 1 },
      ],
    },
  }
}

async function loadPastureProfile() {
  try {
    const profile = await getPastureProfile()
    if (destroyed) return
    apiProfile.value = {
      ...pastureFallbackProfile,
      ...profile,
    }
    emitPastureTopStats()
    const normalizedList = normalizePastureRows(profile.list)
    pastureRows.value = normalizedList.length ? normalizedList : sortPastureRows(pastureListRows)
    schedulePastureEnrichment(pastureRows.value)
  } catch (error) {
    apiProfile.value = pastureFallbackProfile
    pastureRows.value = sortPastureRows(pastureListRows)
  }
}

function schedulePastureEnrichment(rows = pastureRows.value, delay = 600) {
  if (destroyed || !props.active) return
  if (pastureEnrichTimer) window.clearTimeout(pastureEnrichTimer)
  pastureEnrichTimer = window.setTimeout(() => {
    pastureEnrichTimer = null
    if (destroyed || !props.active) return
    enrichPastureRowsWithGrasslands(rows)
  }, delay)
}

async function enrichPastureRowsWithGrasslands(rows = []) {
  const token = ++pastureEnrichToken
  const targets = (Array.isArray(rows) ? rows : [])
    .filter(shouldEnrichPastureTownship)
    .slice(0, 60)
  if (!targets.length) return
  const results = await mapWithConcurrency(targets, 5, async (row) => {
    const pastureId = getRealPastureId(row)
    const rowGrasslands = normalizeRowGrasslands(row)
    let grasslands = rowGrasslands
    let detail = null
    let township = getPastureTownship(row, rowGrasslands) || getPastureFallbackTownship(row, row.name, pastureId)
    try {
      if (pastureId && (!township || !grasslands.length)) {
        grasslands = normalizeGrasslandDetailRows(await getPastureGrasslandsById(pastureId))
      }
    } catch (error) {
      grasslands = rowGrasslands
    }
    township = township || getPastureTownship(row, grasslands) || getPastureFallbackTownship(row, row.name, pastureId)
    if (!township) {
      try {
        detail = pastureId ? await getPasturesDetailsById(pastureId) : null
      } catch (error) {
        detail = null
      }
      const detailGrasslands = normalizeRowGrasslands(detail)
      township = getPastureTownshipFromDetails(detail) || getPastureTownship(detail, detailGrasslands)
      if (detailGrasslands.length && !grasslands.length) grasslands = detailGrasslands
    }
    township = township || getPastureFallbackTownship(row, row.name, pastureId)
    const canLocate = Boolean(pastureId || grasslands.some(hasGrasslandGeometry) || hasGrasslandGeometry(row))
    if (!township && !grasslands.length && canLocate === row.canLocate) return null
    const result = {
      key: row.key,
      id: row.id,
      realPastureId: pastureId,
      name: row.name,
      township: township || row.township,
      grasslands: grasslands.length ? grasslands : row.grasslands,
      canLocate,
      locateText: canLocate ? "定位" : "--",
    }
    applyPastureEnrichmentResult(result, token)
    return result
  })
  if (destroyed || token !== pastureEnrichToken) return
  const resultMap = new Map()
  results.filter(Boolean).forEach((item) => {
    ;[item.key, item.id, item.realPastureId, item.name].filter(Boolean).forEach((key) => {
      resultMap.set(String(key), item)
    })
  })
  if (!resultMap.size) return
  applyPastureEnrichmentResults(resultMap)
}

function shouldEnrichPastureTownship(row = {}) {
  const township = String(row?.township || "").trim()
  if (township && township !== "--") return false
  return pickPastureArea(row) > 0 || isPastureLocatable(row)
}

function applyPastureEnrichmentResult(result, token = pastureEnrichToken) {
  if (!result || destroyed || token !== pastureEnrichToken) return
  const resultMap = new Map()
  ;[result.key, result.id, result.realPastureId, result.name].filter(Boolean).forEach((key) => {
    resultMap.set(String(key), result)
  })
  applyPastureEnrichmentResults(resultMap)
}

function applyPastureEnrichmentResults(resultMap) {
  if (!resultMap?.size) return
  pastureRows.value = pastureRows.value.map((row) => {
    const result = [row.key, row.id, row.realPastureId, row.name]
      .filter(Boolean)
      .map((key) => resultMap.get(String(key)))
      .find(Boolean)
    if (!result) return row
    const township = result.township && result.township !== "--" ? result.township : row.township
    return {
      ...row,
      township,
      grasslands: result.grasslands,
      canLocate: result.canLocate,
      locateText: result.locateText,
      searchText: [row.name, township, row.id].filter(Boolean).join(" "),
    }
  })
}

function normalizeGrasslandDetailRows(rows = []) {
  const normalized = []
  ;(Array.isArray(rows) ? rows : []).forEach((item) => {
    const childRows = normalizeRowGrasslands(item)
    if (childRows.length) normalized.push(...childRows)
    else if (item && typeof item === "object") normalized.push(item)
  })
  return normalized
}

function getPastureTownshipFromDetails(detail) {
  const rows = Array.isArray(detail) ? detail : Array.isArray(detail?.rows) ? detail.rows : Array.isArray(detail?.list) ? detail.list : Array.isArray(detail?.data) ? detail.data : [detail]
  for (const row of rows.filter(Boolean)) {
    const fields = Array.isArray(row?.fields) ? row.fields : Array.isArray(row?.children) ? row.children : []
    const directValue = pickValue(row, ["township", "townName", "town_name", "areaName", "area_name", "fullName", "full_name", "address", "牧场所在区域"])
    if (directValue) return normalizePastureTownName(extractPastureTownName(directValue))
    for (const field of fields) {
      const label = String(field?.name || field?.label || field?.title || "").trim()
      const value = pickValue(field, ["value", "text", "content"])
      if (value && /区域|地址|乡镇/.test(label)) return normalizePastureTownName(extractPastureTownName(value))
    }
    const values = Object.entries(row || {})
      .filter(([key]) => /fullName|full_name|address|areaName|area_name|town/i.test(key))
      .map(([, value]) => value)
    for (const value of values) {
      if (value) return normalizePastureTownName(extractPastureTownName(value))
    }
  }
  return ""
}

function extractPastureTownName(value) {
  const text = String(value || "").trim()
  if (!text || text === "--") return ""
  const directMatch = text.match(/([\u4e00-\u9fa5]{1,12}(?:镇|乡))/)
  return directMatch?.[1] || text
}

async function mapWithConcurrency(items, limit, worker) {
  const results = new Array(items.length)
  let nextIndex = 0
  const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {
    while (nextIndex < items.length) {
      const currentIndex = nextIndex
      nextIndex += 1
      results[currentIndex] = await worker(items[currentIndex], currentIndex)
    }
  })
  await Promise.all(workers)
  return results
}

async function handlePastureSelect(row) {
  if (!isPastureLocatable(row)) {
    mapSceneRef.value?.focusPasture?.({ name: row?.name })
    return
  }
  const pastureId = getRealPastureId(row)
  let grasslands = []
  if (pastureId) {
    try {
      grasslands = await getPastureGrasslandsById(pastureId)
    } catch (error) {
      grasslands = []
    }
  }
  const rowGrasslands = normalizeRowGrasslands(row)
  const geometryGrasslands = grasslands.filter(hasGrasslandGeometry)
  const pastureGrasslands = geometryGrasslands.length ? geometryGrasslands : rowGrasslands
  if (!pastureGrasslands.length) {
    mapSceneRef.value?.focusPasture?.({ name: row?.name })
    return
  }
  mapSceneRef.value?.focusPasture?.({
    name: row?.name,
    grasslands: pastureGrasslands,
  })
}

function initPastureMap() {
  mapReady.value = false
  emit("loading-progress", { progress: 0 })
}

function handlePastureOlReady() {
  mapReady.value = true
  emit("loading-progress", { progress: 100 })
  emit("map-ready")
}

function isPastureGrasslandLayer(layer = pastureMapLayer.value) {
  const key = String(layer?.key || "").toLowerCase()
  const layerName = String(layer?.layerName || "").toLowerCase()
  return key === pastureBoundaryLayerKey || layerName.includes("t_grassland_result_caodi")
}

function toPastureScreenLocalPoint(point = {}) {
  const screen = document.querySelector(".pasture-management")
  const rect = screen?.getBoundingClientRect()
  if (!rect) return { x: Number(point.x) || 960, y: Number(point.y) || 540 }
  return {
    x: (Number(point.x) || rect.left + rect.width / 2) - rect.left,
    y: (Number(point.y) || rect.top + rect.height / 2) - rect.top,
  }
}

function estimateGrasslandFeatureRowCount(feature) {
  const properties = feature?.properties || {}
  return Math.min(10, Object.entries(properties).filter(([, value]) => value !== undefined && value !== null && value !== "").length)
}

function normalizeGrasslandClickFeature(detail = {}, payload = {}) {
  return {
    id: detail.id,
    properties: detail,
    feature: {
      type: "Feature",
      properties: detail,
      geometry: null,
    },
    layer: pastureMapLayer.value || basePastureMapLayer.value,
    lngLat: payload.lngLat,
  }
}

function closeGrasslandFeaturePanel() {
  selectedGrasslandFeature.value = null
  grasslandFeaturePoint.value = null
  grasslandFeatureEmpty.value = false
  grasslandFeatureLoading.value = false
}

async function handlePastureMapClick(payload = {}) {
  if (!isPastureGrasslandLayer(payload.overlayLayer || pastureMapLayer.value)) return
  const lng = Number(payload?.lngLat?.lng)
  const lat = Number(payload?.lngLat?.lat)
  if (!Number.isFinite(lng) || !Number.isFinite(lat)) return
  const requestId = ++grasslandFeatureRequestId
  grasslandFeaturePoint.value = payload.screen || { x: 960, y: 540 }
  selectedGrasslandFeature.value = null
  grasslandFeatureEmpty.value = false
  grasslandFeatureLoading.value = true
  try {
    const detail = await getGrasslandClickInfo({ lng, lat })
    if (requestId !== grasslandFeatureRequestId) return
    if (!detail?.id) {
      closeGrasslandFeaturePanel()
      return
    }
    selectedGrasslandFeature.value = normalizeGrasslandClickFeature(detail, payload)
    grasslandFeatureEmpty.value = false
  } catch (error) {
    if (requestId !== grasslandFeatureRequestId) return
    closeGrasslandFeaturePanel()
  } finally {
    if (requestId === grasslandFeatureRequestId) {
      grasslandFeatureLoading.value = false
    }
  }
}

function refreshMapView() {
  mapSceneRef.value?.refreshView?.()
}

function resetMap() {
  if (mapSceneRef.value?.hasMap?.()) {
    mapReady.value = true
    mapSceneRef.value?.resetView?.()
    return
  }
  mapReady.value = false
  pastureMapKey.value += 1
  nextTick(() => {
    window.setTimeout(() => {
      mapSceneRef.value?.refreshView?.()
    }, 160)
  })
}

defineExpose({
  refreshMapView,
  resetMap,
})

function emitPastureTopStats() {
  emit("module-stats", {
    moduleKey: "pasture-management",
    items: buildPastureTopStats().map((item) => ({
      zh: item.name,
      name: item.name,
      value: item.value,
      unit: item.unit,
      decimals: Number.isInteger(Number(item.value)) ? 0 : 2,
    })),
  })
}

function buildPastureTopStats() {
  const metrics = new Map()
  ;(Array.isArray(coreRows.value) ? coreRows.value : []).forEach((item) => {
    if (!item?.name || isHiddenCoreMetric(item.name)) return
    metrics.set(item.name, item)
  })
  const pastureTotalMetric = metrics.get("牧户数量") || pastureTopStats.find((item) => item.name === "牧户数量" || item.zh === "牧户数量")
  metrics.set("牧户数量", {
    ...pastureTotalMetric,
    name: "牧户数量",
    value: pastureHouseholdTotal,
    unit: pastureTotalMetric?.unit || "户",
  })
  return pastureTopStatNames
    .map((name) => metrics.get(name) || pastureTopStats.find((item) => item.name === name || item.zh === name))
    .filter(Boolean)
    .slice(0, 4)
}

function normalizeCoreRows(rows = []) {
  return (Array.isArray(rows) ? rows : [])
    .map((item, index) => {
      const key = item.key || item.id || item.code || item.name || item.zh || `${index}`
      const rawName = item.name || item.title || item.label || item.zh
      const mappedName = normalizeCoreName(rawName, key)
      const value = pickValue(item, ["value", "num", "count", "dataValue"])
      if (!mappedName || value === undefined || value === null || value === "") return null
      const normalizedMetric = normalizeCoreMetric(mappedName, value, item.unit || item.dw || "")
      return {
        key,
        name: normalizedMetric.name,
        value: normalizedMetric.value,
        unit: normalizedMetric.unit,
        icon: item.icon || ["town", "area", "yield", "rate"][index % 4],
      }
    })
    .filter(Boolean)
}

function normalizeCoreName(name, key) {
  const text = String(name || key || "")
  const lowerKey = String(key || "").toLowerCase()
  if (/avg.*yak|yak.*avg|yak.*household|yak.*per|hjyak/.test(lowerKey) || /户均.*牦牛|牦牛.*户均/.test(text)) return "户均牦牛头数"
  if (/avg.*grass|grass.*avg|grass.*household|grass.*per/.test(lowerKey) || /户均.*草地|草地.*户均/.test(text)) return "户均草地面积"
  if (/income|revenue|人均收入/.test(lowerKey) || text.includes("人均收入")) return "人均收入"
  if (/poverty|monitor|防反贫|监测户/.test(lowerKey) || text.includes("防反贫") || text.includes("监测户")) return "防反贫监测户"
  if (/joint|union|link|联户/.test(lowerKey) || text.includes("联户")) return "联户数量"
  if (/herdsman|muhu|pasture.*count|牧户数量/.test(lowerKey) || text.includes("牧户数量")) return "牧户数量"
  if (/yak/.test(lowerKey) || text.includes("牦牛")) return null
  if (/grass/.test(lowerKey) || text.includes("草地面积")) return "草地面积"
  if (/wet/.test(lowerKey) || text.includes("湿地")) return "湿地面积"
  if (text.includes("牧户") || text.includes("牧场")) return "牧户数量"
  if (/project/.test(lowerKey) || text.includes("工程") || text.includes("项目")) return "工程数量"
  if (/base/.test(lowerKey) || text.includes("基地")) return "基地数量"
  return text
}

function normalizeCoreMetric(name, value, unit) {
  const number = Number(value) || 0
  if (name === "户均牦牛头数") return { name, value: number, unit: unit || "头" }
  if (name === "户均草地面积") return { name, value: number, unit: unit || "亩" }
  if (name === "人均收入") return { name, value: number, unit: unit || "元/年" }
  if (name === "联户数量" || name === "防反贫监测户") return { name, value: number, unit: unit || "户" }
  if ((name.includes("草地") || name.includes("湿地")) && !String(unit).includes("万")) {
    return {
      name,
      value: Math.round((number / 10000) * 100) / 100,
      unit: "万亩",
    }
  }
  if (name.includes("牧户")) return { name, value: number, unit: unit || "户" }
  if (name.includes("工程") || name.includes("项目") || name.includes("基地")) return { name, value: number, unit: unit || "个" }
  return { name, value: number, unit }
}

function isHiddenCoreMetric(name) {
  return name === "草地面积" || name === "湿地面积" || name === "区域数量" || name === "牦牛数量"
}

function normalizeTownRows(rows = []) {
  return (Array.isArray(rows) ? rows : [])
    .map((item) => {
      const name = pickValue(item, ["areaName", "area_name", "name", "townName", "zoneName"])
      const value = pickValue(item, ["number", "value", "count", "num", "pastureCount", "muhuCount", "muhu_count"])
      if (!name || value === undefined || value === null || value === "") return null
      return {
        key: item.code || item.areaCode || item.area_code || name,
        name,
        value: Number(value) || 0,
        unit: "户",
      }
    })
    .filter(Boolean)
    .sort((a, b) => b.value - a.value)
}

function normalizeOnlineRows(rows = []) {
  return (Array.isArray(rows) ? rows : [])
    .map((item) => {
      const name = pickValue(item, ["areaName", "area_name", "name", "townName", "zoneName"])
      const registered = Number(pickValue(item, ["registeredMuhuCount", "registered_muhu_count", "registeredCount"]))
      const online = Number(pickValue(item, ["muhuCount", "muhu_count", "onlineCount"]))
      const rawValue = pickValue(item, ["onlinePercent", "uplineRate", "upline_rate", "onlineRate", "value", "percent", "rate"])
      const value = rawValue !== undefined && rawValue !== null && rawValue !== ""
        ? rawValue
        : registered > 0 ? (online / registered) * 100 : undefined
      if (!name || value === undefined || value === null || value === "") return null
      return {
        key: item.code || item.areaCode || item.area_code || name,
        name,
        value: Number(String(value).replace("%", "")) || 0,
        unit: "%",
        extra: Number.isFinite(online) && Number.isFinite(registered) && registered > 0 ? `${online}/${registered}户` : item.extra,
      }
    })
    .filter(Boolean)
}

function completePastureOnlineRows(rows = []) {
  const rowMap = new Map()
  ;(Array.isArray(rows) ? rows : []).forEach((row) => {
    const codeKey = String(row?.key || row?.code || row?.areaCode || row?.area_code || "").trim()
    const nameKey = normalizePastureTownName(row?.name || row?.areaName || row?.area_name || row?.townName || row?.zoneName)
    ;[codeKey, nameKey].filter(Boolean).forEach((key) => rowMap.set(key, row))
  })
  return pastureTownRows.map((town) => {
    const row = rowMap.get(String(town.key || "")) || rowMap.get(town.name)
    if (row) {
      return {
        ...row,
        key: town.key,
        name: town.name,
        unit: row.unit || "%",
      }
    }
    return {
      key: town.key,
      name: town.name,
      value: 0,
      unit: "%",
      extra: "--",
    }
  })
}

function completePastureGrasslandRows(rows = []) {
  const rowMap = new Map()
  ;(Array.isArray(rows) ? rows : []).forEach((row) => {
    const codeKey = String(row?.key || row?.code || row?.areaCode || row?.area_code || "").trim()
    const nameKey = normalizePastureTownName(row?.name || row?.areaName || row?.area_name || row?.townName || row?.zoneName || row?.township)
    ;[codeKey, nameKey].filter(Boolean).forEach((key) => rowMap.set(key, row))
  })
  return pastureTownRows
    .map((town) => {
      const row = rowMap.get(String(town.key || "")) || rowMap.get(town.name)
      return {
        key: town.key,
        name: town.name,
        value: Number(row?.value) || 0,
        unit: row?.unit || "万亩",
      }
    })
    .sort((a, b) => b.value - a.value)
}

function normalizeAgeRows(rows = []) {
  const groups = new Map()
  ;(Array.isArray(rows) ? rows : []).forEach((item) => {
    const name = pickValue(item, ["ageRange", "age_range", "age", "name", "label"])
    const value = pickValue(item, ["number", "value", "count", "num"])
    if (!name || value === undefined || value === null || value === "") return
    groups.set(name, (groups.get(name) || 0) + (Number(value) || 0))
  })
  return Array.from(groups.entries()).map(([name, value]) => ({
    key: name,
    name,
    value,
    unit: "人",
  }))
}

function normalizeGenderRows(rows = []) {
  const groups = new Map()
  ;(Array.isArray(rows) ? rows : []).forEach((item) => {
    const name = pickValue(item, ["sex", "gender", "name"])
    const value = pickValue(item, ["number", "value", "count", "num"])
    if (!name || value === undefined || value === null || value === "") return
    const label = String(name).includes("女") ? "女" : String(name).includes("男") ? "男" : name
    groups.set(label, (groups.get(label) || 0) + (Number(value) || 0))
  })
  return Array.from(groups.entries()).map(([name, value]) => ({
    key: name,
    name,
    value,
    unit: "人",
  }))
}

function normalizePastureRows(rows = []) {
  return (Array.isArray(rows) ? rows : [])
    .map((item, index) => {
      const name = pickValue(item, ["name", "pasturesName", "pastures_name", "muhuName", "muhu_name", "pastureName", "pasture_name", "herdsmanName"])
      if (!name) return null
      const grasslands = normalizeRowGrasslands(item)
      const grasslandArea = pickPastureArea(item) || sumPastureGrasslandArea(grasslands)
      const grasslandCount = pickGrasslandBoundaryCount(item, grasslands)
      const realId = getRealPastureId(item)
      const township = getPastureTownship(item, grasslands) || getPastureFallbackTownship(item, name, realId)
      const normalized = {
        ...item,
        key: item.key || item.id || item.pasturesId || item.pastures_id || `${name}-${index}`,
        id: realId || item.id || item.pastureId || item.pasturesId || item.pastures_id || item.muhuId || item.key,
        realPastureId: realId,
        name,
        township: township || "--",
        grasslandArea,
        acreage: grasslandArea,
        grasslandCount,
        hasGrasslandBoundary: grasslandCount > 0,
        grasslands,
      }
      normalized.canLocate = Boolean(realId || grasslands.some(hasGrasslandGeometry) || hasGrasslandGeometry(item))
      normalized.locateText = normalized.canLocate ? "定位" : "--"
      normalized.value = normalized.grasslandArea
      normalized.unit = "亩"
      normalized.searchText = [
        normalized.name,
        normalized.herdsmanName,
        normalized.herdsman_name,
        normalized.phone,
        normalized.phonenumber,
        normalized.idCard,
        normalized.id_card,
        normalized.township,
        normalized.id,
      ].filter(Boolean).join(" ")
      return normalized
    })
    .filter(Boolean)
    .sort(comparePastureRows)
}

function sortPastureRows(rows = []) {
  return [...(Array.isArray(rows) ? rows : [])]
    .map((row) => ({
      ...row,
      grasslandArea: pickPastureArea(row),
      acreage: pickPastureArea(row),
    }))
    .sort(comparePastureRows)
}

function comparePastureRows(a, b) {
  const boundaryA = hasPastureGrasslandBoundary(a) ? 1 : 0
  const boundaryB = hasPastureGrasslandBoundary(b) ? 1 : 0
  if (boundaryA !== boundaryB) return boundaryB - boundaryA
  const boundaryCountA = pickGrasslandBoundaryCount(a)
  const boundaryCountB = pickGrasslandBoundaryCount(b)
  if (boundaryCountA !== boundaryCountB) return boundaryCountB - boundaryCountA
  const areaA = pickPastureArea(a)
  const areaB = pickPastureArea(b)
  const hasAreaA = areaA > 0 ? 1 : 0
  const hasAreaB = areaB > 0 ? 1 : 0
  if (hasAreaA !== hasAreaB) return hasAreaB - hasAreaA
  const locatableA = isPastureLocatable(a) ? 1 : 0
  const locatableB = isPastureLocatable(b) ? 1 : 0
  if (locatableA !== locatableB) return locatableB - locatableA
  if (areaA !== areaB) return areaB - areaA
  return String(a?.name || "").localeCompare(String(b?.name || ""), "zh-CN")
}

function hasPastureGrasslandBoundary(item = {}) {
  const flag = pickValue(item, ["hasGrasslandBoundary", "has_grassland_boundary", "hasBoundary", "has_boundary"])
  if (flag === true || flag === 1 || flag === "1") return true
  if (typeof flag === "string" && ["true", "yes", "y"].includes(flag.trim().toLowerCase())) return true
  return pickGrasslandBoundaryCount(item) > 0
}

function pickGrasslandBoundaryCount(item = {}, grasslands = item?.grasslands) {
  const keys = [
    "grasslandCount",
    "grassland_count",
    "grasslandsCount",
    "grasslands_count",
    "grasslandBoundaryCount",
    "grassland_boundary_count",
    "boundaryCount",
    "boundary_count",
  ]
  for (const key of keys) {
    const count = Number(item?.[key])
    if (Number.isFinite(count) && count > 0) return count
  }
  return Array.isArray(grasslands) ? grasslands.length : 0
}

function pickPastureArea(item = {}) {
  const keys = [
    "acreage",
    "grasslandArea",
    "grassland_area",
    "area",
    "areaRange",
    "area_range",
    "cdAcreage",
    "cd_acreage",
    "totalAcreage",
    "total_acreage",
    "grasslandAcreage",
    "grassland_acreage",
    "pastureArea",
    "pasture_area",
  ]
  const values = keys.map((key) => parsePastureArea(item?.[key]))
  const positive = values.find((value) => value > 0)
  return positive || values.find((value) => value === 0) || 0
}

function sumPastureGrasslandArea(grasslands = []) {
  return (Array.isArray(grasslands) ? grasslands : []).reduce((total, row) => total + pickPastureArea(row), 0)
}

function parsePastureArea(value) {
  if (typeof value === "number") return Number.isFinite(value) ? value : 0
  const text = String(value ?? "").trim()
  if (!text || text === "--") return 0
  const match = text.replace(/,/g, "").match(/-?\d+(?:\.\d+)?/)
  if (!match) return 0
  const number = Number(match[0])
  if (!Number.isFinite(number)) return 0
  return text.includes("万") ? number * 10000 : number
}

function aggregatePastureAreaRows(rows = []) {
  return aggregatePastureRows(rows, (row) => Number(row.grasslandArea) || 0, "万亩", (value) => Math.round((value / 10000) * 100) / 100)
}

function aggregatePastureRows(rows = [], pick, unit, transform = (value) => value) {
  const map = new Map()
  ;(Array.isArray(rows) ? rows : []).forEach((row) => {
    const name = row?.township || row?.areaName || row?.area_name
    if (!name || name === "--") return
    const current = map.get(name) || { key: name, name, value: 0, unit }
    const picked = pick(row)
    current.value += Number(picked) || 0
    map.set(name, current)
  })
  return Array.from(map.values())
    .map((row) => ({ ...row, value: transform(row.value) }))
    .filter((row) => Number.isFinite(row.value) && row.value > 0)
    .sort((a, b) => b.value - a.value)
}

function normalizeRowGrasslands(item = {}) {
  const rows = []
  const nestedKeys = ["grasslands", "grasslandList", "grasslandRows", "landList", "plots", "features"]
  nestedKeys.forEach((key) => {
    const value = item?.[key]
    if (Array.isArray(value)) rows.push(...value)
    else if (Array.isArray(value?.rows)) rows.push(...value.rows)
    else if (Array.isArray(value?.list)) rows.push(...value.list)
    else if (Array.isArray(value?.data)) rows.push(...value.data)
  })
  const wkt = pickValue(item, ["wkt", "geo", "geom", "geometryWkt", "geometry_wkt", "shape", "location"])
  if (wkt || item?.geometry) {
    rows.push({
      ...item,
      id: item?.grasslandId || item?.gid || item?.id,
      pastureId: item?.pastureId || item?.pasturesId || item?.pastures_id || item?.muhuId || item?.id,
      name: item?.grasslandName || item?.name,
      wkt,
      geometry: item?.geometry,
    })
  }
  return rows.filter(Boolean)
}

function hasGrasslandGeometry(item = {}) {
  if (typeof item === "string") return Boolean(item.trim())
  if (item?.type === "Feature") return true
  if (item?.type && item?.coordinates) return true
  if (item?.geometry?.type || item?.geometry?.coordinates || typeof item?.geometry === "string") return true
  return Boolean(pickValue(item, ["wkt", "geo", "geom", "geometryWkt", "geometry_wkt", "shape", "location"]))
}

function getRealPastureId(row = {}) {
  const id = row?.realPastureId || row?.pastureId || row?.pasturesId || row?.pastures_id || row?.muhuId || row?.id
  return id === undefined || id === null || id === "" ? null : id
}

function isPastureLocatable(row = {}) {
  if (row?.canLocate) return true
  if (getRealPastureId(row)) return true
  if (hasGrasslandGeometry(row)) return true
  return normalizeRowGrasslands(row).some(hasGrasslandGeometry)
}

function pickValue(item = {}, keys = []) {
  for (const key of keys) {
    const value = item?.[key]
    if (value !== undefined && value !== null && value !== "") return value
  }
  return undefined
}

function getPastureTownship(item = {}, grasslands = []) {
  const directName = pickValue(item, [
    "township",
    "townName",
    "town_name",
    "areaName",
    "area_name",
    "zoneName",
    "zone_name",
    "fullName",
    "full_name",
    "address",
    "牧场所在区域",
  ])
  const directTownship = normalizePastureTownName(directName)
  if (directTownship) return directTownship
  const codeName = getPastureTownNameFromCode(pickValue(item, ["areaCode", "area_code", "townCode", "town_code", "code"]))
  if (codeName) return codeName
  for (const grassland of grasslands) {
    const grasslandName = pickValue(grassland, [
      "township",
      "townName",
      "town_name",
      "areaName",
      "area_name",
      "zoneName",
      "zone_name",
      "fullName",
      "full_name",
      "address",
      "牧场所在区域",
    ])
    const grasslandTownship = normalizePastureTownName(grasslandName)
    if (grasslandTownship) return grasslandTownship
    const grasslandCodeName = getPastureTownNameFromCode(pickValue(grassland, ["areaCode", "area_code", "townCode", "town_code", "code"]))
    if (grasslandCodeName) return grasslandCodeName
  }
  const itemGeometryName = getPastureTownNameFromGeometry(item)
  if (itemGeometryName) return itemGeometryName
  for (const grassland of grasslands) {
    const grasslandGeometryName = getPastureTownNameFromGeometry(grassland)
    if (grasslandGeometryName) return grasslandGeometryName
  }
  return ""
}

function normalizePastureTownName(value) {
  const text = String(value || "").trim()
  if (!text || text === "--") return ""
  const codeName = getPastureTownNameFromCode(text)
  if (codeName) return codeName
  if (isHongyuanTownshipName(text)) return text
  const embeddedTownship = hongyuanTownshipDisplayOrder.find((name) => text.includes(name))
  if (embeddedTownship) return embeddedTownship
  const extracted = extractPastureTownName(text)
  return extracted && extracted !== text ? normalizePastureTownName(extracted) : ""
}

function getPastureFallbackTownship(item = {}, name = "", realId = "") {
  const idKeys = [
    realId,
    item.realPastureId,
    item.pastureId,
    item.pasturesId,
    item.pastures_id,
    item.muhuId,
    item.id,
  ]
  for (const key of idKeys) {
    const township = pastureFallbackTownshipById[String(key || "").trim()]
    if (township) return normalizePastureTownName(township)
  }
  const nameKeys = [
    name,
    item.name,
    item.pasturesName,
    item.pastures_name,
    item.muhuName,
    item.muhu_name,
    item.pastureName,
    item.pasture_name,
  ]
  for (const key of nameKeys) {
    const township = pastureFallbackTownshipByName[normalizePastureNameKey(key)]
    if (township) return normalizePastureTownName(township)
  }
  return ""
}

function normalizePastureNameKey(value) {
  return String(value || "")
    .replace(/[·•・.．。`´’‘ʼʹ＇'"“”\s\-—_()（）]/g, "")
    .replace(/˙/g, "")
}

function getPastureTownNameFromCode(value) {
  const text = String(value || "").trim()
  if (!text) return ""
  const normalizedCode = text.replace(/\D/g, "")
  if (!normalizedCode) return ""
  return getHongyuanTownshipNameByCode(normalizedCode)
}

function getPastureTownNameFromGeometry(item = {}) {
  const geometry = getPastureGeometry(item)
  const center = getGeometryCenter(geometry)
  if (!center) return ""
  const feature = pastureTownshipFeatures.find((row) => isLngLatInGeometry(center, row.geometry))
  return feature?.name ? normalizePastureTownName(feature.name) : ""
}

function getPastureGeometry(item = {}) {
  if (item?.type === "Feature") return getPastureGeometry(item.geometry)
  if (item?.type && item?.coordinates) return item
  if (item?.geometry?.type || item?.geometry?.coordinates) return item.geometry
  const text = pickValue(item, ["geometry", "location", "wkt", "geo", "geom", "geometryWkt", "geometry_wkt", "shape"])
  if (!text || typeof text !== "string") return null
  const cleanText = text.trim()
  if (!cleanText) return null
  if (cleanText.startsWith("{") || cleanText.startsWith("[")) {
    try {
      const parsed = JSON.parse(cleanText)
      return getPastureGeometry(parsed)
    } catch (error) {
      return null
    }
  }
  return parsePastureWktGeometry(cleanText)
}

function parsePastureWktGeometry(value) {
  const text = String(value || "").trim()
  const typeMatch = text.match(/^([A-Z]+)(?:\s+[A-Z]+)?\s*\(/i)
  const type = typeMatch?.[1]?.toUpperCase()
  if (!type) return null
  const body = text.slice(text.indexOf("("))
  if (type === "POINT") return { type: "Point", coordinates: parsePastureWktPoint(body) }
  if (type === "MULTIPOINT") return { type: "MultiPoint", coordinates: getPastureWktChildGroups(body).map(parsePastureWktPoint).filter(Boolean) }
  if (type === "LINESTRING") return { type: "LineString", coordinates: parsePastureWktLine(body) }
  if (type === "MULTILINESTRING") return { type: "MultiLineString", coordinates: getPastureWktChildGroups(body).map(parsePastureWktLine).filter((line) => line.length) }
  if (type === "POLYGON") return { type: "Polygon", coordinates: parsePastureWktPolygonBody(body) }
  if (type === "MULTIPOLYGON") {
    return { type: "MultiPolygon", coordinates: getPastureWktChildGroups(body).map(parsePastureWktPolygonBody).filter((polygon) => polygon.length) }
  }
  return null
}

function parsePastureWktPoint(text) {
  const point = String(text || "").replace(/^\(+|\)+$/g, "").trim().split(/\s+/).slice(0, 2).map(Number)
  return point.length >= 2 && Number.isFinite(point[0]) && Number.isFinite(point[1]) ? [point[0], point[1]] : null
}

function parsePastureWktLine(text) {
  return String(text || "").replace(/^\(+|\)+$/g, "").split(",").map(parsePastureWktPoint).filter(Boolean)
}

function parsePastureWktPolygonBody(body) {
  return getPastureWktChildGroups(body)
    .map(parsePastureWktLine)
    .filter((ring) => ring.length >= 3)
}

function getPastureWktChildGroups(text) {
  const groups = []
  let depth = 0
  let start = -1
  Array.from(String(text || "")).forEach((char, index) => {
    if (char === "(") {
      if (depth === 0) start = index + 1
      depth += 1
    } else if (char === ")") {
      depth -= 1
      if (depth === 0 && start >= 0) {
        groups.push(text.slice(start, index))
        start = -1
      }
    }
  })
  return groups.length ? groups : [String(text || "")]
}

function normalizeTownshipFeatures(raw) {
  try {
    const data = typeof raw === "string" ? JSON.parse(raw) : raw
    return (data?.features || [])
      .map((feature) => ({
        name: feature?.properties?.name || feature?.properties?.areaName || feature?.properties?.townName,
        code: feature?.properties?.code,
        geometry: feature?.geometry,
      }))
      .filter((feature) => feature.name && feature.geometry?.coordinates)
  } catch (error) {
    return []
  }
}

function getGeometryCenter(geometry) {
  const coordinates = getGeometryCoordinates(geometry)
  if (!coordinates.length) return null
  const sum = coordinates.reduce((total, point) => ({
    lng: total.lng + point[0],
    lat: total.lat + point[1],
  }), { lng: 0, lat: 0 })
  return [sum.lng / coordinates.length, sum.lat / coordinates.length]
}

function getGeometryCoordinates(geometry) {
  const points = []
  const visit = (value) => {
    if (!Array.isArray(value)) return
    if (value.length >= 2 && Number.isFinite(Number(value[0])) && Number.isFinite(Number(value[1]))) {
      points.push([Number(value[0]), Number(value[1])])
      return
    }
    value.forEach(visit)
  }
  visit(geometry?.coordinates)
  return points
}

function isLngLatInGeometry(point, geometry) {
  if (!point || !geometry?.coordinates) return false
  if (geometry.type === "Polygon") return isLngLatInPolygon(point, geometry.coordinates)
  if (geometry.type === "MultiPolygon") return geometry.coordinates.some((polygon) => isLngLatInPolygon(point, polygon))
  return false
}

function isLngLatInPolygon(point, polygon) {
  const ring = polygon?.[0]
  if (!Array.isArray(ring) || ring.length < 3) return false
  let inside = false
  const x = point[0]
  const y = point[1]
  for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
    const xi = Number(ring[i]?.[0])
    const yi = Number(ring[i]?.[1])
    const xj = Number(ring[j]?.[0])
    const yj = Number(ring[j]?.[1])
    const intersects = ((yi > y) !== (yj > y)) && (x < ((xj - xi) * (y - yi)) / ((yj - yi) || 1e-12) + xi)
    if (intersects) inside = !inside
  }
  return inside
}

</script>

<style lang="scss">
.pasture-management {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  color: #fff;
  background: #03121c;
}

.pasture-map-scene,
.pasture-ol-map-layer {
  position: absolute;
  inset: 0;
  z-index: 1;
}

.pasture-map-scrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background:
    radial-gradient(circle at 50% 52%, transparent 0%, transparent 40%, rgba(1, 9, 15, 0.18) 70%, rgba(1, 8, 13, 0.62) 100%),
    linear-gradient(90deg, rgba(0, 12, 20, 0.42), transparent 25%, transparent 75%, rgba(0, 12, 20, 0.42));
}

.pasture-map-loading {
  position: absolute;
  left: 50%;
  top: 54%;
  z-index: 4;
  color: rgba(222, 250, 255, 0.78);
  font-size: 14px;
  transform: translate(-50%, -50%);
}

.pasture-management-ui {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
}

.pasture-left,
.pasture-right {
  position: absolute;
  top: 128px;
  bottom: 8px;
  z-index: 6;
  width: 398px;
  perspective: 700px;
  pointer-events: all;
}

.pasture-left {
  left: 32px;
}

.pasture-right {
  right: 32px;
}

.pasture-side-stack {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
  transform-style: preserve-3d;

  &.is-left {
    transform: translate3d(0, 0, 0) rotateY(6deg);
    transform-origin: left center;
  }

  &.is-right {
    transform: translate3d(0, 0, 0) rotateY(-6deg);
    transform-origin: right center;
  }
}

.pasture-side-card {
  flex: 0 0 auto;

  .m-card-bd-content {
    top: 40px;
    bottom: 10px;
    color: #f3feff;
  }

  .m-card-bd-bg {
    filter: brightness(1.08) saturate(1.04);
  }

  .m-card-bd-bg::after {
    content: "";
    position: absolute;
    inset: 4px 5px 6px;
    z-index: 0;
    pointer-events: none;
    border: 1px solid rgba(48, 220, 255, 0.14);
    background:
      linear-gradient(180deg, rgba(17, 73, 92, 0.16), rgba(6, 29, 44, 0.14)),
      linear-gradient(90deg, rgba(48, 220, 255, 0.1), transparent 42%, rgba(48, 220, 255, 0.06));
    box-shadow:
      inset 0 0 0 1px rgba(48, 220, 255, 0.06),
      inset 0 12px 28px rgba(48, 220, 255, 0.04);
  }
}

.pasture-household-rank {
  display: flex;
  flex-direction: column;
  gap: 4px;
  height: 100%;
  padding: 1px 12px 3px;
  box-sizing: border-box;
}

.pasture-household-rank-row {
  position: relative;
  flex: 1 1 0;
  min-height: 24px;
}

.pasture-household-rank-row::before {
  position: absolute;
  left: 0;
  top: 8px;
  width: 5px;
  height: 5px;
  border: 1px solid var(--rank-color);
  border-radius: 50%;
  content: "";
  background: rgba(6, 28, 40, 0.92);
  box-shadow: 0 0 9px var(--rank-color);
}

.pasture-household-rank-row.is-leading .pasture-rank-value {
  color: #ffffff;
  text-shadow: 0 0 12px rgba(35, 246, 200, 0.58);
}

.pasture-rank-copy {
  display: grid;
  grid-template-columns: 32px minmax(0, 1fr) auto;
  gap: 7px;
  align-items: center;
  height: 18px;
  padding-left: 12px;
}

.pasture-rank-index {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 15px;
  border: 1px solid rgba(48, 220, 255, 0.24);
  color: var(--rank-color);
  font-size: 10px;
  line-height: 1;
  font-weight: 800;
  background: linear-gradient(90deg, rgba(48, 220, 255, 0.12), rgba(48, 220, 255, 0.02));
  box-shadow: inset 0 0 10px rgba(48, 220, 255, 0.1);
}

.pasture-rank-name {
  overflow: hidden;
  color: rgba(226, 250, 255, 0.9);
  font-size: 14px;
  line-height: 17px;
  font-weight: 800;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pasture-rank-value {
  color: rgba(240, 253, 255, 0.94);
  font-size: 14px;
  line-height: 17px;
  font-weight: 900;
}

.pasture-rank-value em {
  margin-left: 4px;
  color: rgba(210, 242, 248, 0.72);
  font-size: 10px;
  font-style: normal;
  font-weight: 700;
}

.pasture-rank-track {
  position: relative;
  height: 6px;
  margin: 1px 0 0 45px;
  overflow: hidden;
  border: 1px solid rgba(48, 220, 255, 0.12);
  background:
    linear-gradient(90deg, rgba(48, 220, 255, 0.1), rgba(48, 220, 255, 0.02)),
    rgba(3, 22, 33, 0.72);
  box-shadow: inset 0 0 12px rgba(0, 0, 0, 0.24);
}

.pasture-rank-track i {
  display: block;
  width: var(--rank-rate);
  height: 100%;
  background: linear-gradient(90deg, rgba(35, 246, 200, 0.18), var(--rank-color));
  box-shadow: 0 0 12px var(--rank-color);
}

.pasture-grassland-profile {
  display: grid;
  grid-template-rows: 54px minmax(0, 1fr);
  gap: 5px;
  height: 100%;
  padding: 2px 11px 3px;
  box-sizing: border-box;
}

.pasture-grassland-featured {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 9px;
  min-height: 0;
}

.pasture-grassland-feature-card {
  position: relative;
  overflow: hidden;
  padding: 5px 9px 6px;
  border: 1px solid rgba(48, 220, 255, 0.18);
  background:
    radial-gradient(circle at 15% 20%, color-mix(in srgb, var(--area-color) 36%, transparent), transparent 42%),
    linear-gradient(135deg, rgba(19, 90, 112, 0.24), rgba(3, 28, 42, 0.5));
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.03),
    inset 0 -18px 28px rgba(0, 0, 0, 0.18),
    0 0 14px rgba(48, 220, 255, 0.08);
}

.pasture-grassland-feature-card::after {
  position: absolute;
  right: -18px;
  top: -18px;
  width: 54px;
  height: 54px;
  border: 1px solid color-mix(in srgb, var(--area-color) 55%, transparent);
  border-radius: 50%;
  content: "";
  opacity: 0.5;
  box-shadow: 0 0 20px color-mix(in srgb, var(--area-color) 42%, transparent);
}

.pasture-grassland-feature-card span {
  display: block;
  color: rgba(220, 250, 255, 0.82);
  font-size: 12px;
  line-height: 14px;
  font-weight: 800;
}

.pasture-grassland-feature-card strong {
  display: block;
  margin-top: 1px;
  color: #ffffff;
  font-size: 19px;
  line-height: 22px;
  font-weight: 900;
  text-shadow: 0 0 12px color-mix(in srgb, var(--area-color) 50%, transparent);
}

.pasture-grassland-feature-card em,
.pasture-grassland-mini-row em {
  margin-left: 4px;
  color: rgba(216, 244, 250, 0.72);
  font-size: 10px;
  font-style: normal;
  font-weight: 800;
}

.pasture-feature-track {
  position: absolute;
  right: 10px;
  bottom: 6px;
  left: 10px;
  height: 3px;
  overflow: hidden;
  background: rgba(14, 57, 72, 0.72);
}

.pasture-feature-track i {
  display: block;
  width: var(--area-rate);
  height: 100%;
  background: linear-gradient(90deg, rgba(255, 255, 255, 0.18), var(--area-color));
  box-shadow: 0 0 12px var(--area-color);
}

.pasture-grassland-mini-list {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-height: 0;
}

.pasture-grassland-mini-row {
  display: grid;
  grid-template-columns: 64px minmax(0, 1fr) 70px;
  gap: 6px;
  align-items: center;
  min-height: 14px;
}

.pasture-grassland-mini-row span {
  overflow: hidden;
  color: rgba(222, 250, 255, 0.84);
  font-size: 12px;
  line-height: 14px;
  font-weight: 800;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pasture-grassland-mini-row strong {
  justify-self: end;
  color: rgba(245, 253, 255, 0.94);
  font-size: 12px;
  line-height: 14px;
  font-weight: 900;
}

.pasture-mini-track {
  height: 5px;
  overflow: hidden;
  border: 1px solid rgba(48, 220, 255, 0.12);
  background: rgba(8, 39, 54, 0.66);
}

.pasture-mini-track i {
  display: block;
  width: var(--area-rate);
  height: 100%;
  background: linear-gradient(90deg, rgba(48, 220, 255, 0.08), var(--area-color));
  box-shadow: 0 0 10px var(--area-color);
}

.pasture-video-card {
  .m-card-bd-content {
    bottom: 12px;
  }
}

.pasture-video-monitor {
  position: relative;
  display: block;
  height: 100%;
  padding: 2px 18px;
  box-sizing: border-box;
  pointer-events: auto;
}

.pasture-video-screen {
  position: relative;
  width: 88%;
  height: 100%;
  min-width: 0;
  margin: 0 auto;
  overflow: hidden;
  cursor: pointer;
  border: 1px solid rgba(48, 220, 255, 0.34);
  background-color: #020d14;
  background-position: center;
  background-size: cover;
  box-shadow:
    inset 0 0 0 1px rgba(10, 255, 214, 0.08),
    inset 0 0 26px rgba(48, 220, 255, 0.16),
    0 0 16px rgba(18, 145, 190, 0.14);
}

.pasture-video-screen.is-playing {
  background-image: none !important;
}

.pasture-video-player,
.pasture-video-player > div,
.pasture-video-player video,
.pasture-video-player canvas,
.pasture-video-player iframe {
  position: absolute !important;
  inset: 0 !important;
  z-index: 1;
  width: 100% !important;
  height: 100% !important;
}

.pasture-video-player {
  pointer-events: auto;
}

.pasture-video-player video {
  object-fit: cover;
}

.pasture-video-screen .loading-container,
.pasture-video-screen .loading-item,
.pasture-video-screen #loading-icon {
  display: none !important;
  visibility: hidden !important;
  pointer-events: none !important;
}

.pasture-video-nav {
  position: absolute;
  top: 50%;
  z-index: 6;
  width: 22px;
  height: 42px;
  border: 1px solid rgba(48, 220, 255, 0.26);
  color: rgba(227, 253, 255, 0.88);
  font-size: 20px;
  line-height: 20px;
  cursor: pointer;
  background: linear-gradient(180deg, rgba(48, 220, 255, 0.18), rgba(10, 39, 54, 0.56));
  box-shadow: inset 0 0 12px rgba(48, 220, 255, 0.1);
  transform: translateY(-50%);
}

.pasture-video-nav:disabled {
  cursor: not-allowed;
  opacity: 0.38;
}

.pasture-video-prev {
  left: 25px;
}

.pasture-video-next {
  right: 25px;
}

.pasture-video-screen::before {
  position: absolute;
  inset: 0;
  z-index: 2;
  content: "";
  pointer-events: none;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.04), transparent 34%, rgba(0, 0, 0, 0.44)),
    repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.04) 0, rgba(255, 255, 255, 0.04) 1px, transparent 1px, transparent 5px);
}

.pasture-video-screen.is-playing::before {
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.36), transparent 28%, rgba(0, 0, 0, 0.24));
}

.pasture-video-scan {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background: linear-gradient(90deg, transparent, rgba(126, 251, 246, 0.2), transparent);
  transform: translateX(-100%);
  animation: pastureVideoScan 3.2s linear infinite;
}

.pasture-video-play {
  position: absolute;
  left: 50%;
  top: 46%;
  z-index: 4;
  width: 30px;
  height: 30px;
  border: 1px solid rgba(255, 255, 255, 0.62);
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.36);
  box-shadow: 0 0 12px rgba(48, 220, 255, 0.2);
  transform: translate(-50%, -50%);
}

.pasture-video-play::after {
  position: absolute;
  left: 12px;
  top: 8px;
  width: 0;
  height: 0;
  content: "";
  border-top: 7px solid transparent;
  border-bottom: 7px solid transparent;
  border-left: 10px solid #ffffff;
}

.pasture-video-point-name {
  position: absolute;
  right: 10px;
  bottom: 8px;
  left: 10px;
  z-index: 4;
  overflow: hidden;
  padding: 4px 8px;
  border-left: 2px solid rgba(69, 233, 164, 0.78);
  color: #ffffff;
  font-size: 12px;
  line-height: 15px;
  font-weight: 800;
  text-overflow: ellipsis;
  white-space: nowrap;
  background: rgba(0, 0, 0, 0.42);
}

.pasture-video-loading,
.pasture-video-error {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 5;
  min-width: 72px;
  padding: 4px 8px;
  border: 1px solid rgba(48, 220, 255, 0.32);
  color: #ffffff;
  font-size: 11px;
  line-height: 14px;
  text-align: center;
  background: rgba(0, 0, 0, 0.62);
  transform: translate(-50%, -50%);
}

.pasture-video-error {
  border-color: rgba(255, 122, 69, 0.44);
  color: #ffb18e;
}

@keyframes pastureVideoScan {
  to {
    transform: translateX(100%);
  }
}

.pasture-map-layer-tools {
  position: absolute;
  top: auto;
  right: 458px;
  bottom: 72px;
  left: auto;
  z-index: 7;
  width: 248px;
  height: 430px;
  pointer-events: all;

  .m-card {
    width: 100% !important;
    height: 100% !important;
    background: rgba(3, 18, 30, 0.54);
    backdrop-filter: blur(2px);
  }
}

.pasture-map-feature-info {
  position: absolute;
  z-index: 9;
  width: 460px;
  pointer-events: all;

  .m-card {
    width: 100% !important;
    background: rgba(3, 18, 30, 0.7);
    backdrop-filter: blur(2px);
  }
}

.pasture-management .m-card-hd-title {
  color: #ffffff !important;
  background: none !important;
  -webkit-text-fill-color: #ffffff !important;
  font-weight: 800 !important;
  filter: brightness(1.2) contrast(1.08) !important;
  text-shadow:
    0 0 1px rgba(255, 255, 255, 0.96),
    0 0 8px rgba(255, 255, 255, 0.22) !important;
}

.pasture-management .business-bars.is-distribution {
  max-height: 100%;
  padding-right: 8px;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: rgba(48, 220, 255, 0.72) rgba(10, 42, 58, 0.42);

  &::-webkit-scrollbar {
    width: 5px;
  }

  &::-webkit-scrollbar-track {
    border-radius: 999px;
    background: rgba(10, 42, 58, 0.42);
  }

  &::-webkit-scrollbar-thumb {
    border-radius: 999px;
    background: linear-gradient(180deg, rgba(48, 220, 255, 0.9), rgba(36, 246, 196, 0.68));
    box-shadow: 0 0 8px rgba(48, 220, 255, 0.38);
  }
}

</style>
