// ── ERPNext Dispatch resource calls ─────────────────────────────────────────
// All ERPNext fetch() calls live here.
// Swap BASE_URL in config/erpnext.js to change the target server.
// ─────────────────────────────────────────────────────────────────────────────
import { BASE_URL } from 'src/config/erpnext'
import { HUB_URL } from 'src/config/hub'
import { authFetch } from './auth'

// Créneaux disponibles (hub : gap-finding + tampon de route + classement) pour un nouveau job à une adresse.
// payload = { duration_h, latitude, longitude, after_date?, limit? } → [{ tech_id, tech_name, date, start_time, end_time, travel_min, distance_km, reasons[] }]
export async function suggestSlots (payload) {
  const res = await fetch(`${HUB_URL}/dispatch/suggest-slots`, {
    method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload || {}),
  })
  if (!res.ok) throw new Error('suggest-slots ' + res.status)
  const data = await res.json()
  return data.slots || data || []
}

// Occupation par ressource (tech) sur l'horizon, filtrée compétence → bandes verticales « Trouver un créneau ».
// payload = { after_date?, days?, skill? } → { start, days, dates[], techs: [{ tech_id, tech_name, occupancy, shift_h, busy_h, free_h, days:[{date,dow,off,reason,occupancy,shift_start,shift_end,shift_h,busy_h,free_h,jobs,blocks:[{top,height,start,end,reserved}]}] }] }
export async function techOccupancy (payload) {
  const res = await fetch(`${HUB_URL}/dispatch/occupancy`, {
    method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload || {}),
  })
  if (!res.ok) throw new Error('occupancy ' + res.status)
  return res.json()
}

async function apiGet (path) {
  const res = await authFetch(BASE_URL + path)
  const data = await res.json()
  if (data.exc) {
    console.error('[apiGet] ERPNext error:', path.slice(0, 120), data.exc.slice ? data.exc.slice(0, 200) : data.exc)
    throw new Error(data.exc)
  }
  return data
}

async function apiPut (doctype, name, body) {
  const res = await authFetch(
    `${BASE_URL}/api/resource/${encodeURIComponent(doctype)}/${encodeURIComponent(name)}`,
    {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(body),
    },
  )
  const text = await res.text()
  if (!res.ok) console.error(`[API] PUT ${doctype}/${name} failed:`, res.status, text)
  let data
  try { data = JSON.parse(text) } catch { throw new Error(`PUT ${doctype}/${name}: invalid JSON — ${text.slice(0, 200)}`) }
  if (data.exc) throw new Error(data.exc)
  return data
}

// Single-call fetch with all fields + child tables
async function listDocs (doctype, fields = '["*"]', filters = null, limit = 200) {
  let url = `/api/resource/${encodeURIComponent(doctype)}?fields=${fields}&limit_page_length=${limit}`
  if (filters) url += '&filters=' + encodeURIComponent(JSON.stringify(filters))
  const data = await apiGet(url)
  return data.data || []
}

// Fetch single doc (needed for child tables not returned by list)
async function fetchDoc (doctype, name) {
  return (await apiGet(`/api/resource/${encodeURIComponent(doctype)}/${encodeURIComponent(name)}`)).data
}

export async function fetchTechnicians () {
  // Fast: single list call (no child tables = no tags)
  const techs = await listDocs('Dispatch Technician', '["*"]', null, 100)
  return techs  // tags loaded lazily via loadTechTags()
}

// Background: fetch individual docs to get child tables (tags)
export async function loadTechTags (techNames) {
  const results = await Promise.all(techNames.map(n => fetchDoc('Dispatch Technician', n).catch(() => null)))
  return results.filter(Boolean)
}

// Fast: single list call, no child tables (assistants/tags come empty)
export async function fetchJobsFast (filters = null) {
  return listDocs('Dispatch Job', '["*"]', filters, 500)
}

// Full: adds child tables (assistants, tags) — use only for jobs that need them
export async function fetchJobFull (name) {
  return fetchDoc('Dispatch Job', name)
}

// Legacy: fetches all with child tables (slow — avoid)
export async function fetchJobs (filters = null) {
  return fetchJobsFast(filters)
}

