<template>
  <div class="people-view">
    <div class="ms-ppl-toolbar">
      <h1 class="ms-ppl-section-title">People</h1>
      <div class="ms-ppl-search-wrap">
        <span class="ms-ppl-search-icon">🔍</span>
        <input v-model="searchQuery" class="ms-ppl-search-input" placeholder="Search people..." @input="onSearch" />
      </div>
    </div>

    <div v-if="loading" class="loading-state">
      <div class="spinner-lg"></div>
      <p>Loading...</p>
    </div>
    <div v-else-if="confirmedPeople.length === 0 && pendingPeople.length === 0 && skippedPeople.length === 0" class="empty">
      No people found. people.value.length = {{ people.length }}
    </div>
    <template v-else>
      <!-- 已知人物 -->
      <div v-if="confirmedPeople.length" class="ms-ppl-section">
        <div class="ms-ppl-section-toolbar">
          <div class="ms-ppl-section-title">已知人物：</div>
        </div>
        <div class="ms-ppl-face-grid">
          <div v-for="p in confirmedPeople" :key="p.identity_uuid" class="ms-ppl-face-card" @click="selectPerson(p)" @contextmenu.prevent="showContextMenu($event, p)">
            <div class="ms-ppl-face-img-wrap">
              <img v-if="profiles[p.identity_uuid]" :src="profiles[p.identity_uuid]" alt="" @vue:mounted="loadProfile(p.identity_uuid)">
              <svg v-else class="ms-silhouette" @vue:mounted="loadProfile(p.identity_uuid)" viewBox="0 0 120 120" fill="none">
                <circle cx="60" cy="45" r="25" fill="#d1d5db"/>
                <ellipse cx="60" cy="105" rx="40" ry="25" fill="#d1d5db"/>
              </svg>
              <span class="ms-ppl-card-star" :class="{ starred: p.starred }">⭐</span>
            </div>
            <span class="ms-ppl-face-name">{{ p.name }}</span>
          </div>
        </div>
      </div>

      <hr class="ms-ppl-hr">

      <!-- 待定人物 -->
      <div v-if="pendingPeople.length" class="ms-ppl-section">
        <div class="ms-ppl-section-toolbar">
          <div class="ms-ppl-section-title">待定人物：</div>
        </div>
        <div class="ms-ppl-face-grid">
          <div v-for="p in pendingPeople" :key="p.identity_uuid" class="ms-ppl-face-card" @click="selectPerson(p)" @contextmenu.prevent="showContextMenu($event, p)">
            <div class="ms-ppl-face-img-wrap">
              <img v-if="profiles[p.identity_uuid]" :src="profiles[p.identity_uuid]" alt="" @vue:mounted="loadProfile(p.identity_uuid)">
              <svg v-else class="ms-silhouette" @vue:mounted="loadProfile(p.identity_uuid)" viewBox="0 0 120 120" fill="none">
                <circle cx="60" cy="45" r="25" fill="#d1d5db"/>
                <ellipse cx="60" cy="105" rx="40" ry="25" fill="#d1d5db"/>
              </svg>
              <span class="ms-ppl-card-star" :class="{ starred: p.starred }">⭐</span>
            </div>
            <span class="ms-ppl-face-name">{{ p.name }}</span>
          </div>
        </div>
      </div>

      <hr class="ms-ppl-hr">

      <!-- 已略過 -->
      <div v-if="skippedPeople.length" class="ms-ppl-section">
        <div class="ms-ppl-section-toolbar">
          <div class="ms-ppl-section-title skipped-title">
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" style="flex-shrink:0;">
              <circle cx="12" cy="12" r="9" stroke="currentColor" stroke-width="1.8"></circle>
              <path d="M15 9l-6 6M9 9l6 6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"></path>
            </svg>
            已略過：
          </div>
        </div>
        <div class="ms-ppl-face-grid">
          <div v-for="p in skippedPeople" :key="p.identity_uuid" class="ms-ppl-face-card skipped-card" @click="selectPerson(p)" @contextmenu.prevent="showContextMenu($event, p)">
            <div class="ms-ppl-face-img-wrap">
              <img v-if="profiles[p.identity_uuid]" :src="profiles[p.identity_uuid]" alt="" @vue:mounted="loadProfile(p.identity_uuid)">
              <svg v-else class="ms-silhouette" @vue:mounted="loadProfile(p.identity_uuid)" viewBox="0 0 120 120" fill="none">
                <circle cx="60" cy="45" r="25" fill="#d1d5db"/>
                <ellipse cx="60" cy="105" rx="40" ry="25" fill="#d1d5db"/>
              </svg>
              <span class="ms-ppl-card-star" :class="{ starred: p.starred }">⭐</span>
            </div>
            <span class="ms-ppl-face-name">{{ p.name }}</span>
          </div>
        </div>
      </div>

      <hr class="ms-ppl-hr">

      <!-- 待定人臉 -->
      <div v-if="faceCandidates.length" class="ms-ppl-section">
        <div class="ms-ppl-section-toolbar">
          <div class="ms-ppl-section-title">待定人臉：</div>
        </div>
        <div class="ms-ppl-face-grid ms-uface-grid">
          <div v-for="c in faceCandidates.slice(0, 50)" :key="c.id" class="ms-ppl-face-card" @click="showAssignModal(c)">
            <div class="ms-ppl-face-img-wrap">
              <img v-if="candidateThumbs[c.file_uuid]" :src="candidateThumbs[c.file_uuid]" alt="" @vue:mounted="loadCandidateThumb(c.file_uuid)">
              <div v-else class="face-placeholder" @vue:mounted="loadCandidateThumb(c.file_uuid)">{{ Math.round(c.confidence * 100) }}%</div>
            </div>
            <span class="ms-ppl-face-name">{{ c.file_uuid.slice(0, 8) }}... #{{ c.frame_number }}</span>
          </div>
        </div>
      </div>
    </template>

    <div v-if="ctxMenu.show" class="ms-ctx-menu" :style="{ left: ctxMenu.x + 'px', top: ctxMenu.y + 'px', display: 'block' }">
      <button class="ms-ctx-item" @click="ctxAction('star')">{{ ctxMenu.person?.starred ? '☆ 取消重要人物' : '★ 標為重要人物' }}</button>
      <hr class="ms-ctx-menu-divider">
      <button class="ms-ctx-item" @click="ctxAction('rename')">✎ 編輯名稱</button>
      <button class="ms-ctx-item" @click="ctxAction('merge')">⇄ 已有此人物</button>
      <button class="ms-ctx-item ms-ctx-danger" @click="ctxAction('skip')">✕ 略過此人物</button>
    </div>

    <VideoPlayer v-if="playing" :file-uuid="currentVideo.fileUuid" :start-time="currentVideo.startTime" :end-time="currentVideo.endTime" :title="currentVideo.title" @close="playing = false" />
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
import { useRouter } from 'vue-router'
import { invoke } from '@tauri-apps/api/core'

