<template>
  <div class="ota-vehicles">
    <div class="toolbar">
      <el-input v-model="search" size="small" clearable :placeholder="t.searchPh" class="search" />
      <el-checkbox v-model="onlyMismatch">{{ t.onlyMismatch }}</el-checkbox>
      <div class="latency-switch">
        <span>{{ t.latency }}</span>
        <el-switch v-model="latencyOn" @change="onLatencyToggle" />
      </div>
      <el-button size="small" plain :icon="Refresh" :loading="loading" @click="reload">{{ t.refresh }}</el-button>
      <el-button
        size="small"
        type="success"
        plain
        :disabled="!canWrite || selectedRows.length !== 1"
        :loading="pulling"
        @click="pullSelected"
      >
        {{ t.pullFromCar }}
      </el-button>
      <el-dropdown :disabled="!canWrite || !selectedRows.length" @command="onSyncCommand">
        <el-button size="small" type="primary" :disabled="!canWrite || !selectedRows.length">
          {{ t.sync }}
          <el-icon class="el-icon--right"><ArrowDown /></el-icon>
        </el-button>
        <template #dropdown>
          <el-dropdown-menu>
            <el-dropdown-item command="all">{{ t.syncAll }}</el-dropdown-item>
            <el-dropdown-item command="M.exe">{{ t.syncM }}</el-dropdown-item>
            <el-dropdown-item command="D.exe">{{ t.syncD }}</el-dropdown-item>
            <el-dropdown-item command="C.exe">{{ t.syncC }}</el-dropdown-item>
          </el-dropdown-menu>
        </template>
      </el-dropdown>
    </div>

    <p class="hint-line">{{ t.vehiclesHint }}</p>

    <el-table
      ref="tableRef"
      v-loading="loading"
      class="ota-table"
      :data="filtered"
      size="small"
      height="420"
      row-key="id"
      stripe
      @selection-change="onSelect"
    >
      <el-table-column type="selection" width="48" />
      <el-table-column :label="t.vehicle" min-width="140">
        <template #default="{ row }">
          <div class="car-cell">
            <strong>{{ row.name }}</strong>
            <span class="mono dim">{{ row.id }}</span>
          </div>
        </template>
      </el-table-column>
      <el-table-column prop="ip" label="IP" width="120">
        <template #default="{ row }"><span class="mono">{{ row.ip || '-' }}</span></template>
      </el-table-column>
      <el-table-column label="Medulla" width="110">
        <template #default="{ row }">
          <span class="match" :class="matchState(row, 'M.exe')" :title="row.medulla?.exe?.version || ''">
            <i /><span>{{ shortHash(row.medulla?.exe?.version) }}</span>
          </span>
        </template>
      </el-table-column>
      <el-table-column label="Detour" width="110">
        <template #default="{ row }">
          <span class="match" :class="matchState(row, 'D.exe')" :title="row.detour?.exe?.version || ''">
            <i /><span>{{ shortHash(row.detour?.exe?.version) }}</span>
          </span>
        </template>
      </el-table-column>
      <el-table-column label="Clumsy" width="110">
        <template #default="{ row }">
          <span class="match" :class="matchState(row, 'C.exe')" :title="row.clumsy?.exe?.version || ''">
            <i /><span>{{ shortHash(row.clumsy?.exe?.version) }}</span>
          </span>
        </template>
      </el-table-column>
      <el-table-column label="RTT" width="90">
        <template #default="{ row }">
          <span :class="rttClass(row)">{{ rttLabel(row) }}</span>
        </template>
      </el-table-column>
      <el-table-column prop="state" :label="t.state" width="90" />
    </el-table>

    <div v-if="selectedRows.length" class="action-bar">
      <span>{{ t.selectedPrefix }} <b>{{ selectedRows.length }}</b> {{ t.selectedSuffix }}</span>
      <span class="dim">{{ t.batchPrefix }} {{ Math.ceil(selectedRows.length / Math.max(1, settings?.maxCar ?? 2)) }}</span>
      <el-button
        size="small"
        type="success"
        plain
        :disabled="!canWrite || selectedRows.length !== 1"
        :loading="pulling"
        @click="pullSelected"
      >
        {{ t.pullFromCar }}
      </el-button>
      <el-button type="primary" size="small" :disabled="!canWrite" @click="confirmSync()">{{ t.startDeploy }}</el-button>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { ArrowDown, Refresh } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
  createOtaSyncJob,
  getOtaSettings,
  listOtaVehicles,
  pullOtaPackage,
  putOtaSettings
} from '@/api/ota'
import type { OtaSettings, OtaVehicleRow } from '@/types/ota'
import { useOtaWorkbench } from '@/composables/useOtaWorkbench'
import { OTA_COPY } from './otaCopy'