export async function updateJob (name, payload) {
  return apiPut('Dispatch Job', name, payload)
}

// Complète un job VIA le chaînage canonique du hub (déblocage dépendances + activation abonnement
// + facture prorata BROUILLON en bout de chaîne, gatée PRORATION_WRITE / BILLING_APPROVAL_GATE).
// Réutilisé par la complétion d'un ticket de VENTE/installation. Renvoie { activated, invoices, pending_approval? }.
export async function setJobStatusChain (job, status) {
  const res = await fetch(`${HUB_URL}/dispatch/job-status`, {
    method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ job, status }),
  })
  const data = await res.json().catch(() => ({}))
  if (!res.ok) throw new Error(data.error || ('Dispatch API ' + res.status))
  return data
}

// Aperçu d'activation (LECTURE SEULE) : abonnements « En attente » du client+lieu + facture prorata consolidée
// (TOUS services : internet+tv+téléphone en 1 facture). Sert à SAVOIR si une complétion activerait qqch + montre le total.
export async function activationPreview ({ customer, service_location }) {
  const res = await fetch(`${HUB_URL}/billing/activation-preview`, {
    method: 'POST', headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ customer, service_location, statuses: ['En attente'] }),
  })
  const data = await res.json().catch(() => ({}))
  if (!res.ok) throw new Error(data.error || ('Billing API ' + res.status))
  return data
}

export async function createJob (payload) {
  const res = await authFetch(
    `${BASE_URL}/api/resource/Dispatch%20Job`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    },
  )
  const data = await res.json()
  if (data.exc) throw new Error(data.exc)
  return data.data
}

export async function publishJobs (jobNames) {
  const results = await Promise.allSettled(
    jobNames.map(name => apiPut('Dispatch Job', name, { published: 1 }))
  )
  const failed = results.filter(r => r.status === 'rejected')
  if (failed.length) console.warn(`[publishJobs] ${failed.length}/${jobNames.length} failed`)
  return { published: jobNames.length - failed.length, failed: failed.length }
}

export async function updateTech (name, payload) {
  return apiPut('Dispatch Technician', name, payload)
}

export async function createTech (payload) {
  const res = await authFetch(
    `${BASE_URL}/api/resource/Dispatch%20Technician`,
    { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) },
  )
  const data = await res.json()
  if (data.exc) throw new Error(data.exc)
  return data.data
}

export async function deleteTech (name) {
  const res = await authFetch(
    `${BASE_URL}/api/resource/Dispatch%20Technician/${encodeURIComponent(name)}`,
    { method: 'DELETE' },
  )
  if (!res.ok) {
    const data = await res.json().catch(() => ({}))
    const msg = data._server_messages ? JSON.parse(JSON.parse(data._server_messages)[0]).message : data.exception || 'Delete failed'
    throw new Error(msg)
  }
}

export async function fetchTags () {
  const data = await apiGet('/api/resource/Dispatch%20Tag?fields=["name","label","color","category"]&limit=200')
  return data.data || []
}

export async function createTag (label, category = 'Custom', color = '#6b7280') {
  const res = await authFetch(
    `${BASE_URL}/api/resource/Dispatch%20Tag`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ label, category, color }),
    },
  )
  const data = await res.json()
  if (data.exc) throw new Error(data.exc)
  return data.data
}

export async function updateTag (name, payload) {
  return apiPut('Dispatch Tag', name, payload)
}

export async function renameTag (oldName, newName) {
  const res = await authFetch(
    `${BASE_URL}/api/method/frappe.client.rename_doc`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ doctype: 'Dispatch Tag', old_name: oldName, new_name: newName }),
    },
  )
  const data = await res.json()
  if (data.exc) throw new Error(data.exc)
  return data.message // returns new name
}

export async function deleteTag (name) {
  const res = await authFetch(
    `${BASE_URL}/api/resource/Dispatch%20Tag/${encodeURIComponent(name)}`,
    { method: 'DELETE' },
  )
  if (!res.ok) throw new Error('Delete tag failed')
}
