<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import * as echarts from 'echarts'
import { controlCaseApi, getAnalizeSeries, getControlCaseChartData, getControlCaseFieldMap } from '@/api.ts'
import { formatApiError } from '@/api_client.ts'
import type { ControlCase } from '@/models.ts'

const fieldOptions = [
  { value: 'psi', label: 'psi' },
  { value: 'phi', label: 'phi' },
  { value: 'T', label: 'T' },
  { value: 'C', label: 'C' },
] as const

const parameterOptions = [
  { value: 'Rel', label: 'Rel' },
  { value: 'RelC', label: 'RelC' },
  { value: 'Le', label: 'Le' },
  { value: 'Pr', label: 'Pr' },
  { value: 'Pe', label: 'Pe' },
  { value: 'Ma', label: 'Ma' },
  { value: 'Time', label: 'Time' },
] as const

const selectedParameter = ref<(typeof parameterOptions)[number]['value']>('Rel')
const filterParameter = ref('')
const filterMin = ref('')
const filterMax = ref('')
const loading = ref(false)
const error = ref('')
const containerRef = ref<HTMLDivElement | null>(null)
const caseChartRef = ref<HTMLDivElement | null>(null)
const chartTitle = ref('')
const seriesData = ref<{ case_id: number; x: number; omega: number; psi_max: number; case_name: string }[]>([])
const selectedCaseId = ref<number | null>(null)
const selectedCase = ref<ControlCase | null>(null)
const selectedCaseLoading = ref(false)
const selectedCaseError = ref('')
const selectedCasePoints = ref<{ t: number; psi_m: number }[]>([])
const selectedFieldMap = ref<{
  requested_t: number
  stage_t: number
  rows: number
  cols: number
  fields: Record<string, number[][]>
} | null>(null)
const selectedFieldMapLoading = ref(false)
const selectedFieldMapError = ref('')
const selectedField = ref<(typeof fieldOptions)[number]['value']>('psi')
const fieldMapRef = ref<HTMLDivElement | null>(null)
let chart: echarts.ECharts | null = null
let caseChart: echarts.ECharts | null = null
let fieldMapChart: echarts.ECharts | null = null

const selectedLabel = computed(
  () => parameterOptions.find((item) => item.value === selectedParameter.value)?.label ?? selectedParameter.value,
)

function buildOption() {
  return {
    title: { text: `Omega / PsiMax vs ${chartTitle.value || selectedLabel.value}` },
    tooltip: {
      trigger: 'item',
      formatter: (params: any) => {
        const p = params.data
        const valueLabel = params.seriesName === 'Omega' ? 'Omega' : 'PsiMax'
        const value = params.seriesName === 'Omega' ? p.omega : p.psi_max
        return [
          `<strong>${p.case_name}</strong>`,
          `${selectedLabel.value}: ${p.x}`,
          `${valueLabel}: ${value}`,
        ].join('<br/>')
      },
    },
    legend: { top: 28 },
    grid: { left: 56, right: 32, top: 80, bottom: 56, containLabel: true },
    xAxis: { type: 'value', name: selectedLabel.value },
    yAxis: [
      { type: 'value', name: 'Omega', position: 'left' },
      { type: 'value', name: 'PsiMax', position: 'right' },
    ],
    series: [
      {
        name: 'Omega',
        type: 'scatter',
        symbolSize: 10,
        yAxisIndex: 0,
        data: seriesData.value.map((point) => ({
          case_id: point.case_id,
          name: point.case_name,
          value: [point.x, point.omega],
          case_name: point.case_name,
          x: point.x,
          omega: point.omega,
          psi_max: point.psi_max,
        })),
      },
      {
        name: 'PsiMax',
        type: 'scatter',
        symbolSize: 10,
        yAxisIndex: 1,
        data: seriesData.value.map((point) => ({
          case_id: point.case_id,
          name: point.case_name,
          value: [point.x, point.psi_max],
          case_name: point.case_name,
          x: point.x,
          omega: point.omega,
          psi_max: point.psi_max,
        })),
      },
    ],
  }
}

function renderChart() {
  if (!chart) return
  chart.setOption(buildOption(), true)
}