console.log('PeopleView script loaded')

const router = useRouter()
import VideoPlayer from '../components/VideoPlayer.vue'

const people = ref<any[]>([])
const loading = ref(true)
const searchQuery = ref('')
const searchResults = ref<any[]>([])
const isSearching = ref(false)
const selected = ref<any>(null)
const faces = ref<any[]>([])
const traces = ref<any[]>([])
const playing = ref(false)
const currentVideo = ref({ fileUuid: '', startTime: 0, endTime: 0, title: '' })
const profiles = ref<Record<string, string>>({})
const showCandidates = ref(false)
const showMerge = ref(false)
const mergeTarget = ref('')
const candidates = ref<any[]>([])
const candidateThumbs = ref<Record<string, string>>({})
const faceCandidates = ref<any[]>([])
const ctxMenu = ref({ show: false, x: 0, y: 0, person: null as any })

const confirmedPeople = computed(() => {
  const base = isSearching.value && searchResults.value.length ? searchResults.value : people.value
  const filtered = searchQuery.value ? base.filter((p: any) => p.name.toLowerCase().includes(searchQuery.value.toLowerCase())) : base
  return filtered.filter((p: any) => p.status === 'confirmed')
})

const pendingPeople = computed(() => {
  const base = isSearching.value && searchResults.value.length ? searchResults.value : people.value
  const filtered = searchQuery.value ? base.filter((p: any) => p.name.toLowerCase().includes(searchQuery.value.toLowerCase())) : base
  return filtered.filter((p: any) => p.status === 'pending')
})

const skippedPeople = computed(() => {
  const base = isSearching.value && searchResults.value.length ? searchResults.value : people.value
  const filtered = searchQuery.value ? base.filter((p: any) => p.name.toLowerCase().includes(searchQuery.value.toLowerCase())) : base
  return filtered.filter((p: any) => p.status === 'skipped')
})

