<script lang="ts" setup>
import { DigitalApi, type DigitalWell, type WellLogDataset } from '@/api/digital'
import { Search } from '@element-plus/icons-vue'
import * as echarts from 'echarts'

defineOptions({ name: 'DigitalWellLog' })

type CurveKey =
  | 'sws'
  | 'dts'
  | 'porns'
  | 'rhobs'
  | 'rxos'
  | 'r25s'
  | 'cilds'
  | 'calis'
  | 'sps'
  | 'grs'

interface CurveConfig {
  name: string
  key: CurveKey
  unit: string
  min: number
  max: number
  color: string
  track: number
  inverse?: boolean
  lineType?: 'solid' | 'dashed' | 'dotted'
}

const curves: CurveConfig[] = [
  { name: 'GR', key: 'grs', unit: 'API', min: 30, max: 300, color: '#16a34a', track: 2 },
  { name: 'SP', key: 'sps', unit: 'mV', min: 10, max: 300, color: '#2563eb', track: 2 },
  {
    name: 'CALI',
    key: 'calis',
    unit: 'mm',
    min: 450,
    max: 800,
    color: '#dc2626',
    track: 3,
    lineType: 'dashed'
  },
  {
    name: 'RESX',
    key: 'rxos',
    unit: 'ohm.m',
    min: 1,
    max: 30,
    color: '#f97316',
    track: 0,
    lineType: 'dotted'
  },
  { name: 'RESS', key: 'r25s', unit: 'ohm.m', min: 1, max: 30, color: '#d946ef', track: 0 },
  { name: 'RESD', key: 'cilds', unit: 'ohm·m', min: 1, max: 900, color: '#2563eb', track: 3 },
  { name: 'RHOB', key: 'rhobs', unit: 'g/cm³', min: 1.95, max: 2.95, color: '#111827', track: 0 },
  {
    name: 'NPHI',
    key: 'porns',
    unit: 'v/v',
    min: 0,
    max: 1,
    color: '#2563eb',
    track: 0
  },
  {
    name: 'DTC_SINT',
    key: 'dts',
    unit: 'us/m',
    min: 30,
    max: 200,
    color: '#7c3aed',
    track: 1,
    lineType: 'dashed'
  },
  { name: 'SW', key: 'sws', unit: 'v/v_decimal', min: 20, max: 100, color: '#0891b2', track: 1 }
]

const trackNames = [
  '低量程（约 0~30）',
  '中低量程（约 20~200）',
  '中量程（约 10~300）',
  '高量程（约 450~900）'
]
const trackCurves = trackNames.map((_, track) => curves.filter((curve) => curve.track === track))
const trackLayout = [
  { left: '7%', width: '21%' },
  { left: '29%', width: '21%' },
  { left: '51%', width: '21%' },
  { left: '73%', width: '21%' }
]
const SAMPLE_LIMIT = 2000

const message = useMessage()
const chartRef = ref<HTMLElement>()
const wells = ref<DigitalWell[]>([])
const selectedWellId = ref<number>()
const keyword = ref('')
const dataset = shallowRef<WellLogDataset>()
const listLoading = ref(false)
const dataLoading = ref(false)
const sampledIndices = shallowRef<number[]>([])
const chart = shallowRef<echarts.ECharts>()
let resizeObserver: ResizeObserver | undefined

const filteredWells = computed(() => {
  const word = keyword.value.trim().toLowerCase()
  if (!word) return wells.value
  return wells.value.filter((well) =>
    `${well.well ?? ''} ${well.uwi ?? ''}`.toLowerCase().includes(word)
  )
})

const selectedWell = computed(() => {
  return wells.value.find((well) => well.id === selectedWellId.value) ?? dataset.value?.wellInfo
})

