<template>
  <q-page class="q-pa-md ops-page">
    <div class="row items-center q-mb-sm">
      <div class="text-h6 text-weight-bold">Conformité des adresses</div>
      <q-space />
      <q-btn flat dense no-caps size="sm" icon="my_location" label="≈ Centre CP/ville (reste)" color="blue-grey" :loading="areaSaving" @click="applyArea"><q-tooltip>Placer les adresses non matchées restantes au centre de leur code postal (sinon ville) — dernier repli</q-tooltip></q-btn>
      <q-btn flat dense round icon="refresh" @click="reload" :loading="loading"><q-tooltip>Rafraîchir</q-tooltip></q-btn>
    </div>
    <div class="text-caption text-grey-7 q-mb-md">
      Source de vérité : adresse canonique Adresses Québec (RQA locale) liée à chaque emplacement de service.
      Résous ici les adresses non conformes — la décision est persistée (plus de re-recherche).
    </div>

    <!-- Statistiques -->
    <div class="row q-col-gutter-sm q-mb-md">
      <div v-for="s in statusCards" :key="s.key" class="col-6 col-sm-3">
        <q-card flat bordered class="text-center q-pa-sm">
          <div class="text-h6 text-weight-bold" :class="s.cls">{{ s.n }}</div>
          <div class="text-caption text-grey-7">{{ s.label }}</div>
        </q-card>
      </div>
    </div>

    <!-- Campings : géoloc de remplacement fixe (le lot pointe vers l'adresse principale du camping) -->
    <q-expansion-item icon="cottage" label="Campings — géoloc de remplacement fixe" class="q-mb-sm" header-class="text-weight-medium">
      <q-card flat bordered class="q-pa-sm">
        <div class="text-caption text-grey-7 q-mb-sm">Pour un lot de camping, l'adresse est souvent la <b>résidence</b> du client, pas le terrain. On force la position du <b>camping</b> (le tech y navigue, puis trouve le terrain). Ajoute un camping → appliqué à tous ses lots (match sur la ville).</div>
        <q-markup-table flat dense wrap-cells class="q-mb-sm">
          <thead><tr><th class="text-left">Camping (mot-clé)</th><th class="text-left">Adresse principale</th><th>GPS</th><th>Lots</th></tr></thead>
          <tbody>
            <tr v-for="c in campings" :key="c.id">
              <td class="text-left">{{ c.name }} <span class="text-grey-5">({{ c.keyword }})</span></td>
              <td class="text-left">{{ c.address }}</td>
              <td class="text-center"><a :href="'https://www.google.com/maps?q=' + c.latitude + ',' + c.longitude" target="_blank">{{ (+c.latitude).toFixed(4) }}, {{ (+c.longitude).toFixed(4) }}</a></td>
              <td class="text-center">{{ campingLots[c.name] || 0 }}</td>
            </tr>
          </tbody>
        </q-markup-table>
        <div class="row q-col-gutter-xs items-center">
          <q-input class="col-3" dense outlined v-model="newCamp.name" label="Nom" />
          <q-input class="col-2" dense outlined v-model="newCamp.keyword" label="Mot-clé (ville)" />
          <q-input class="col-3" dense outlined v-model="newCamp.address" label="Adresse principale" />
          <q-input class="col" dense outlined v-model.number="newCamp.latitude" label="Lat" />
          <q-input class="col" dense outlined v-model.number="newCamp.longitude" label="Lon" />
          <q-btn dense unelevated color="primary" icon="add" :disable="!campValid" :loading="campSaving" @click="addCamping"><q-tooltip>Ajouter + appliquer</q-tooltip></q-btn>
          <q-btn dense flat color="indigo" icon="sync" :loading="campSaving" @click="applyCampings"><q-tooltip>Réappliquer tous les campings</q-tooltip></q-btn>
        </div>
      </q-card>
    </q-expansion-item>

    <!-- Filtres par type -->
    <div class="row items-center q-gutter-sm q-mb-sm">
      <q-chip v-for="t in typeChips" :key="t.key" clickable :selected="filter.type === t.key"
              :color="filter.type === t.key ? 'primary' : 'grey-3'" :text-color="filter.type === t.key ? 'white' : 'grey-9'"
              @click="setType(t.key)">{{ t.label }} <q-badge v-if="t.n != null" color="white" text-color="primary" class="q-ml-xs">{{ t.n }}</q-badge></q-chip>
      <q-space />
      <q-input dense outlined v-model="filter.q" debounce="400" placeholder="Rechercher adresse / ville / client" style="min-width:260px" @update:model-value="reload">
        <template #prepend><q-icon name="search" /></template>
      </q-input>
    </div>

    <!-- Liste -->
    <DataTable flat bordered :rows="rows" :columns="columns" row-key="name" :loading="loading"
             v-model:pagination="pagination" :rows-per-page-options="[25,50,100]" @request="onRequest" :rows-number="total" binary-state-sort>
      <template #body-cell-original="props">
        <q-td :props="props">
          <div class="text-weight-medium">{{ props.row.address_line }}</div>
          <div class="text-caption text-grey-7">{{ props.row.city }} {{ props.row.postal_code }} · <span class="text-grey-6">{{ props.row.customer }}</span></div>
        </q-td>
      </template>
      <template #body-cell-type="props">
        <q-td :props="props"><q-chip dense square :color="typeColor(props.row.type)" text-color="white" class="text-caption">{{ typeLabel(props.row.type) }}</q-chip></q-td>
      </template>
      <template #body-cell-proposition="props">
        <q-td :props="props">
          <div v-if="props.row.linked_address">{{ props.row.linked_address }}
            <q-badge v-if="hasCoord(props.row)" color="green-6" class="q-ml-xs">GPS</q-badge>
          </div>
          <span v-else class="text-grey-5">— (aucune ; utiliser Corriger)</span>
        </q-td>
      </template>
      <template #body-cell-actions="props">
        <q-td :props="props" class="text-right" style="white-space:nowrap">
          <q-btn v-if="props.row.linked_address" dense flat round color="positive" icon="check" @click="resolve(props.row,'approve')"><q-tooltip>Approuver la proposition</q-tooltip></q-btn>
          <q-btn dense flat round color="primary" icon="edit_location_alt" @click="openCorrect(props.row)"><q-tooltip>Corriger (chercher la bonne adresse AQ)</q-tooltip></q-btn>
          <q-btn dense flat round color="teal" icon="my_location" @click="openGps(props.row)"><q-tooltip>Saisir/relever le GPS</q-tooltip></q-btn>
          <q-btn dense flat round color="grey-7" icon="map" type="a" :href="mapUrl(props.row)" target="_blank"><q-tooltip>Voir sur map.targointernet.com (géoloc des unités)</q-tooltip></q-btn>
          <q-btn dense flat round color="negative" icon="block" @click="resolve(props.row,'reject')"><q-tooltip>Rejeter (pas d'adresse civique)</q-tooltip></q-btn>
        </q-td>
      </template>
      <template #no-data><div class="full-width text-center q-pa-md text-grey-6">Aucune adresse à traiter dans ce filtre 🎉</div></template>
    </DataTable>

    <!-- Dialogue Corriger : recherche AQ locale -->
    <q-dialog v-model="correct.open">
      <q-card style="min-width:420px;max-width:560px">
        <q-card-section class="q-pb-none">
          <div class="text-subtitle1 text-weight-bold">Corriger l'adresse</div>
          <div class="text-caption text-grey-7">Originale : {{ correct.row && correct.row.address_line }}, {{ correct.row && correct.row.city }}</div>
        </q-card-section>
        <q-card-section>
          <q-input dense outlined autofocus v-model="correct.q" debounce="350" placeholder="Chercher l'adresse Adresses Québec…" @update:model-value="searchCandidates" :loading="correct.loading">
            <template #prepend><q-icon name="search" /></template>
          </q-input>
          <q-list separator class="q-mt-sm" style="max-height:300px;overflow:auto">
            <q-item v-for="c in correct.candidates" :key="c.id" clickable v-ripple @click="applyCorrect(c)">
              <q-item-section>
                <q-item-label>{{ c.address_full }}</q-item-label>
                <q-item-label caption>{{ Math.round((c.similarity_score||0)*100) }}% · {{ c.latitude && (+c.latitude).toFixed(5) }}, {{ c.longitude && (+c.longitude).toFixed(5) }}</q-item-label>
              </q-item-section>
              <q-item-section side><q-badge v-if="c.fiber_available" color="green-6">FIBRE</q-badge></q-item-section>
            </q-item>
            <q-item v-if="correct.q && !correct.loading && !correct.candidates.length"><q-item-section class="text-grey-6">Aucun résultat — affine la recherche.</q-item-section></q-item>
          </q-list>
        </q-card-section>
        <q-card-actions align="right"><q-btn flat label="Fermer" v-close-popup /></q-card-actions>
      </q-card>
    </q-dialog>

    <!-- Dialogue GPS manuel -->
    <q-dialog v-model="gps.open">
      <q-card style="min-width:360px">
        <q-card-section class="q-pb-none">
          <div class="text-subtitle1 text-weight-bold">Position GPS</div>
          <div class="text-caption text-grey-7">{{ gps.row && gps.row.address_line }}, {{ gps.row && gps.row.city }}</div>
        </q-card-section>
        <q-card-section class="q-gutter-sm">
          <q-banner dense class="bg-grey-2 text-caption">Astuce : relève la coordonnée de l'unité sur
            <a :href="gps.row && mapUrl(gps.row)" target="_blank">map.targointernet.com</a> puis colle « lat, long » ci-dessous.</q-banner>
          <q-input dense outlined v-model="gps.paste" label="Coller « lat, long »" @update:model-value="parsePaste" />
          <div class="row q-col-gutter-sm">
            <q-input class="col" dense outlined type="number" step="any" v-model.number="gps.lat" label="Latitude" />
            <q-input class="col" dense outlined type="number" step="any" v-model.number="gps.lon" label="Longitude" />
          </div>
        </q-card-section>
        <q-card-actions align="right">
          <q-btn flat label="Annuler" v-close-popup />
          <q-btn unelevated color="teal" label="Enregistrer" :disable="!gpsValid" @click="saveGps" />
        </q-card-actions>
      </q-card>
    </q-dialog>
  </q-page>
</template>

<script setup>
import { ref, reactive, computed, onMounted } from 'vue'
import DataTable from 'src/components/shared/DataTable.vue'
import { useQuasar } from 'quasar'
import * as addr from 'src/api/address'

const $q = useQuasar()
const loading = ref(false)
const rows = ref([])
const total = ref(0)
const stats = reactive({ by_status: [], by_type: [] })
const filter = reactive({ type: '', q: '' })
const pagination = ref({ page: 1, rowsPerPage: 50, rowsNumber: 0 })

const columns = [
  { name: 'original', label: 'Adresse (originale)', field: 'address_line', align: 'left' },
  { name: 'type', label: 'Type', field: 'type', align: 'left' },
  { name: 'proposition', label: 'Proposition AQ (canonique)', field: 'linked_address', align: 'left' },
  { name: 'status', label: 'Statut', field: 'status', align: 'left' },
  { name: 'actions', label: '', field: 'actions', align: 'right' },
]
const TYPES = { camping: { label: 'Camping', color: 'deep-orange-5' }, civique: { label: 'Civique à corriger', color: 'blue-6' }, review: { label: 'À confirmer', color: 'amber-7' }, non_adresse: { label: 'Non-adresse', color: 'grey-6' } }
const typeLabel = (t) => (TYPES[t] || {}).label || t
const typeColor = (t) => (TYPES[t] || {}).color || 'grey-6'
const hasCoord = (r) => r.latitude != null && Math.abs(parseFloat(r.latitude)) > 0.0001

const statN = (k) => { const x = stats.by_status.find(s => s.s === k); return x ? Number(x.n) : 0 }
const typeN = (k) => { const x = stats.by_type.find(s => s.t === k); return x ? Number(x.n) : 0 }
const statusCards = computed(() => [
  { key: 'validated', label: 'Validées (conformes)', n: statN('validated'), cls: 'text-positive' },
  { key: 'review', label: 'À confirmer', n: statN('review'), cls: 'text-warning' },
  { key: 'area', label: '≈ Secteur (CP/ville)', n: statN('area'), cls: 'text-blue-grey-7' },
  { key: 'unmatched', label: 'Non matchées', n: statN('unmatched'), cls: 'text-deep-orange' },
  { key: 'no_address', label: 'Rejetées (sans adresse)', n: statN('no_address'), cls: 'text-grey-7' },
])
const typeChips = computed(() => [
  { key: '', label: 'Tout', n: statN('review') + statN('unmatched') },
  { key: 'civique', label: 'Civique à corriger', n: typeN('civique') },
  { key: 'camping', label: 'Camping', n: typeN('camping') },
  { key: 'review', label: 'À confirmer', n: typeN('review') },
  { key: 'non_adresse', label: 'Non-adresse', n: typeN('non_adresse') },
])

function mapUrl (row) {
  const q = encodeURIComponent([row.address_line, row.city].filter(Boolean).join(', '))
  return 'https://map.targointernet.com/infrastructure/map.php?q=' + q
}

async function loadStats () { try { const r = await addr.conformityStats(); stats.by_status = r.by_status || []; stats.by_type = r.by_type || [] } catch (e) {} }
async function loadList () {
  loading.value = true
  try {
    const offset = (pagination.value.page - 1) * pagination.value.rowsPerPage
    const r = await addr.conformityList({ type: filter.type, q: filter.q, limit: pagination.value.rowsPerPage, offset })
    rows.value = r.rows || []; total.value = r.total || 0; pagination.value.rowsNumber = r.total || 0
  } catch (e) { $q.notify({ type: 'negative', message: 'Chargement échoué: ' + e.message }) } finally { loading.value = false }
}
function onRequest (props) { pagination.value.page = props.pagination.page; pagination.value.rowsPerPage = props.pagination.rowsPerPage; loadList() }
function setType (t) { filter.type = t; pagination.value.page = 1; loadList() }
function reload () { pagination.value.page = 1; loadStats(); loadList() }

async function resolve (row, action, extra) {
  try {
    await addr.conformityResolve({ name: row.name, action, ...(extra || {}) })
    rows.value = rows.value.filter(r => r.name !== row.name); total.value = Math.max(0, total.value - 1)
    loadStats()
    $q.notify({ type: 'positive', message: action === 'reject' ? 'Marquée sans adresse' : 'Adresse validée ✓', timeout: 1200 })
  } catch (e) { $q.notify({ type: 'negative', message: 'Échec: ' + e.message }) }
}

// ── Corriger ──
const correct = reactive({ open: false, row: null, q: '', candidates: [], loading: false })
function openCorrect (row) { correct.row = row; correct.q = [row.address_line, row.city].filter(Boolean).join(' '); correct.candidates = []; correct.open = true; searchCandidates() }
async function searchCandidates () {
  if (!correct.q || correct.q.length < 3) { correct.candidates = []; return }
  correct.loading = true
  try { const r = await addr.conformityCandidates(correct.q); correct.candidates = r.results || [] } catch (e) {} finally { correct.loading = false }
}
function applyCorrect (c) {
  const row = correct.row
  resolve(row, 'correct', { aq_address_id: String(c.id), linked_address: c.address_full, latitude: c.latitude, longitude: c.longitude })
  correct.open = false
}

// ── GPS manuel ──
const gps = reactive({ open: false, row: null, lat: null, lon: null, paste: '' })
const gpsValid = computed(() => isFinite(gps.lat) && isFinite(gps.lon) && Math.abs(gps.lat) > 0.0001 && Math.abs(gps.lon) > 0.0001)
function openGps (row) { gps.row = row; gps.lat = row.latitude ? +row.latitude : null; gps.lon = row.longitude ? +row.longitude : null; gps.paste = ''; gps.open = true }
function parsePaste () { const m = String(gps.paste).match(/(-?\d+\.\d+)[ ,]+(-?\d+\.\d+)/); if (m) { gps.lat = +m[1]; gps.lon = +m[2] } }
function saveGps () { if (!gpsValid.value) return; resolve(gps.row, 'gps', { latitude: gps.lat, longitude: gps.lon }); gps.open = false }

// ── Campings (géoloc de remplacement fixe) ──
const campings = ref([])
const campingLots = ref({})
const campSaving = ref(false)
const newCamp = reactive({ name: '', keyword: '', address: '', latitude: null, longitude: null })
const campValid = computed(() => !!newCamp.name && !!newCamp.keyword && isFinite(newCamp.latitude) && isFinite(newCamp.longitude))
async function loadCampings () { try { const r = await addr.campingsList(); campings.value = r.campings || []; campingLots.value = r.lots || {} } catch (e) {} }
function campApplied (r) { return (r.applied || []).reduce((s, x) => s + (x.n || 0), 0) }
async function addCamping () {
  if (!campValid.value) return
  campSaving.value = true
  try {
    const r = await addr.campingsUpsert({ name: newCamp.name, keyword: newCamp.keyword, address: newCamp.address, latitude: newCamp.latitude, longitude: newCamp.longitude })
    $q.notify({ type: 'positive', message: `Camping enregistré + appliqué (${campApplied(r)} lots)`, timeout: 2200 })
    Object.assign(newCamp, { name: '', keyword: '', address: '', latitude: null, longitude: null })
    loadCampings(); reload()
  } catch (e) { $q.notify({ type: 'negative', message: 'Échec: ' + e.message }) } finally { campSaving.value = false }
}
async function applyCampings () {
  campSaving.value = true
  try { const r = await addr.campingsApply(); $q.notify({ type: 'positive', message: `Appliqué : ${campApplied(r)} lots`, timeout: 2200 }); loadCampings(); reload() }
  catch (e) { $q.notify({ type: 'negative', message: e.message }) } finally { campSaving.value = false }
}

// ── Dernier repli : centre du code postal / ville pour les unmatched restants ──
const areaSaving = ref(false)
async function applyArea () {
  areaSaving.value = true
  try {
    const r = await addr.conformityApplyArea()
    $q.notify({ type: 'positive', message: `${r.total || 0} placés au secteur (${r.by_postal || 0} par code postal, ${r.by_city || 0} par ville)`, timeout: 2600 })
    reload()
  } catch (e) { $q.notify({ type: 'negative', message: 'Échec: ' + e.message }) } finally { areaSaving.value = false }
}

onMounted(() => { loadStats(); loadList(); loadCampings() })
</script>