defineProps<{ canWrite: boolean }>()
const emit = defineEmits<{ jobCreated: []; pulled: [] }>()

const t = OTA_COPY
const { refreshMeta } = useOtaWorkbench()
const rows = ref<OtaVehicleRow[]>([])
const selectedRows = ref<OtaVehicleRow[]>([])
const loading = ref(false)
const pulling = ref(false)
const search = ref('')
const onlyMismatch = ref(false)
const latencyOn = ref(false)
const settings = ref<OtaSettings | null>(null)
const pendingComponents = ref<string[] | undefined>(undefined)

function shortHash(h?: string) {
  if (!h) return '-'
  return h.length > 8 ? h.slice(0, 8) : h
}

function matchState(row: OtaVehicleRow, key: string) {
  if (!row.reachable) return 'offline'
  return row.match?.[key] || 'unknown'
}

const filtered = computed(() => {
  const q = search.value.trim().toLowerCase()
  return rows.value.filter((r) => {
    if (onlyMismatch.value) {
      const bad = Object.values(r.match || {}).some((v) => v === 'mismatch')
      if (!bad) return false
    }
    if (!q) return true
    return [r.id, r.name, r.ip].some((x) => (x || '').toLowerCase().includes(q))
  })
})

function rttLabel(row: OtaVehicleRow) {
  if (!latencyOn.value) return '-'
  if (row.rttMs == null) return 'timeout'
  return `${row.rttMs}ms`
}

function rttClass(row: OtaVehicleRow) {
  if (!latencyOn.value || row.rttMs == null) return 'mono dim'
  const th = settings.value?.rttThresholdMs ?? 200
  return row.rttMs > th ? 'mono danger' : 'mono ok'
}

function onSelect(list: OtaVehicleRow[]) {
  selectedRows.value = list
}

async function reload() {
  loading.value = true
  try {
    settings.value = await getOtaSettings()
    latencyOn.value = !!settings.value.latencyEnabled
    rows.value = await listOtaVehicles(latencyOn.value)
  } catch (e: any) {
    ElMessage.error(e?.message || t.loadFail)
  } finally {
    loading.value = false
  }
}

async function onLatencyToggle(v: string | number | boolean) {
  const on = !!v
  try {
    if (!settings.value) settings.value = await getOtaSettings()
    settings.value = await putOtaSettings({ ...settings.value, latencyEnabled: on })
    await reload()
  } catch (e: any) {
    ElMessage.error(e?.message || t.latencySaveFail)
    latencyOn.value = !on
  }
}

async function pullSelected() {
  if (selectedRows.value.length !== 1) {
    ElMessage.warning(t.pullNeedOne)
    return
  }
  const car = selectedRows.value[0]
  if (!car.ip) {
    ElMessage.warning(t.pullNeedIp)
    return
  }
  pulling.value = true
  try {
    const pkg = await pullOtaPackage(car.id)
    const n = Object.keys(pkg.components || {}).length
    if (!n) {
      ElMessage.warning(t.pullEmpty)
      return
    }
    ElMessage.success(t.pullOk)
    emit('pulled')
    void refreshMeta()
  } catch (e: any) {
    ElMessage.error(e?.message || t.pullFail)
  } finally {
    pulling.value = false
  }
}

function onSyncCommand(cmd: string) {
  pendingComponents.value = cmd === 'all' ? undefined : [cmd]
  void confirmSync(pendingComponents.value)
}