const originalCount = computed(() => dataset.value?.depths?.length ?? 0)
const depthRange = computed(() => {
  const depths = dataset.value?.depths ?? []
  let min = Number.POSITIVE_INFINITY
  let max = Number.NEGATIVE_INFINITY
  for (const rawDepth of depths) {
    const depth = Number(rawDepth)
    if (!Number.isFinite(depth)) continue
    if (depth < min) min = depth
    if (depth > max) max = depth
  }
  if (!Number.isFinite(min) || !Number.isFinite(max)) return '--'
  return `${min.toFixed(2)} – ${max.toFixed(2)} m`
})

function getWellName(well?: DigitalWell) {
  return well?.well || well?.uwi || (well?.id ? `井 ${well.id}` : '--')
}

/**
 * 按深度等距寻找最近点，只生成一次索引；所有曲线严格复用同一组索引。
 * 这样既适配不等间距深度，也不会发生曲线和深度错位。
 */
function createDepthSampleIndices(depths: number[], limit: number) {
  const valid = depths
    .map((depth, index) => ({ depth: Number(depth), index }))
    .filter((item) => Number.isFinite(item.depth))
  if (valid.length <= limit) return valid.map((item) => item.index)

  const first = valid[0]
  const last = valid[valid.length - 1]
  const ascending = last.depth >= first.depth
  const result = new Set<number>([first.index, last.index])
  let cursor = 0

  for (let point = 1; point < limit - 1; point += 1) {
    const target = first.depth + ((last.depth - first.depth) * point) / (limit - 1)
    while (cursor < valid.length - 2) {
      const currentDistance = Math.abs(valid[cursor].depth - target)
      const nextDistance = Math.abs(valid[cursor + 1].depth - target)
      const passedTarget = ascending
        ? valid[cursor + 1].depth > target
        : valid[cursor + 1].depth < target
      if (nextDistance > currentDistance && passedTarget) break
      cursor += 1
    }
    result.add(valid[cursor].index)
  }

  return Array.from(result).sort((a, b) => a - b)
}

function curveValue(curve: CurveConfig, index: number): number | null {
  const raw = (dataset.value?.[curve.key] as number[] | undefined)?.[index]
  const value = Number(raw)
  if (!Number.isFinite(value) || value <= -900) return null
  // SW 接口为 0~1 小数时换算成用户给定的 20~100 显示范围。
  if (curve.key === 'sws' && Math.abs(value) <= 1.5) return value * 100
  return value
}

function formatNumber(value: unknown) {
  const number = Number(value)
  if (!Number.isFinite(number)) return '--'
  return Math.abs(number) >= 100
    ? number.toFixed(1)
    : number.toFixed(3).replace(/0+$/, '').replace(/\.$/, '')
}