function buildCaseOption() {
  return {
    title: { text: 'psi_m / time' },
    tooltip: { trigger: 'axis' },
    grid: { left: 56, right: 24, top: 48, bottom: 48, containLabel: true },
    xAxis: { type: 'value', name: 't' },
    yAxis: { type: 'value', name: 'psi_m' },
    series: [
      {
        type: 'line',
        smooth: true,
        showSymbol: false,
        data: selectedCasePoints.value.map((point) => [point.t, point.psi_m]),
      },
    ],
  }
}

function renderCaseChart() {
  if (!caseChart) return
  caseChart.setOption(buildCaseOption(), true)
}

function buildFieldMapOption() {
  const payload = selectedFieldMap.value
  if (!payload) return null

  const matrix = payload.fields[selectedField.value] ?? []
  const rows = matrix.length
  const cols = matrix[0]?.length ?? 0
  const seriesData: Array<[number, number, number]> = []
  let min = Number.POSITIVE_INFINITY
  let max = Number.NEGATIVE_INFINITY

  matrix.forEach((row, y) => {
    row.forEach((value, x) => {
      seriesData.push([x, y, value])
      if (Number.isFinite(value)) {
        min = Math.min(min, value)
        max = Math.max(max, value)
      }
    })
  })

  if (!Number.isFinite(min) || !Number.isFinite(max)) {
    min = 0
    max = 1
  }

  return {
    title: {
      text: `Field map: ${selectedField.value}`,
      subtext: `requested t=${payload.requested_t}, stage t=${payload.stage_t}`,
    },
    tooltip: {
      position: 'top',
      formatter: (params: any) => {
        const [x, y, value] = params.data as [number, number, number]
        return [`<strong>${selectedField.value}</strong>`, `x: ${x}`, `y: ${y}`, `value: ${value}`].join('<br/>')
      },
    },
    grid: { left: 56, right: 32, top: 64, bottom: 48, containLabel: true },
    xAxis: { type: 'category', name: 'x', data: Array.from({ length: cols }, (_, index) => index) },
    yAxis: { type: 'category', name: 'y', data: Array.from({ length: rows }, (_, index) => index), inverse: true },
    visualMap: {
      min,
      max,
      calculable: true,
      orient: 'horizontal',
      left: 'center',
      bottom: 0,
      inRange: {
        color: ['#0000ff', '#00ffff', '#00ff00', '#ffff00', '#ff7f00', '#ff0000'],
      },
    },
    series: [
      {
        type: 'heatmap',
        data: seriesData,
        emphasis: { itemStyle: { shadowBlur: 10, shadowColor: 'rgba(0, 0, 0, 0.35)' } },
      },
    ],
  }
}

function renderFieldMap() {
  if (!fieldMapChart || !selectedFieldMap.value) return
  const option = buildFieldMapOption()
  if (option) {
    fieldMapChart.setOption(option, true)
  }
}

function formatValue(value: unknown) {
  if (value == null || value === '') return '—'
  if (typeof value === 'object') return JSON.stringify(value)
  return String(value)
}

function caseObject(value: ControlCase | null) {
  return (value ?? {}) as Record<string, unknown>
}

function paramsObject(value: ControlCase | null) {
  const record = caseObject(value)
  return (record.Params ?? record.params ?? {}) as Record<string, unknown>
}

async function loadData() {
  loading.value = true
  error.value = ''

  try {
    const response = await getAnalizeSeries(selectedParameter.value, {
      filter_parameter: filterParameter.value || undefined,
      filter_min: filterMin.value === '' ? undefined : Number(filterMin.value),
      filter_max: filterMax.value === '' ? undefined : Number(filterMax.value),
    })
    chartTitle.value = response.label
    seriesData.value = response.points.map((point) => ({
      case_id: point.case_id,
      x: point.x,
      omega: point.omega,
      psi_max: point.psi_max,
      case_name: point.case_name,
    }))
    renderChart()
  } catch (err) {
    error.value = formatApiError(err, 'Не удалось загрузить данные графика.')
    seriesData.value = []
  } finally {
    loading.value = false
  }
}

