<template>
  <div class="ota-custom">
    <header class="head">
      <h3>自定义文件同步</h3>
      <p>支持多文件批量推送；目标路径为小车内目录，各文件保留原文件名。对齐 CarOTA 自定义同步。</p>
    </header>

    <div class="split">
      <section class="left">
        <div class="step">
          <h4>1. 本地文件（可多选）</h4>
          <div class="row">
            <el-button size="small" @click="clearFiles">清空</el-button>
            <el-upload
              :auto-upload="false"
              :show-file-list="false"
              multiple
              :on-change="onFileChange"
            >
              <el-button size="small" type="primary" plain>添加文件…</el-button>
            </el-upload>
          </div>
          <ul class="file-list">
            <li v-for="(f, i) in files" :key="f.uid">
              <span class="mono">{{ f.name }}</span>
              <button type="button" class="x" @click="removeFile(i)">×</button>
            </li>
            <li v-if="!files.length" class="empty">尚未添加文件</li>
          </ul>
          <p class="muted">已选 {{ files.length }} 个文件</p>
        </div>

        <div class="step">
          <h4>2. 小车内目标路径</h4>
          <el-select
            v-model="remotePath"
            size="small"
            filterable
            allow-create
            default-first-option
            class="w-full"
            placeholder="例如 C:\\Program Files\\Medulla\\plugins"
          >
            <el-option v-for="h in pathHistory" :key="h" :label="h" :value="h" />
          </el-select>
          <p class="muted">所有文件推到同一目录，各自保留文件名</p>
        </div>

        <div class="step">
          <h4>3. 更新后重启（可多选）</h4>
          <p class="warn">不勾选则只推送文件。WatchDog 更新建议勾选重启。</p>
          <el-checkbox v-model="restartMedulla">重启 Medulla</el-checkbox>
          <el-checkbox v-model="restartClumsy">重启 Clumsy</el-checkbox>
          <el-checkbox v-model="restartDetour">重启 Detour</el-checkbox>
          <el-checkbox v-model="restartWatchDog">重启 WatchDog</el-checkbox>
        </div>
      </section>

      <section class="right">
        <div class="right-head">
          <h4>4. 选择车辆</h4>
          <el-checkbox v-model="selectAll" :indeterminate="indeterminate" @change="onSelectAll">全选</el-checkbox>
        </div>
        <p class="muted">已选 {{ selectedIds.length }} / {{ carsWithIp.length }} 台</p>
        <div class="car-cards">
          <button
            v-for="c in carsWithIp"
            :key="c.id"
            type="button"
            class="car-card"
            :class="{ on: selectedIds.includes(c.id) }"
            :aria-pressed="selectedIds.includes(c.id)"
            @click="toggleCar(c.id)"
          >
            <span class="tick" :class="{ on: selectedIds.includes(c.id) }" aria-hidden="true" />
            <div>
              <strong>{{ c.name }}</strong>
              <span class="mono">{{ c.ip }}</span>
            </div>
          </button>
        </div>
      </section>
    </div>

    <footer class="foot">
      <el-button @click="reset">取消</el-button>
      <el-button type="primary" :disabled="!canWrite" :loading="pushing" @click="startSync">开始同步</el-button>
    </footer>
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue'
import type { CheckboxValueType, UploadFile, UploadFiles } from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import { listOtaVehicles, pushOtaCustomFile } from '@/api/ota'
import type { OtaVehicleRow } from '@/types/ota'

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

const HIST_KEY = 'migu.ota.custom.remotePathHistory'

const cars = ref<OtaVehicleRow[]>([])
const files = ref<UploadFile[]>([])
const remotePath = ref('')
const pathHistory = ref<string[]>([])
const restartMedulla = ref(false)
const restartClumsy = ref(false)
const restartDetour = ref(false)
const restartWatchDog = ref(false)
const selectedIds = ref<string[]>([])
const pushing = ref(false)

const carsWithIp = computed(() => cars.value.filter((c) => !!c.ip))
const selectAll = ref(false)
const indeterminate = computed(() => {
  const n = selectedIds.value.length
  return n > 0 && n < carsWithIp.value.length
})

watch(carsWithIp, (list) => {
  if (!selectedIds.value.length && list.length) {
    selectedIds.value = list.map((c) => c.id)
    selectAll.value = true
  }
})

function onSelectAll(v: CheckboxValueType) {
  selectedIds.value = v ? carsWithIp.value.map((c) => c.id) : []
}

function toggleCar(id: string) {
  const set = new Set(selectedIds.value)
  if (set.has(id)) set.delete(id)
  else set.add(id)
  selectedIds.value = [...set]
  selectAll.value = selectedIds.value.length === carsWithIp.value.length
}

function onFileChange(_file: UploadFile, fileList: UploadFiles) {
  // 合并去重
  const map = new Map<string, UploadFile>()
  for (const f of [...files.value, ...fileList]) {
    if (f.raw) map.set(`${f.name}:${f.size}`, f)
  }
  files.value = [...map.values()]
}

function removeFile(i: number) {
  files.value.splice(i, 1)
}

function clearFiles() {
  files.value = []
}

function reset() {
  clearFiles()
  restartMedulla.value = false
  restartClumsy.value = false
  restartDetour.value = false
  restartWatchDog.value = false
}

function buildOps(): number[] {
  const ops: number[] = []
  if (restartMedulla.value) ops.push(0)
  if (restartClumsy.value) ops.push(1)
  if (restartDetour.value) ops.push(2)
  if (restartWatchDog.value) ops.push(3)
  return ops.length ? ops : [-1]
}