function buildChartOption(): echarts.EChartsOption {
  const depths = dataset.value?.depths ?? []
  const indices = sampledIndices.value
  const sampledDepths = indices.map((index) => Number(depths[index])).filter(Number.isFinite)
  const minDepth = sampledDepths.length ? Math.min(...sampledDepths) : 0
  const maxDepth = sampledDepths.length ? Math.max(...sampledDepths) : 1

  const xAxis: any[] = curves.map((curve) => ({
    type: 'value',
    gridIndex: curve.track,
    min: curve.min,
    max: curve.max,
    inverse: curve.inverse,
    show: false,
    splitNumber: 5
  }))

  const yAxis: any[] = trackLayout.map((_, track) => ({
    type: 'value' as const,
    gridIndex: track,
    min: minDepth,
    max: maxDepth,
    inverse: true,
    axisLine: { show: true, lineStyle: { color: '#64748b', width: 1 } },
    axisTick: { show: track === 0, length: 5, lineStyle: { color: '#64748b' } },
    axisLabel: {
      show: track === 0,
      margin: 9,
      color: '#334155',
      fontSize: 11,
      formatter: (value: number) => value.toFixed(1)
    },
    splitLine: { show: true, lineStyle: { color: '#cbd5e1', width: 1 } },
    minorTick: { show: true, splitNumber: 5 },
    minorSplitLine: { show: true, lineStyle: { color: '#eef2f7' } },
    axisPointer: {
      show: true,
      snap: true,
      label: {
        show: track === 0,
        formatter: ({ value }: { value: number }) => `${Number(value).toFixed(2)} m`
      }
    }
  }))

  const series = curves.map((curve, curveIndex) => ({
    name: curve.name,
    type: 'line' as const,
    xAxisIndex: curveIndex,
    yAxisIndex: curve.track,
    data: indices.map((index) => [curveValue(curve, index), Number(depths[index])]),
    showSymbol: false,
    symbol: 'none',
    connectNulls: false,
    animation: false,
    clip: true,
    progressive: 4000,
    progressiveThreshold: 8000,
    lineStyle: { color: curve.color, width: 1.25, type: curve.lineType ?? 'solid' },
    emphasis: { disabled: true }
  }))

  return {
    animation: false,
    backgroundColor: '#ffffff',
    grid: trackLayout.map((layout) => ({
      ...layout,
      top: 8,
      bottom: 32,
      containLabel: false,
      show: true,
      borderColor: '#64748b',
      borderWidth: 1
    })),
    xAxis,
    yAxis,
    series,
    axisPointer: {
      link: [{ yAxisIndex: [0, 1, 2, 3] }],
      lineStyle: { color: '#ef4444', type: 'dashed' }
    },
    tooltip: {
      trigger: 'axis',
      confine: true,
      backgroundColor: 'rgba(15, 23, 42, .94)',
      borderWidth: 0,
      textStyle: { color: '#f8fafc', fontSize: 12 },
      // 测井图以深度为主轴。明确使用 y 轴触发，避免 NPHI 等重复值
      // 被 ECharts 按相同 x 值一次性全部收进 tooltip。
      axisPointer: { type: 'line', axis: 'y' },
      formatter: (params: any) => {
        const rows = Array.isArray(params) ? params : [params]
        const uniqueRows = Array.from(
          new Map(rows.map((item: any) => [item.seriesName, item])).values()
        ) as any[]
        const depth = uniqueRows[0]?.value?.[1]
        const content = uniqueRows
          .map((item: any) => {
            const curve = curves.find((entry) => entry.name === item.seriesName)
            const value = item.value?.[0]
            return `<div style="display:flex;justify-content:space-between;gap:24px;margin-top:5px"><span><i style="display:inline-block;width:8px;height:8px;border-radius:50%;background:${item.color};margin-right:7px"></i>${item.seriesName}</span><b>${formatNumber(value)} ${curve?.unit ?? ''}</b></div>`
          })
          .join('')
        return `<div style="font-weight:600;margin-bottom:3px">深度 ${formatNumber(depth)} m</div>${content}`
      }
    },
    dataZoom: [
      {
        type: 'inside',
        yAxisIndex: [0, 1, 2, 3],
        filterMode: 'none',
        zoomOnMouseWheel: true,
        moveOnMouseMove: true,
        moveOnMouseWheel: false
      },
      {
        type: 'slider',
        yAxisIndex: [0, 1, 2, 3],
        filterMode: 'none',
        right: 4,
        top: 8,
        bottom: 32,
        width: 14,
        showDetail: false,
        borderColor: '#cbd5e1',
        fillerColor: 'rgba(37, 99, 235, .14)',
        handleSize: 14
      }
    ]
  }
}

function renderChart() {
  if (!chartRef.value) return
  if (!chart.value) chart.value = echarts.init(chartRef.value)
  chart.value.setOption(buildChartOption(), true)
}

function updateSampling() {
  sampledIndices.value = createDepthSampleIndices(dataset.value?.depths ?? [], SAMPLE_LIMIT)
  nextTick(renderChart)
}

async function loadWellData(wellId?: number) {
  if (!wellId) return
  dataLoading.value = true
  try {
    dataset.value = await DigitalApi.getWellLogDataset(wellId)
    updateSampling()
  } catch (error) {
    dataset.value = undefined
    sampledIndices.value = []
    renderChart()
    message.error('测井数据加载失败，请稍后重试')
  } finally {
    dataLoading.value = false
  }
}