async function loadSelectedCase(caseId: number) {
  selectedCaseId.value = caseId
  selectedCaseLoading.value = true
  selectedCaseError.value = ''
  selectedCase.value = null
  selectedCasePoints.value = []
  selectedFieldMap.value = null
  selectedFieldMapError.value = ''
  selectedFieldMapLoading.value = false
  fieldMapChart?.clear()

  try {
    const [caseData, csvData] = await Promise.all([
      controlCaseApi.retrieve(caseId),
      getControlCaseChartData(caseId),
    ])

    selectedCase.value = caseData

    const tIndex = csvData.columns.findIndex((column) => column.toLowerCase() === 't')
    const psiMIndex = csvData.columns.findIndex((column) => column.toLowerCase() === 'psi_m')

    if (tIndex < 0 || psiMIndex < 0) {
      throw new Error('CSV does not contain t/psi_m columns')
    }

    selectedCasePoints.value = csvData.rows
      .map((row) => ({
        t: Number(row[tIndex]),
        psi_m: Number(row[psiMIndex]),
      }))
      .filter((point) => Number.isFinite(point.t) && Number.isFinite(point.psi_m))

    renderCaseChart()
  } catch (err) {
    selectedCaseError.value = formatApiError(err, 'Не удалось загрузить расчетный случай.')
  } finally {
    selectedCaseLoading.value = false
  }
}

async function loadFieldMap(time: number) {
  if (!selectedCaseId.value) return

  selectedFieldMapLoading.value = true
  selectedFieldMapError.value = ''

  try {
    selectedFieldMap.value = await getControlCaseFieldMap(selectedCaseId.value, time)
    renderFieldMap()
  } catch (err) {
    selectedFieldMapError.value = formatApiError(err, 'Не удалось загрузить карту полей.')
    selectedFieldMap.value = null
    fieldMapChart?.clear()
  } finally {
    selectedFieldMapLoading.value = false
  }
}

function handleResize() {
  chart?.resize()
  caseChart?.resize()
  fieldMapChart?.resize()
}

function resetFilters() {
  filterParameter.value = ''
  filterMin.value = ''
  filterMax.value = ''
  void loadData()
}

onMounted(async () => {
  if (containerRef.value) {
    chart = echarts.init(containerRef.value)
    chart.on('click', (params: any) => {
      const caseId = Number(params?.data?.case_id)
      if (Number.isFinite(caseId) && caseId > 0) {
        void loadSelectedCase(caseId)
      }
    })
  }
  if (caseChartRef.value) {
    caseChart = echarts.init(caseChartRef.value)
    caseChart.on('click', (params: any) => {
      const t = Number(params?.data?.[0])
      if (Number.isFinite(t)) {
        void loadFieldMap(t)
      }
    })
  }
  if (fieldMapRef.value) {
    fieldMapChart = echarts.init(fieldMapRef.value)
    window.addEventListener('resize', handleResize)
  }
  await loadData()
})

watch(selectedParameter, loadData)
watch(selectedField, renderFieldMap)
watch(selectedFieldMap, renderFieldMap)

onBeforeUnmount(() => {
  window.removeEventListener('resize', handleResize)
  chart?.dispose()
  caseChart?.dispose()
  fieldMapChart?.dispose()
  chart = null
  caseChart = null
  fieldMapChart = null
})
</script>