function loadHistory() {
  try {
    const raw = localStorage.getItem(HIST_KEY)
    pathHistory.value = raw ? (JSON.parse(raw) as string[]) : []
    if (pathHistory.value[0]) remotePath.value = pathHistory.value[0]
  } catch {
    pathHistory.value = []
  }
}

function saveHistory(path: string) {
  const next = [path, ...pathHistory.value.filter((p) => p !== path)].slice(0, 20)
  pathHistory.value = next
  localStorage.setItem(HIST_KEY, JSON.stringify(next))
}

async function startSync() {
  if (!files.value.length) {
    ElMessage.warning('请至少添加一个本地文件')
    return
  }
  if (!remotePath.value.trim()) {
    ElMessage.warning('请填写小车内目标路径')
    return
  }
  if (!selectedIds.value.length) {
    ElMessage.warning('请至少选择一台车')
    return
  }
  const names = files.value.map((f) => f.name)
  const preview = names.slice(0, 8).join('\n') + (names.length > 8 ? `\n…共 ${names.length} 个` : '')
  const ops = buildOps()
  const opLabel = ops[0] === -1 && ops.length === 1
    ? '不重启'
    : ops.map((o) => ({ 0: 'Medulla', 1: 'Clumsy', 2: 'Detour', 3: 'WatchDog' }[o] || String(o))).join('+')

  try {
    await ElMessageBox.confirm(
      `文件：\n${preview}\n\n路径：${remotePath.value}\n车辆：${selectedIds.value.length} 台\n重启：${opLabel}`,
      '确认自定义同步',
      { type: 'warning', confirmButtonText: '开始同步', cancelButtonText: '取消' }
    )
  } catch {
    return
  }

  const rawFiles = files.value.map((f) => f.raw!).filter(Boolean)
  pushing.value = true
  try {
    saveHistory(remotePath.value.trim())
    await pushOtaCustomFile({
      carIds: selectedIds.value,
      remotePath: remotePath.value.trim(),
      restartOps: ops,
      files: rawFiles
    })
    ElMessage.success('任务已创建，可在任务进度查看')
    emit('jobCreated')
  } catch (e: any) {
    ElMessage.error(e?.message || '同步失败')
  } finally {
    pushing.value = false
  }
}

onMounted(async () => {
  loadHistory()
  cars.value = await listOtaVehicles(false)
})
</script>

<style scoped>
.ota-custom {
  color: rgb(var(--mg-text-tint-rgb));
}
.head h3 {
  margin: 0 0 4px;
  font-size: 16px;
}
.head p {
  margin: 0 0 14px;
  font-size: 12px;
  color: rgba(var(--mg-text-hi-rgb), 0.55);
}
.split {
  display: grid;
  grid-template-columns: 1.35fr 1fr;
  gap: 16px;
}
.left,
.right {
  border: 1px solid rgba(var(--mg-primary-rgb), 0.12);
  border-radius: 12px;
  background: #fff;
  padding: 14px;
  min-width: 0;
}
.step {
  margin-bottom: 18px;
}
.step h4,
.right-head h4 {
  margin: 0 0 8px;
  font-size: 13px;
}
.right-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.row {
  display: flex;
  gap: 8px;
  margin-bottom: 8px;
}
.file-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 150px;
  overflow: auto;
  border: 1px solid rgba(var(--mg-primary-rgb), 0.1);
  border-radius: 8px;
  background: rgba(var(--mg-primary-rgb), 0.03);
}
.file-list li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 6px 10px;
  font-size: 12px;
  border-bottom: 1px solid rgba(var(--mg-primary-rgb), 0.06);
}
.file-list li.empty {
  color: rgba(var(--mg-text-hi-rgb), 0.45);
}
.x {
  border: 0;
  background: transparent;
  cursor: pointer;
  font-size: 16px;
  color: rgba(var(--mg-text-hi-rgb), 0.55);
}
.muted {
  margin: 6px 0 0;
  font-size: 12px;
  color: rgba(var(--mg-text-hi-rgb), 0.5);
}
.warn {
  margin: 0 0 8px;
  font-size: 12px;
  color: #c47f00;
}
.step :deep(.el-checkbox) {
  display: flex;
  margin: 4px 0;
}
.w-full {
  width: 100%;
}
.car-cards {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 380px;
  overflow: auto;
  margin-top: 8px;
}
.car-card {
  display: flex;
  align-items: center;
  gap: 10px;
  text-align: left;
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid rgba(var(--mg-primary-rgb), 0.18);
  background: #fff;
  cursor: pointer;
}
.car-card.on {
  background: rgba(124, 58, 237, 0.08);
  border-color: #7c3aed;
  border-width: 2px;
}
.car-card .tick {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  border-radius: 4px;
  border: 1.5px solid #7c3aed;
  background: #fff;
  box-sizing: border-box;
  position: relative;
}
.car-card .tick.on {
  background: #7c3aed;
  border-color: #7c3aed;
}
.car-card .tick.on::after {
  content: '';
  position: absolute;
  left: 5px;
  top: 2px;
  width: 5px;
  height: 9px;
  border: solid #fff;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.car-card strong {
  display: block;
  font-size: 13px;
}
.car-card .mono {
  font-size: 11px;
  color: rgba(var(--mg-text-hi-rgb), 0.55);
}
.mono {
  font-family: var(--mg-font-mono);
}
.foot {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 14px;
}
@media (max-width: 960px) {
  .split {
    grid-template-columns: 1fr;
  }
}
</style>