async function loadWells() {
  listLoading.value = true
  let initialWellId: number | undefined
  try {
    const result = await DigitalApi.getWellList()
    wells.value = result?.list ?? []
    if (!selectedWellId.value && wells.value.length) selectedWellId.value = wells.value[0].id
    initialWellId = selectedWellId.value
  } catch (error) {
    message.error('井列表加载失败，请稍后重试')
  } finally {
    listLoading.value = false
  }

  if (initialWellId) await loadWellData(initialWellId)
}

function selectWell(well: DigitalWell) {
  if (well.id === selectedWellId.value) return
  selectedWellId.value = well.id
  loadWellData(well.id)
}

onMounted(async () => {
  resizeObserver = new ResizeObserver(() => chart.value?.resize())
  if (chartRef.value) resizeObserver.observe(chartRef.value)
  renderChart()
  await loadWells()
})

onBeforeUnmount(() => {
  resizeObserver?.disconnect()
  chart.value?.dispose()
})
</script>

<template>
  <div
    class="digital-shell h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]">
    <aside class="well-panel">
      <div class="panel-title">
        <div>
          <div class="eyebrow">WELL DIRECTORY</div>
          <h2>井目录</h2>
        </div>
        <span class="well-count">{{ wells.length }}</span>
      </div>

      <el-input v-model="keyword" :prefix-icon="Search" clearable placeholder="搜索井名 / UWI" />

      <div v-loading="listLoading" class="well-list">
        <button
          v-for="well in filteredWells"
          :key="well.id"
          type="button"
          class="well-item"
          :class="{ active: selectedWellId === well.id }"
          @click="selectWell(well)">
          <span class="well-status"></span>
          <span class="well-text">
            <strong>{{ getWellName(well) }}</strong>
            <small>{{ well.uwi || `ID ${well.id}` }}</small>
          </span>
          <span class="well-depth">{{ well.stop ? `${well.stop.toFixed(0)}m` : '' }}</span>
        </button>
        <el-empty
          v-if="!listLoading && !filteredWells.length"
          :image-size="70"
          description="暂无匹配井" />
      </div>

      <div v-if="selectedWell" class="well-summary">
        <div class="summary-caption">当前井信息</div>
        <dl>
          <div
            ><dt>井名</dt><dd>{{ getWellName(selectedWell) }}</dd></div
          >
          <div
            ><dt>UWI</dt><dd>{{ selectedWell.uwi || '--' }}</dd></div
          >
          <div
            ><dt>深度段</dt><dd>{{ depthRange }}</dd></div
          >
          <div
            ><dt>采样间隔</dt><dd>{{ selectedWell.step ?? '--' }} m</dd></div
          >
        </dl>
      </div>
    </aside>

    <main class="workspace">
      <section v-loading="dataLoading" class="log-board">
        <div class="track-header">
          <div v-for="(group, track) in trackCurves" :key="track" class="track-card">
            <div v-for="curve in group" :key="curve.key" class="curve-scale">
              <div class="curve-name" :style="{ color: curve.color }">
                <i :style="{ backgroundColor: curve.color }"></i>{{ curve.name }}
              </div>
              <div class="scale-row"
                ><span>{{ curve.inverse ? curve.max : curve.min }}</span
                ><b>{{ curve.unit }}</b
                ><span>{{ curve.inverse ? curve.min : curve.max }}</span></div
              >
            </div>
          </div>
        </div>
        <div class="depth-caption">DEPTH<br /><span>m</span></div>
        <div ref="chartRef" class="log-chart"></div>
        <el-empty
          v-if="!dataLoading && !originalCount"
          class="chart-empty"
          description="请选择有测井数据的井" />
      </section>
    </main>
  </div>
</template>

<style scoped lang="scss">
.digital-shell {
  --navy: #0f2742;

  display: flex;
  width: 100%;
  overflow: hidden;
  color: #1e293b;
  background: #e8edf3;
  border: 1px solid #cbd5e1;
  border-radius: 8px;
}

.eyebrow {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1.5px;
  color: #64748b;
}

.well-panel {
  display: flex;
  flex: 0 0 238px;
  flex-direction: column;
  gap: 14px;
  padding: 18px 14px;
  background: #f8fafc;
  border-right: 1px solid #cbd5e1;
}