<template>
  <section class="d-flex flex-column gap-3">
    <div class="d-flex flex-wrap justify-content-between align-items-center gap-2">
      <div>
        <h1 class="h3 mb-1">Analize graph</h1>
        <p class="text-muted mb-0">Omega и PsiMax по выбранному параметру</p>
      </div>
      <div class="d-flex gap-2 align-items-center">
        <select v-model="selectedParameter" class="form-select">
          <option v-for="option in parameterOptions" :key="option.value" :value="option.value">
            {{ option.label }}
          </option>
        </select>
        <button class="btn btn-outline-secondary" type="button" @click="loadData" :disabled="loading">
          Обновить
        </button>
      </div>
    </div>

    <div class="card shadow-sm">
      <div class="card-body">
        <div class="row g-3 align-items-end">
          <div class="col-12 col-lg-4">
            <label class="form-label">Фильтр по параметру</label>
            <select v-model="filterParameter" class="form-select">
              <option value="">Без фильтра</option>
              <option v-for="option in parameterOptions" :key="option.value" :value="option.value">
                {{ option.label }}
              </option>
            </select>
          </div>
          <div class="col-6 col-lg-2">
            <label class="form-label">Min</label>
            <input v-model="filterMin" type="number" step="any" class="form-control" />
          </div>
          <div class="col-6 col-lg-2">
            <label class="form-label">Max</label>
            <input v-model="filterMax" type="number" step="any" class="form-control" />
          </div>
          <div class="col-12 col-lg-4 d-flex justify-content-end gap-2">
            <button class="btn btn-outline-secondary" type="button" @click="resetFilters" :disabled="loading">
              Сбросить фильтр
            </button>
            <button class="btn btn-primary" type="button" @click="loadData" :disabled="loading">
              Применить фильтр
            </button>
          </div>
        </div>
      </div>
    </div>

    <div v-if="error" class="alert alert-danger mb-0" role="alert">{{ error }}</div>

    <div class="card shadow-sm">
      <div class="card-body">
        <div class="position-relative" style="min-height: 560px;">
          <div ref="containerRef" style="height: 560px; width: 100%;"></div>
          <div
            v-if="loading"
            class="position-absolute top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center bg-white bg-opacity-75"
          >
            <div class="text-muted">Загрузка...</div>
          </div>
        </div>
      </div>
    </div>

    <div class="card shadow-sm">
      <div class="card-header bg-white d-flex justify-content-between align-items-center flex-wrap gap-2">
        <div class="fw-semibold">Выбранный расчетный случай</div>
        <div v-if="selectedCaseId" class="text-muted small">ID {{ selectedCaseId }}</div>
      </div>
      <div class="card-body">
        <div v-if="selectedCaseLoading" class="text-center text-muted py-4">Загрузка...</div>
        <div v-else-if="selectedCaseError" class="alert alert-danger mb-0" role="alert">
          {{ selectedCaseError }}
        </div>
        <template v-else-if="selectedCase">
          <div class="row g-3 mb-3">
            <div class="col-12 col-lg-4">
              <div class="text-muted small">Название</div>
              <div class="fw-semibold">{{ selectedCase.Name }}</div>
            </div>
            <div class="col-12 col-lg-4">
              <div class="text-muted small">Статус</div>
              <div class="fw-semibold">{{ selectedCase.Status }}</div>
            </div>
            <div class="col-12 col-lg-4">
              <div class="text-muted small">Params ID</div>
              <div class="fw-semibold">{{ selectedCase.ParamsID }}</div>
            </div>
          </div>

          <details class="mb-3">
            <summary class="fw-semibold">ControlCase</summary>
            <div class="table-responsive mt-2">
              <table class="table table-sm align-middle mb-0">
                <tbody>
                  <tr v-for="([key, value]) in Object.entries(caseObject(selectedCase))" :key="key">
                    <th class="table-light" style="width: 240px">{{ key }}</th>
                    <td>{{ formatValue(value) }}</td>
                  </tr>
                </tbody>
              </table>
            </div>
          </details>

          <div class="table-responsive mb-3">
            <div class="fw-semibold mb-2">Params</div>
            <table class="table table-sm align-middle mb-0">
              <tbody>
                <tr v-for="([key, value]) in Object.entries(paramsObject(selectedCase))" :key="key">
                  <th class="table-light" style="width: 240px">{{ key }}</th>
                  <td>{{ formatValue(value) }}</td>
                </tr>
              </tbody>
            </table>
          </div>
        </template>

        <div class="position-relative" style="min-height: 360px;">
          <div ref="caseChartRef" style="height: 360px; width: 100%;"></div>
          <div
            v-if="!selectedCase && !selectedCaseLoading && !selectedCaseError"
            class="position-absolute top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center text-muted"
          >
            Нажмите на точку на верхнем графике, чтобы увидеть расчетный случай.
          </div>
        </div>

        <div class="d-flex flex-wrap gap-2 mt-3">
          <button
            v-for="option in fieldOptions"
            :key="option.value"
            type="button"
            class="btn btn-sm"
            :class="selectedField === option.value ? 'btn-primary' : 'btn-outline-primary'"
            @click="selectedField = option.value"
            :disabled="!selectedFieldMap"
          >
            {{ option.label }}
          </button>
        </div>

        <div class="position-relative mt-3" style="min-height: 420px;">
          <div ref="fieldMapRef" style="height: 420px; width: 100%;"></div>
          <div
            v-if="selectedFieldMapLoading"
            class="position-absolute top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center bg-white bg-opacity-75"
          >
            <div class="text-muted">Загрузка карты полей...</div>
          </div>
          <div
            v-else-if="selectedFieldMapError"
            class="position-absolute top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center text-danger"
          >
            {{ selectedFieldMapError }}
          </div>
          <div
            v-else-if="!selectedFieldMap"
            class="position-absolute top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center text-muted"
          >
            Нажмите на точку графика `psi_m`, чтобы показать карту полей.
          </div>
        </div>
      </div>
    </div>
  </section>
</template>