onMounted(async () => {
  try {
    console.log('PeopleView: calling getPeople...')
    const result: any = await invoke('getPeople', { page: 1, perPage: 1000 })
    console.log('PeopleView: getPeople result:', Array.isArray(result) ? result.length : typeof result)
    people.value = Array.isArray(result) ? result : []
    console.log('PeopleView: people.value length:', people.value.length)
    if (people.value.length > 0) {
      console.log('PeopleView: first person:', JSON.stringify(people.value[0]))
    }
  } catch (e) {
    console.error('Failed to load people:', e)
  } finally {
    loading.value = false
  }
  try {
    const fc: any = await invoke('getFaceCandidates', { page: 1, perPage: 100 })
    faceCandidates.value = Array.isArray(fc) ? fc : []
  } catch (e) {
    console.error('Failed to load face candidates:', e)
  }
  document.addEventListener('click', closeCtxMenu)
})

onUnmounted(() => {
  document.removeEventListener('click', closeCtxMenu)
})

let searchTimer: any
function onSearch() {
  clearTimeout(searchTimer)
  searchTimer = setTimeout(async () => {
    if (!searchQuery.value.trim()) { isSearching.value = false; return }
    try {
      const results: any = await invoke('searchIdentities', { query: searchQuery.value, limit: 50 })
      searchResults.value = Array.isArray(results) ? results : []
      isSearching.value = true
    } catch (e) { console.error('Search failed:', e) }
  }, 300)
}

async function loadProfile(uuid: string) {
  if (profiles.value[uuid]) return
  try { profiles.value[uuid] = await invoke('getIdentityProfile', { uuid }) } catch {}
}

async function loadCandidateThumb(uuid: string) {
  if (!uuid || candidateThumbs.value[uuid]) return
  try { candidateThumbs.value[uuid] = await invoke('getThumbnail', { uuid, frame: 30 }) } catch {}
}

function selectPerson(p: any) {
  router.push({ name: 'PersonDetail', params: { uuid: p.identity_uuid } })
}

async function toggleStar() {
  if (!selected.value) return
  selected.value.starred = !selected.value.starred
  const idx = people.value.findIndex((x: any) => x.identity_uuid === selected.value.identity_uuid)
  if (idx >= 0) people.value[idx].starred = selected.value.starred
}

async function confirmDelete() {
  if (!confirm(`Delete "${selected.value.name}"?`)) return
  try {
    await invoke('deleteIdentity', { uuid: selected.value.identity_uuid })
    people.value = people.value.filter((p: any) => p.identity_uuid !== selected.value.identity_uuid)
    selected.value = null
  } catch (e) { console.error('Failed to delete:', e) }
}

async function loadCandidates() {
  try {
    const result: any = await invoke('getFaceCandidates', { page: 1, perPage: 50 })
    candidates.value = Array.isArray(result) ? result : []
  } catch (e) { console.error('Failed to load candidates:', e) }
}

async function bindCandidate(c: any) {
  if (!selected.value) return
  try {
    await invoke('bindFace', { uuid: selected.value.identity_uuid, faceId: String(c.id), fileUuid: c.file_uuid })
    showCandidates.value = false
    if (selected.value) selectPerson(selected.value)
  } catch (e) { console.error('Bind failed:', e) }
}

async function confirmMerge() {
  if (!selected.value || !mergeTarget.value) return
  try {
    await invoke('mergeIdentities', { uuid: selected.value.identity_uuid, intoUuid: mergeTarget.value })
    showMerge.value = false
    people.value = people.value.filter((p: any) => p.identity_uuid !== selected.value.identity_uuid)
    selected.value = null
  } catch (e) { console.error('Merge failed:', e) }
}

function showAssignModal(c: any) {
  alert(`Assign face ${c.id} to existing person - not yet implemented`)
}

function formatTime(sec: number): string {
  const m = Math.floor(sec / 60); const s = Math.floor(sec % 60)
  return `${m}:${s.toString().padStart(2, '0')}`
}

function playTrace(t: any) {
  currentVideo.value = { fileUuid: t.file_uuid, startTime: t.first_sec, endTime: t.last_sec, title: `${selected.value?.name} - ${formatTime(t.first_sec)}-${formatTime(t.last_sec)}` }
  playing.value = true
}

function showContextMenu(e: MouseEvent, p: any) {
  e.preventDefault()
  e.stopPropagation()
  ctxMenu.value = { show: true, x: e.clientX, y: e.clientY, person: p }
}