.panel-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.panel-title h2 {
  margin: 3px 0 0;
  font-size: 20px;
  color: var(--navy);
}

.well-count {
  min-width: 30px;
  padding: 5px 8px;
  font-size: 12px;
  font-weight: 700;
  color: #fff;
  text-align: center;
  background: var(--navy);
  border-radius: 12px;
}

.well-list {
  flex: 1;
  min-height: 0;
  padding-right: 3px;
  overflow-y: auto;
}

.well-item {
  display: flex;
  width: 100%;
  padding: 10px;
  margin-bottom: 4px;
  color: #334155;
  text-align: left;
  cursor: pointer;
  background: transparent;
  border: 0;
  border-radius: 6px;
  transition: 0.15s ease;
  align-items: center;
  gap: 9px;
}

.well-item:hover {
  background: #e9eff6;
}

.well-item.active {
  color: #0f3d69;
  background: #dce9f6;
  box-shadow: inset 3px 0 #2563eb;
}

.well-status {
  width: 7px;
  height: 7px;
  flex: none;
  background: #22c55e;
  border-radius: 50%;
  box-shadow: 0 0 0 3px rgb(34 197 94 / 12%);
}

.well-text {
  display: flex;
  min-width: 0;
  flex: 1;
  flex-direction: column;
}

.well-text strong {
  overflow: hidden;
  font-size: 13px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.well-text small {
  margin-top: 2px;
  font-size: 10px;
  color: #94a3b8;
}

.well-depth {
  font-family: monospace;
  font-size: 10px;
  color: #64748b;
}

.well-summary {
  padding: 13px;
  background: #eef3f8;
  border: 1px solid #d8e0e9;
  border-radius: 6px;
}

.summary-caption {
  margin-bottom: 8px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1px;
  color: #64748b;
}

.well-summary dl {
  margin: 0;
}

.well-summary dl div {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding: 4px 0;
  font-size: 11px;
}

.well-summary dt {
  color: #64748b;
}

.well-summary dd {
  margin: 0;
  overflow: hidden;
  font-weight: 600;
  color: #1e293b;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.workspace {
  display: flex;
  min-width: 0;
  flex: 1;
  flex-direction: column;
  padding: 14px;
}

.log-board {
  position: relative;
  display: flex;
  min-height: 0;
  flex: 1;
  flex-direction: column;
  overflow: hidden;
  background: #fff;
  border: 1px solid #aebbc9;
}

.track-header {
  display: grid;
  flex: none;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1.15%;
  margin: 0 6% 0 7%;
  background: #64748b;
  border-right: 1px solid #64748b;
  border-left: 1px solid #64748b;
}

.track-card {
  min-width: 0;
  background: #fff;
  border-top: 1px solid #64748b;
}

.curve-scale {
  padding: 5px 7px 6px;
  border-bottom: 1px dotted #cbd5e1;
}

.curve-name {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 800;
}

.curve-name i {
  width: 18px;
  height: 2px;
  margin-right: 6px;
}

.scale-row {
  display: flex;
  margin-top: 3px;
  font-family: monospace;
  font-size: 11px;
  color: #334155;
  align-items: center;
  justify-content: space-between;
}

.scale-row b {
  font-weight: 500;
  color: #64748b;
}

.depth-caption {
  position: absolute;
  top: 9px;
  left: 1%;
  z-index: 2;
  width: 5%;
  font-size: 13px;
  font-weight: 800;
  line-height: 1.4;
  color: #334155;
  text-align: center;
}

.depth-caption span {
  font-size: 11px;
  font-weight: 500;
  color: #64748b;
}

.log-chart {
  width: 100%;
  min-height: 360px;
  flex: 1;
}

.chart-empty {
  position: absolute;
  inset: 150px 0 0;
  background: rgb(255 255 255 / 85%);
}

@media (width <= 1200px) {
  .well-panel {
    flex-basis: 205px;
  }
}
</style>