async function confirmSync(components?: string[]) {
  if (!selectedRows.value.length) return
  const comps = components === undefined ? pendingComponents.value : components
  const compsLabel = comps?.length ? comps.join(', ') : t.compsAll
  try {
    await ElMessageBox.confirm(
      t.confirmBody(selectedRows.value.length, compsLabel),
      t.confirmTitle,
      { type: 'warning', confirmButtonText: t.confirmOk, cancelButtonText: t.confirmCancel }
    )
  } catch {
    return
  }

  let carIds = selectedRows.value.map((c) => c.id)
  const th = settings.value?.rttThresholdMs ?? 200
  if (latencyOn.value && settings.value?.overThreshold === 'skip') {
    const skipped = selectedRows.value.filter((c) => c.rttMs == null || c.rttMs > th)
    if (skipped.length) {
      ElMessage.warning(t.skipLatency(skipped.length))
      carIds = selectedRows.value.filter((c) => c.rttMs != null && c.rttMs <= th).map((c) => c.id)
    }
  }
  if (!carIds.length) {
    ElMessage.error(t.noCars)
    return
  }

  try {
    await createOtaSyncJob({
      carIds,
      components: comps,
      requireLatencyCheck: latencyOn.value
    })
    ElMessage.success(t.jobCreated)
    emit('jobCreated')
    void refreshMeta()
  } catch (e: any) {
    ElMessage.error(e?.message || t.jobFail)
  }
}

onMounted(() => void reload())
</script>

<style scoped>
.ota-vehicles {
  --ota-fg: rgb(var(--mg-text-tint-rgb));
  --ota-fg-muted: rgba(var(--mg-text-hi-rgb), 0.55);
  --ota-line: rgba(var(--mg-primary-rgb), 0.12);
  color: var(--ota-fg);
}

.toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  margin-bottom: 8px;
}

.hint-line {
  margin: 0 0 10px;
  font-size: 12px;
  color: var(--ota-fg-muted);
}

.search {
  width: 220px;
}

.latency-switch {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--ota-fg);
  padding: 5px 12px;
  border-radius: 999px;
  background: rgba(var(--mg-primary-rgb), 0.08);
  border: 1px solid rgba(var(--mg-primary-rgb), 0.14);
}

.car-cell {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.car-cell strong {
  font-weight: 600;
  color: var(--ota-fg);
}

.mono {
  font-family: var(--mg-font-mono);
  font-size: 12px;
}

.dim {
  color: var(--ota-fg-muted);
}

.ok {
  color: var(--mg-status-success);
}

.danger {
  color: var(--mg-status-danger);
}

.action-bar {
  position: sticky;
  bottom: 0;
  margin-top: 12px;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  padding: 10px 14px;
  border-radius: 10px;
  background: rgba(var(--mg-bg-card-rgb), 0.98);
  border: 1px solid rgba(var(--mg-primary-rgb), 0.22);
  box-shadow: 0 8px 20px rgba(var(--mg-primary-rgb), 0.08);
}

.match {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--mg-font-mono);
  font-size: 11px;
  color: var(--ota-fg);
}

.match i {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--mg-status-idle);
}

.match.match i {
  background: var(--mg-status-success);
}

.match.mismatch i {
  background: var(--mg-status-warning);
}

.match.offline i,
.match.unknown i {
  background: var(--mg-status-idle);
}

:deep(.ota-table) {
  --el-table-bg-color: #fff;
  --el-table-tr-bg-color: #fff;
  --el-table-header-bg-color: rgba(var(--mg-primary-rgb), 0.06);
  --el-table-row-hover-bg-color: rgba(var(--mg-primary-rgb), 0.06);
  --el-table-text-color: rgb(var(--mg-text-tint-rgb));
  --el-table-header-text-color: rgba(var(--mg-text-hi-rgb), 0.9);
  --el-table-border-color: rgba(var(--mg-primary-rgb), 0.12);
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid var(--ota-line);
}

:deep(.ota-table .el-checkbox__inner) {
  width: 16px !important;
  height: 16px !important;
  border: 1.5px solid #7c3aed !important;
  background: #fff !important;
}

:deep(.ota-table .el-checkbox__input.is-checked .el-checkbox__inner),
:deep(.ota-table .el-checkbox__input.is-indeterminate .el-checkbox__inner) {
  background: #7c3aed !important;
  border-color: #7c3aed !important;
}

:deep(.ota-table .el-checkbox__input.is-checked .el-checkbox__inner::after) {
  border-color: #fff !important;
}
</style>