function ctxAction(action: string) {
  const p = ctxMenu.value.person
  if (!p) return
  ctxMenu.value.show = false
  if (action === 'star') {
    p.starred = !p.starred
    const idx = people.value.findIndex((x: any) => x.identity_uuid === p.identity_uuid)
    if (idx >= 0) people.value[idx].starred = p.starred
  } else if (action === 'skip') {
    invoke('updateIdentityStatus', { uuid: p.identity_uuid, status: 'skipped' }).then(() => {
      const idx = people.value.findIndex((x: any) => x.identity_uuid === p.identity_uuid)
      if (idx >= 0) people.value[idx].status = 'skipped'
    }).catch(e => console.error('Skip failed:', e))
  } else if (action === 'confirm') {
    invoke('updateIdentityStatus', { uuid: p.identity_uuid, status: 'confirmed' }).then(() => {
      const idx = people.value.findIndex((x: any) => x.identity_uuid === p.identity_uuid)
      if (idx >= 0) people.value[idx].status = 'confirmed'
    }).catch(e => console.error('Confirm failed:', e))
  } else if (action === 'rename' || action === 'merge') {
    selectPerson(p)
  }
}

function closeCtxMenu(e?: MouseEvent) {
  if (e && e.target instanceof Element && e.target.closest('.ms-ctx-menu')) return
  ctxMenu.value.show = false
}

watch(showCandidates, (v) => { if (v) loadCandidates() })
</script>

<style scoped>
.ms-ppl-card-star { position: absolute; top: 6px; left: 6px; font-size: 16px; color: #f59e0b; text-shadow: 0 1px 3px rgba(0,0,0,.25); display: none; }
.ms-ppl-face-card.starred .ms-ppl-card-star, .ms-ppl-card-star.starred { display: block; }
.people-view { max-width: 1200px; }
h1 { margin: 0; }
.loading-state, .empty { text-align: center; padding: 60px 0; color: #5f6368; }
.spinner-lg { width: 24px; height: 24px; border: 3px solid #e8eaed; border-top-color: #202124; border-radius: 50%; animation: spin 0.7s linear infinite; margin: 0 auto 12px; }
@keyframes spin { to { transform: rotate(360deg); } }
.ms-ppl-detail-view { display: none; }
.ms-ppl-detail-view.show { display: block; }
.ms-ppl-detail-header { display: flex; align-items: flex-start; gap: 22px; margin-bottom: 28px; position: relative; margin-top: 20px; }
.ms-ppl-detail-avatar { width: 120px; height: 120px; border-radius: 20px; background: #e0e0e0; flex-shrink: 0; overflow: hidden; }
.ms-ppl-detail-avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 20px; }
.ms-ppl-detail-name-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.ms-ppl-star-btn { font-size: 20px; background: transparent; border: none; cursor: pointer; outline: none; line-height: 1; color: #d1d5db; transition: color .15s; padding: 0; flex-shrink: 0; }
.ms-ppl-star-btn.starred { color: #f59e0b; }
.ms-ppl-detail-aliases { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 12px; }
.ms-ppl-alias-chip { display: inline-flex; align-items: center; background: #f0f0f0; border-radius: 999px; padding: 3px 10px; font-size: 11.5px; color: #5f6368; }
.ms-ppl-edit-fields { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }
.ms-ppl-edit-field-row { display: flex; align-items: center; gap: 12px; }
.ms-ppl-edit-label { font-family: 'DM Sans', 'Noto Sans TC', sans-serif; font-size: 12px; font-weight: 700; color: #202124; letter-spacing: .03em; min-width: 30px; text-align: right; flex-shrink: 0; padding-top: 2px; }
.ms-ppl-view-box { font-family: 'DM Sans', 'Noto Sans TC', sans-serif; font-size: 13.5px; color: #202124; min-height: 24px; display: flex; align-items: center; word-break: break-word; padding: 2px 0; }
.ms-ppl-view-name-box { font-size: 20px; font-weight: 700; flex: 1; min-width: 0; cursor: pointer; }
.ms-ppl-view-name-box:hover { text-decoration: underline; }
.ms-ppl-view-field-box { flex: 1; min-width: 0; color: #3c4043; }
.ms-ppl-strip-wrap { display: flex; align-items: center; gap: 10px; margin-bottom: 28px; }
.ms-ppl-strip-add-btn { width: 52px; height: 52px; border-radius: 12px; border: 1.5px dashed #bdc1c6; background: #fff; font-size: 20px; color: #bdc1c6; display: grid; place-items: center; cursor: pointer; outline: none; flex-shrink: 0; transition: border-color .15s, color .15s; }
.ms-ppl-strip-add-btn:hover { border-color: #202124; color: #202124; }
.ms-ppl-face-strip { display: flex; gap: 10px; overflow-x: auto; padding-bottom: 4px; scrollbar-width: thin; flex: 1; }
.ms-ppl-strip-face { position: relative; flex-shrink: 0; cursor: pointer; }
.ms-ppl-strip-face-img { width: 52px; height: 52px; border-radius: 12px; border: 2px solid transparent; background: #e8eaed; overflow: hidden; transition: border-color .15s; }
.ms-ppl-strip-face:hover .ms-ppl-strip-face-img { border-color: #202124; }
.ms-uface-grid .ms-ppl-face-card { width: 120px; }
.ms-uface-grid .ms-ppl-face-img-wrap { width: 120px; height: 120px; border-radius: 20px; }
.ms-ppl-media-label { font-size: 13px; color: #5f6368; margin-bottom: 16px; }
.close-btn { position: absolute; top: 16px; right: 16px; }
.detail-header { display: flex; align-items: center; gap: 20px; margin-bottom: 24px; }
.detail-avatar { width: 80px; height: 80px; border-radius: 16px; flex-shrink: 0; }
.detail-info { flex: 1; }
.name-row { display: flex; align-items: center; gap: 8px; }
.name-row h2 { margin: 0; cursor: pointer; font-size: 1.25rem; }
.name-row h2:hover { text-decoration: underline; }
.name-input { font-size: 1.25rem; font-weight: 700; border: 1.5px solid #d1d5db; border-radius: 8px; padding: 4px 8px; width: 200px; outline: none; }
.edit-btn { width: 28px; height: 28px; }
.uuid { color: #9aa0a6; font-size: 0.8rem; font-family: monospace; margin: 4px 0 8px; }
.detail-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.tabs { display: flex; gap: 8px; margin-bottom: 16px; }
.tab { padding: 8px 16px; border: 1.5px solid #d1d5db; background: #fff; border-radius: 10px; cursor: pointer; font-size: 0.85rem; }
.tab.active { background: #202124; color: #fff; border-color: #202124; }
.face-strip { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 8px; }
.face-thumb { width: 52px; height: 52px; border-radius: 8px; background: #e8eaed; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.face-placeholder { font-size: 0.6rem; color: #5f6368; }
.ms-ppl-media-item { cursor: pointer; }
.ms-ppl-media-thumb { position: relative; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); display: flex; align-items: center; justify-content: center; }
.thumb-play { color: #fff; font-size: 1.2rem; opacity: 0.8; }
.merge-input { width: 100%; padding: 10px 14px; border: 1.5px solid #d1d5db; border-radius: 10px; margin-bottom: 16px; font-size: 0.9rem; outline: none; }
.merge-input:focus { border-color: #202124; }
.ms-modal-actions { display: flex; justify-content: flex-end; }
h2 { margin: 0 0 16px; font-size: 1rem; }
.ms-merge-grid { display: flex; flex-wrap: wrap; gap: 16px; max-height: 50vh; overflow-y: auto; }
.ms-ctx-menu { position: fixed; z-index: 99999; background: #fff; border-radius: 12px; box-shadow: 0 8px 30px rgba(0,0,0,.15); padding: 6px; min-width: 160px; font-size: 13px; color: #222; }
.ms-ctx-item { display: flex; align-items: center; gap: 8px; padding: 8px 12px; cursor: pointer; border-radius: 8px; border: none; background: transparent; width: 100%; text-align: left; font-size: 13px; color: #222; font-family: inherit; }
.ms-ctx-item:hover { background: #f3f4f6; }
.ms-ctx-item.ms-ctx-danger { color: #d93025; }
.ms-ctx-item.ms-ctx-danger:hover { background: #fce8e6; }
.ms-ctx-menu-divider { height: 1px; background: #eee; margin: 4px 8px; }
.ms-ppl-section { margin-bottom: 8px; }
.ms-ppl-section-toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.ms-ppl-section-title { font-family: 'DM Sans', 'Noto Sans TC', sans-serif; font-size: 14px; font-weight: 600; color: #202124; margin: 0; display: flex; align-items: center; gap: 6px; }
.ms-ppl-hr { border: none; border-top: 1.5px solid #e8eaed; margin: 24px 0; }
.skipped-title { color: #9aa0a6; }
.skipped-card .ms-ppl-face-img-wrap { filter: grayscale(0.6); opacity: 0.7; }
.skipped-card .ms-ppl-face-name { color: #bdc1c6; }
</style>
