import axios from 'axios'
import { withBase, stripBase } from './../utils/basePath'

// BASE_URL ends in '/', so this is '/api' at root or '/ops/api' under a subpath
// mount. Keeps the SPA, its API, and IIS all on the same mount path.
const api = axios.create({
  baseURL: import.meta.env.BASE_URL + 'api',
  headers: {
    'Content-Type': 'application/json'
  }
})

// Add auth token and normalize pagination params
api.interceptors.request.use(config => {
  const token = localStorage.getItem('token')
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  // Send both perpage and per_page for backend compatibility
  if (config.params?.perpage) {
    config.params.per_page = config.params.perpage
  }
  return config
})

// Handle 401 errors (token expired) - only redirect if user was logged in
api.interceptors.response.use(
  response => response,
  error => {
    if (error.response?.status === 401) {
      const hadToken = localStorage.getItem('token')
      localStorage.removeItem('token')
      localStorage.removeItem('user')
      // Only redirect if user was previously logged in (session expired).
      // Preserve the destination so login returns the user to this page.
      if (hadToken) {
        const loginPath = withBase('/login')
        // Router paths exclude the mount base; strip it or login's
        // router.push double-prefixes under a subpath mount.
        const here = stripBase(window.location.pathname) + window.location.search
        const target = here && here !== '/login'
          ? loginPath + '?redirect=' + encodeURIComponent(here)
          : loginPath
        window.location.href = target
      }
    }
    return Promise.reject(error)
  }
)

export default api

// The backend clamps perpage to MAX_PAGE_SIZE (100) and says nothing about it,
// so asking for `perpage: 1000` silently returns the first 100 rows and drops
// the rest. A picker built that way looks complete and is not: with 126
// applications on a live site, the 26 sorting last were simply unselectable.
//
// Use this wherever a control needs the WHOLE list (dropdowns, pickers, label
// batches) rather than a page of it. Returns the full array directly, not an
// axios response. Anything that renders a paged table should keep calling
// list() with a real page number instead.
export async function fetchAllPages(path, params = {}) {
  const first = await api.get(path, { params: { ...params, perpage: 100, page: 1 } })
  let items = first.data.data || []
  const totalpages = first.data.meta?.pagination?.totalpages || 1
  if (totalpages > 1) {
    const rest = await Promise.all(
      Array.from({ length: totalpages - 1 }, (_, i) =>
        api.get(path, { params: { ...params, perpage: 100, page: i + 2 } })
      )
    )
    rest.forEach(response => { items = items.concat(response.data.data || []) })
  }
  return items
}

// Auth API
export const authApi = {
  login(username, password) {
    return api.post('/auth/login', { username, password })
  },
  logout() {
    return api.post('/auth/logout')
  },
  me() {
    return api.get('/auth/me')
  },
  refresh() {
    const refreshToken = localStorage.getItem('refreshToken')
    return api.post('/auth/refresh', {}, {
      headers: { Authorization: `Bearer ${refreshToken}` }
    })
  },
  changePassword(payload) {
    return api.post('/auth/change-password', payload)
  }
}

// Machines API (plugin)
export const machinesApi = {
  list(params = {}) {
    return api.get('/machines', { params })
  },
  // Every machine, paged past the backend's 100-row cap. Batch label printing
  // and "pick any record" dropdowns must use this: list() with a large
  // perpage is clamped to 100 and still returns a success response, so
  // the tail simply goes missing.
  listAll(params = {}) {
    return fetchAllPages('/machines', params)
  },
  get(id) {
    return api.get(`/machines/${id}`)
  },
  getByAsset(assetId) {
    return api.get(`/machines/by-asset/${assetId}`)
  },
  create(data) {
    return api.post('/machines', data)
  },
  update(id, data) {
    return api.put(`/machines/${id}`, data)
  },
  delete(id) {
    return api.delete(`/machines/${id}`)
  },
  dashboardSummary() {
    return api.get('/machines/dashboard/summary')
  },
  // Machine types
  types: {
    list(params = {}) {
      return api.get('/machines/types', { params })
    },
    get(id) {
      return api.get(`/machines/types/${id}`)
    },
    create(data) {
      return api.post('/machines/types', data)
    },
    update(id, data) {
      return api.put(`/machines/types/${id}`, data)
    },
    remove(id) {
      return api.delete(`/machines/types/${id}`)
    }
  }
}

// Computers API (plugin)
export const computersApi = {
  list(params = {}) {
    return api.get('/computers', { params })
  },
  // Every PC, paged past the backend's 100-row cap. Batch label printing
  // and "pick any record" dropdowns must use this: list() with a large
  // perpage is clamped to 100 and still returns a success response, so
  // the tail simply goes missing.
  listAll(params = {}) {
    return fetchAllPages('/computers', params)
  },
  displayKiosks() {
    return api.get('/computers/display-kiosks')
  },
  get(id) {
    return api.get(`/computers/${id}`)
  },
  getByAsset(assetId) {
    return api.get(`/computers/by-asset/${assetId}`)
  },
  getByHostname(hostname) {
    return api.get(`/computers/by-hostname/${hostname}`)
  },
  create(data) {
    return api.post('/computers', data)
  },
  update(id, data) {
    return api.put(`/computers/${id}`, data)
  },
  delete(id) {
    return api.delete(`/computers/${id}`)
  },
  dashboardSummary() {
    return api.get('/computers/dashboard/summary')
  },
  // Computer types
  types: {
    list(params = {}) {
      return api.get('/computers/types', { params })
    },
    get(id) {
      return api.get(`/computers/types/${id}`)
    },
    create(data) {
      return api.post('/computers/types', data)
    },
    update(id, data) {
      return api.put(`/computers/types/${id}`, data)
    },
    remove(id) {
      return api.delete(`/computers/types/${id}`)
    }
  },
  // Remote-access protocol catalog
  protocols: {
    list(params = {}) {
      return api.get('/computers/protocols', { params })
    },
    create(data) {
      return api.post('/computers/protocols', data)
    },
    update(id, data) {
      return api.put(`/computers/protocols/${id}`, data)
    },
    remove(id) {
      return api.delete(`/computers/protocols/${id}`)
    }
  }
}

// Relationship Types API
export const relationshipTypesApi = {
  list() {
    return api.get('/assets/relationshiptypes')
  },
  create(data) {
    return api.post('/assets/relationshiptypes', data)
  },
  update(id, data) {
    return api.put(`/assets/relationshiptypes/${id}`, data)
  },
  remove(id) {
    return api.delete(`/assets/relationshiptypes/${id}`)
  }
}

// Model Types API (the vendor models catalog: modeltypeid)
export const modeltypesApi = {
  list(params = {}) {
    return api.get('/modeltypes', { params })
  },
  create(data) {
    return api.post('/modeltypes', data)
  },
  update(id, data) {
    return api.put(`/modeltypes/${id}`, data)
  },
  delete(id) {
    return api.delete(`/modeltypes/${id}`)
  }
}

// Vendors API
export const vendorsApi = {
  list(params = {}) {
    return api.get('/vendors', { params })
  },
  get(id) {
    return api.get(`/vendors/${id}`)
  },
  create(data) {
    return api.post('/vendors', data)
  },
  update(id, data) {
    return api.put(`/vendors/${id}`, data)
  },
  delete(id) {
    return api.delete(`/vendors/${id}`)
  }
}

// Locations API
export const locationsApi = {
  list(params = {}) {
    return api.get('/locations', { params })
  },
  get(id) {
    return api.get(`/locations/${id}`)
  },
  create(data) {
    return api.post('/locations', data)
  },
  update(id, data) {
    return api.put(`/locations/${id}`, data)
  },
  delete(id) {
    return api.delete(`/locations/${id}`)
  },
  types: {
    list(params = {}) {
      return api.get('/locations/types', { params })
    },
    create(data) {
      return api.post('/locations/types', data)
    },
    update(id, data) {
      return api.put(`/locations/types/${id}`, data)
    },
    remove(id) {
      return api.delete(`/locations/types/${id}`)
    }
  }
}

// Printers API
export const printersApi = {
  list(params = {}) {
    return api.get('/printers', { params })
  },

  // Printer assignment for one asset - a MACHINE normally, since the assignment
  // belongs to the bay and reaches whichever PC controls it. Reads and writes
  // that asset's OWN rows: the resolved view (own, else inherited) is
  // /printers/for-host, which the client uses, not the editor.
  assignment: {
    get(assetid) {
      return api.get(`/printers/assignments/for-asset/${assetid}`)
    },
    // The WHOLE set in one call. A row-at-a-time edit is a non-atomic
    // reconcile: an HTTP failure part way leaves an asset half-assigned, with
    // nothing recording what was meant.
    set(assetid, printerassetids, defaultprinterassetid) {
      return api.put(`/printers/assignments/for-asset/${assetid}`, {
        printerassetids,
        defaultprinterassetid: defaultprinterassetid ?? null
      })
    }
  },
  // Every printer, paged past the backend's 100-row cap. Batch label printing
  // and "pick any record" dropdowns must use this: list() with a large
  // perpage is clamped to 100 and still returns a success response, so
  // the tail simply goes missing.
  listAll(params = {}) {
    return fetchAllPages('/printers', params)
  },
  get(id) {
    return api.get(`/printers/${id}`)
  },
  // create/update write asset core + printer extension in one call (the
  // printers plugin owns both). Use these instead of the legacy machinesApi.
  create(data) {
    return api.post('/printers', data)
  },
  update(id, data) {
    return api.put(`/printers/${id}`, data)
  },
  updateExtension(id, data) {
    return api.put(`/printers/${id}/printerdata`, data)
  },
  // printer sub-types (Laser, Inkjet, Label, Card, Wide Format, ...)
  types: {
    list(params = {}) {
      return api.get('/printers/types', { params })
    },
    create(data) {
      return api.post('/printers/types', data)
    },
    update(id, data) {
      return api.put(`/printers/types/${id}`, data)
    },
    remove(id) {
      return api.delete(`/printers/types/${id}`)
    }
  },
  updateCommunication(id, data) {
    return api.put(`/printers/${id}/communication`, data)
  },
  getSupplies(id) {
    return api.get(`/printers/${id}/supplies`)
  },
  getDrivers(id) {
    return api.get(`/printers/${id}/drivers`)
  },
  supplyForecast(days = 90) {
    return api.get('/printers/supplies/forecast', { params: { days } })
  },
  lowSupplies() {
    return api.get('/printers/lowsupplies')
  },
  refreshSupplies() {
    return api.post('/printers/supplies/refresh')
  },
  lookup({ ip, fqdn } = {}) {
    return api.get('/printers/lookup', { params: { ip, fqdn } })
  },
  dashboardSummary() {
    return api.get('/printers/dashboard/summary')
  },
  // Flat network-printer list (with mapx/mapy) for the installer map.
  installList() {
    return api.get('/printers/install-list')
  },
  drivers: {
    list(params = {}) {
      return api.get('/printers/drivers', { params })
    },
    create(data) {
      return api.post('/printers/drivers', data)
    },
    update(id, data) {
      return api.put(`/printers/drivers/${id}`, data)
    },
    delete(id) {
      return api.delete(`/printers/drivers/${id}`)
    }
  },
  supplyTypes: {
    list() {
      return api.get('/printers/supplytypes')
    },
    create(data) {
      return api.post('/printers/supplytypes', data)
    }
  },
  // model -> toner/drum/waste part-number management
  modelSupplies: {
    meta() {
      return api.get('/printers/supplies/meta')
    },
    listModels(params = {}) {
      return api.get('/printers/models', { params })
    },
    list(modelnumberid) {
      return api.get(`/printers/models/${modelnumberid}/supplies`)
    },
    create(modelnumberid, data) {
      return api.post(`/printers/models/${modelnumberid}/supplies`, data)
    },
    update(modelsupplyid, data) {
      return api.put(`/printers/supplies/${modelsupplyid}`, data)
    },
    delete(modelsupplyid) {
      return api.delete(`/printers/supplies/${modelsupplyid}`)
    }
  }
}

// Dashboard API
export const dashboardApi = {
  summary() {
    return api.get('/dashboard/summary')
  },
  navigation() {
    return api.get('/dashboard/navigation')
  }
}

// Models API
export const modelsApi = {
  list(params = {}) {
    return api.get('/models', { params })
  },
  // Backend caps perpage at 100, so page through every model. Returns the
  // full array directly (not an axios response). Use in forms whose model
  // dropdown must include the editing record's model regardless of page.
  listAll(params = {}) {
    return fetchAllPages('/models', params)
  },
  get(id) {
    return api.get(`/models/${id}`)
  },
  create(data) {
    return api.post('/models', data)
  },
  update(id, data) {
    return api.put(`/models/${id}`, data)
  },
  delete(id) {
    return api.delete(`/models/${id}`)
  },
  uploadImage(id, file) {
    // multipart photo upload; backend sets imageurl to the served URL
    const form = new FormData()
    form.append('file', file)
    return api.post(`/models/${id}/image`, form, { headers: { 'Content-Type': 'multipart/form-data' } })
  },
  removeImage(id) {
    return api.delete(`/models/${id}/image`)
  }
}

// Operating Systems API
export const operatingsystemsApi = {
  list(params = {}) {
    return api.get('/operatingsystems', { params })
  },
  get(id) {
    return api.get(`/operatingsystems/${id}`)
  },
  create(data) {
    return api.post('/operatingsystems', data)
  },
  update(id, data) {
    return api.put(`/operatingsystems/${id}`, data)
  },
  delete(id) {
    return api.delete(`/operatingsystems/${id}`)
  }
}

// Business Units API
export const businessunitsApi = {
  list(params = {}) {
    return api.get('/businessunits', { params })
  },
  get(id) {
    return api.get(`/businessunits/${id}`)
  },
  create(data) {
    return api.post('/businessunits', data)
  },
  update(id, data) {
    return api.put(`/businessunits/${id}`, data)
  },
  delete(id) {
    return api.delete(`/businessunits/${id}`)
  }
}

// Applications API
export const applicationsApi = {
  list(params = {}) {
    return api.get('/applications', { params })
  },
  // Every application, paged past the backend's 100-row cap. The catalogue is
  // already over 100 entries on a live site, so any picker offering "all
  // applications" must use this and not list({ perpage: <big number> }).
  listAll(params = {}) {
    return fetchAllPages('/applications', params)
  },
  get(id) {
    return api.get(`/applications/${id}`)
  },
  create(data) {
    return api.post('/applications', data)
  },
  update(id, data) {
    return api.put(`/applications/${id}`, data)
  },
  delete(id) {
    return api.delete(`/applications/${id}`)
  },
  // multipart image upload; backend sets image to the served URL
  uploadImage(id, file) {
    const form = new FormData()
    form.append('file', file)
    return api.post(`/applications/${id}/image`, form, { headers: { 'Content-Type': 'multipart/form-data' } })
  },
  removeImage(id) {
    return api.delete(`/applications/${id}/image`)
  },
  // installer upload; backend sets installpath to the download URL.
  // onProgress gets 0-100 - an installer is big enough that a silent wait
  // reads as a hang.
  uploadPackage(id, file, onProgress) {
    const form = new FormData()
    form.append('file', file)
    return api.post(`/applications/${id}/package`, form, {
      headers: { 'Content-Type': 'multipart/form-data' },
      onUploadProgress: event => {
        if (onProgress && event.total) {
          onProgress(Math.round((event.loaded * 100) / event.total))
        }
      }
    })
  },
  removePackage(id) {
    return api.delete(`/applications/${id}/package`)
  },
  // Versions
  getVersions(appId) {
    return api.get(`/applications/${appId}/versions`)
  },
  createVersion(appId, data) {
    return api.post(`/applications/${appId}/versions`, data)
  },
  // Get PCs that have this app installed
  getInstalledOn(appId) {
    return api.get(`/applications/${appId}/installed`)
  },
  // Machine applications (installed apps)
  getMachineApps(machineId) {
    return api.get(`/applications/machines/${machineId}`)
  },
  installApp(machineId, data) {
    return api.post(`/applications/machines/${machineId}`, data)
  },
  uninstallApp(machineId, appId) {
    return api.delete(`/applications/machines/${machineId}/${appId}`)
  },
  updateInstalledApp(machineId, appId, data) {
    return api.put(`/applications/machines/${machineId}/${appId}`, data)
  }
}

// Support Teams API (teams + nested contacts)
export const supportteamsApi = {
  list(params = {}) {
    return api.get('/supportteams', { params })
  },
  get(id) {
    return api.get(`/supportteams/${id}`)
  },
  create(data) {
    return api.post('/supportteams', data)
  },
  update(id, data) {
    return api.put(`/supportteams/${id}`, data)
  },
  remove(id) {
    return api.delete(`/supportteams/${id}`)
  },
  contacts: {
    add(teamId, data) {
      return api.post(`/supportteams/${teamId}/contacts`, data)
    },
    update(teamId, contactId, data) {
      return api.put(`/supportteams/${teamId}/contacts/${contactId}`, data)
    },
    remove(teamId, contactId) {
      return api.delete(`/supportteams/${teamId}/contacts/${contactId}`)
    }
  }
}

// Search API
export const searchApi = {
  search(query, params = {}) {
    return api.get('/search', { params: { q: query, ...params } })
  }
}

// Knowledge Base API
export const knowledgebaseApi = {
  list(params = {}) {
    return api.get('/knowledgebase', { params })
  },
  get(id) {
    return api.get(`/knowledgebase/${id}`)
  },
  create(data) {
    return api.post('/knowledgebase', data)
  },
  update(id, data) {
    return api.put(`/knowledgebase/${id}`, data)
  },
  delete(id) {
    return api.delete(`/knowledgebase/${id}`)
  },
  trackClick(id) {
    return api.post(`/knowledgebase/${id}/click`)
  },
  getStats() {
    return api.get('/knowledgebase/stats')
  }
}

// Assets API (unified)
export const assetsApi = {
  list(params = {}) {
    return api.get('/assets', { params })
  },
  get(id) {
    return api.get(`/assets/${id}`)
  },
  create(data) {
    return api.post('/assets', data)
  },
  update(id, data) {
    return api.put(`/assets/${id}`, data)
  },
  delete(id) {
    return api.delete(`/assets/${id}`)
  },
  getMap(params = {}) {
    return api.get('/assets/map', { params })
  },
  // Relationships
  getRelationships(id) {
    return api.get(`/assets/${id}/relationships`)
  },
  createRelationship(data) {
    return api.post('/assets/relationships', data)
  },
  deleteRelationship(relationshipId) {
    return api.delete(`/assets/relationships/${relationshipId}`)
  },
  // Search assets (for relationship picker)
  search(query, params = {}) {
    return api.get('/assets', { params: { search: query, ...params } })
  },
  // Lookup asset by asset/machine number
  lookup(assetnumber) {
    return api.get(`/assets/lookup/${encodeURIComponent(assetnumber)}`)
  },
  types: {
    list() {
      return api.get('/assets/types')
    },
    get(id) {
      return api.get(`/assets/types/${id}`)
    },
    update(id, data) {
      return api.put(`/assets/types/${id}`, data)
    }
  },
  statuses: {
    list(params = {}) {
      return api.get('/assets/statuses', { params })
    },
    create(data) {
      return api.post('/assets/statuses', data)
    },
    update(id, data) {
      return api.put(`/assets/statuses/${id}`, data)
    },
    delete(id) {
      return api.delete(`/assets/statuses/${id}`)
    }
  }
}

// Notifications API
export const notificationsApi = {
  list(params = {}) {
    return api.get('/notifications', { params })
  },
  get(id) {
    return api.get(`/notifications/${id}`)
  },
  create(data) {
    return api.post('/notifications', data)
  },
  update(id, data) {
    return api.put(`/notifications/${id}`, data)
  },
  delete(id) {
    return api.delete(`/notifications/${id}`)
  },
  getActive() {
    return api.get('/notifications/active')
  },
  getCalendar(params = {}) {
    return api.get('/notifications/calendar', { params })
  },
  dashboardSummary() {
    return api.get('/notifications/dashboard/summary')
  },
  getShopfloor(params = {}) {
    return api.get('/notifications/shopfloor', { params })
  },
  getEmployeeRecognitions(sso) {
    return api.get(`/notifications/employee/${sso}`)
  },
  types: {
    list() {
      return api.get('/notifications/types')
    },
    get(id) {
      return api.get(`/notifications/types/${id}`)
    },
    create(data) {
      return api.post('/notifications/types', data)
    },
    update(id, data) {
      return api.put(`/notifications/types/${id}`, data)
    }
  }
}

// USB Devices API
export const usbApi = {
  list(params = {}) {
    return api.get('/usb', { params })
  },
  // Every USB device, paged past the backend's 100-row cap. Batch label printing
  // and "pick any record" dropdowns must use this: list() with a large
  // perpage is clamped to 100 and still returns a success response, so
  // the tail simply goes missing.
  listAll(params = {}) {
    return fetchAllPages('/usb', params)
  },
  get(id) {
    return api.get(`/usb/${id}`)
  },
  create(data) {
    return api.post('/usb', data)
  },
  update(id, data) {
    return api.put(`/usb/${id}`, data)
  },
  checkout(id, data) {
    return api.post(`/usb/${id}/checkout`, data)
  },
  checkin(id, data = {}) {
    return api.post(`/usb/${id}/checkin`, data)
  },
  getHistory(id, params = {}) {
    return api.get(`/usb/${id}/history`, { params })
  },
  // check-out log rows for one badge; activeonly = only currently-held devices
  getUserCheckouts(badge, activeonly = true) {
    const path = activeonly ? '/usb/checkouts/active' : '/usb/checkouts'
    return api.get(path, { params: { badge } })
  }
}

// Reports API
export const reportsApi = {
  list() {
    return api.get('/reports')
  },
  machinesByType(params = {}) {
    return api.get('/reports/machines-by-type', { params })
  },
  assetsByStatus(params = {}) {
    return api.get('/reports/assets-by-status', { params })
  },
  kbPopularity(params = {}) {
    return api.get('/reports/kb-popularity', { params })
  },
  softwareCompliance(params = {}) {
    return api.get('/reports/software-compliance', { params })
  },
  assetInventory(params = {}) {
    return api.get('/reports/asset-inventory', { params })
  },
  pcRelationships(params = {}) {
    return api.get('/reports/pc-relationships', { params })
  },
  // On-demand report delivery: email the given rows as an HTML table.
  // Recipients default to the site Alert Recipients when `to` is omitted.
  email(payload) {
    return api.post('/reports/email', payload)
  }
}

// Employees API (wjf_employees database)
export const employeesApi = {
  search(query, limit = 10) {
    return api.get('/employees/search', { params: { q: query, limit } })
  },
  lookup(sso) {
    return api.get(`/employees/lookup/${sso}`)
  },
  lookupMultiple(ssoList) {
    return api.get('/employees/lookup', { params: { sso: ssoList } })
  },
  // Self-hosted directory management (directory_mode=selfhosted)
  directory: {
    list() {
      return api.get('/employees/directory')
    },
    create(data) {
      return api.post('/employees/directory', data)
    },
    update(sso, data) {
      return api.put(`/employees/directory/${sso}`, data)
    },
    remove(sso) {
      return api.delete(`/employees/directory/${sso}`)
    },
    importCsv(csv) {
      return api.post('/employees/directory/import', { csv })
    },
    // multipart photo upload; backend sets photofilename + returns photourl
    uploadPhoto(sso, file) {
      const form = new FormData()
      form.append('file', file)
      return api.post(`/employees/${sso}/photo`, form, { headers: { 'Content-Type': 'multipart/form-data' } })
    },
    removePhoto(sso) {
      return api.delete(`/employees/${sso}/photo`)
    }
  }
}

// Alias for different casing
export const businessUnitsApi = businessunitsApi

// Dashboard defaults: visitor-IP -> business-unit mapping for kiosks
export const dashboardDefaultsApi = {
  list() {
    return api.get('/dashboarddefaults')
  },
  create(data) {
    return api.post('/dashboarddefaults', data)
  },
  update(id, data) {
    return api.put(`/dashboarddefaults/${id}`, data)
  },
  delete(id) {
    return api.delete(`/dashboarddefaults/${id}`)
  },
  // Resolve the calling display's business unit by its IP
  visitorLocation() {
    return api.get('/dashboarddefaults/visitor-location')
  }
}

// System Settings API
export const pluginsApi = {
  list() {
    return api.get('/plugins')
  },
  // Flat array of enabled plugin names. jwt-optional, safe for kiosk routes.
  enabled() {
    return api.get('/plugins/enabled')
  },
  setEnabled(name, enabled) {
    return api.put(`/plugins/${name}`, { enabled })
  }
}

export const setupApi = {
  needsAdmin() {
    return api.get('/setup/needs-admin')
  },
  createAdmin(data) {
    return api.post('/setup/create-admin', data)
  },
  seedReference() {
    return api.post('/setup/seed-reference')
  },
  seedStarter() {
    return api.post('/setup/seed-starter')
  },
  complete() {
    return api.post('/setup/complete')
  }
}

// Buildings and the levels within them (ADR-017). Reads are public - the
// printer installer map draws a blueprint before anyone logs in.
export const mapLevelsApi = {
  list() {
    return api.get('/maplevels')
  },
  get(levelid) {
    return api.get(`/maplevels/${levelid}`)
  },
  createBuilding(payload) {
    return api.post('/maplevels/buildings', payload)
  },
  updateBuilding(buildingid, payload) {
    return api.patch(`/maplevels/buildings/${buildingid}`, payload)
  },
  create(payload) {
    return api.post('/maplevels', payload)
  },
  update(levelid, payload) {
    return api.patch(`/maplevels/${levelid}`, payload)
  },
  remove(levelid) {
    return api.delete(`/maplevels/${levelid}`)
  },
  // Returns the image's real pixel size alongside the stored dimensions. On an
  // empty level the server adopts them; on a populated one it refuses and says
  // so, because changing the coordinate space moves every marker on it.
  uploadBlueprint(levelid, theme, file) {
    const form = new FormData()
    form.append('file', file)
    form.append('theme', theme)
    return api.post(`/maplevels/${levelid}/blueprint`, form, {
      headers: { 'Content-Type': 'multipart/form-data' },
    })
  },
}

// Bulk marker positions: the landmark transform, bulk placement, review state
// and undo. Every write snapshots first.
export const mapPositionsApi = {
  setPositions(positions, verified = true) {
    return api.post('/mappositions/positions', { positions, verified })
  },
  transform(payload) {
    return api.post('/mappositions/transform', payload)
  },
  verify(assetids, unverify = false) {
    return api.post('/mappositions/verify', { assetids, unverify })
  },
  snapshots() {
    return api.get('/mappositions/snapshots')
  },
  restore(snapshotid) {
    return api.post(`/mappositions/snapshots/${snapshotid}/restore`)
  },
}

export const settingsApi = {
  list(params = {}) {
    return api.get('/settings', { params })
  },
  get(key) {
    return api.get(`/settings/${key}`)
  },
  update(key, value) {
    return api.put(`/settings/${key}`, { value })
  },
  testEmail(to) {
    return api.post('/settings/test-email', { to })
  },
  create(data) {
    return api.post('/settings', data)
  },
  uploadMapBlueprint(theme, file) {
    const form = new FormData()
    form.append('theme', theme)
    form.append('file', file)
    return api.post('/settings/map-blueprint', form, { headers: { 'Content-Type': 'multipart/form-data' } })
  },
  uploadBrandingLogo(kind, file) {
    // kind is one of site/qr/badge/favicon; backend writes the matching setting
    const form = new FormData()
    form.append('kind', kind)
    form.append('file', file)
    return api.post('/settings/branding-logo', form, { headers: { 'Content-Type': 'multipart/form-data' } })
  }
}

// Slide manager (lobby display + shopfloor screensaver)
export const slidesApi = {
  list(surface) {
    return api.get(`/slides/${surface}`)
  },
  upload(surface, formData) {
    return api.post(`/slides/${surface}/upload`, formData, {
      headers: { 'Content-Type': 'multipart/form-data' }
    })
  },
  reorder(surface, order) {
    return api.post(`/slides/${surface}/order`, { order })
  },
  remove(surface, files) {
    return api.post(`/slides/${surface}/delete`, { files })
  }
}

// Audit Logs API
export const auditLogsApi = {
  list(params = {}) {
    return api.get('/auditlogs', { params })
  },
  getEntityHistory(entitytype, entityid) {
    return api.get(`/auditlogs/entity/${entitytype}/${entityid}`)
  },
  getStats() {
    return api.get('/auditlogs/stats')
  }
}

// Users API
export const usersApi = {
  list() {
    return api.get('/users')
  },
  get(id) {
    return api.get(`/users/${id}`)
  },
  create(data) {
    return api.post('/users', data)
  },
  update(id, data) {
    return api.put(`/users/${id}`, data)
  },
  delete(id) {
    return api.delete(`/users/${id}`)
  },
  // Permissions
  permissions: {
    list() {
      return api.get('/users/permissions')
    }
  },
  // Roles
  roles: {
    list() {
      return api.get('/users/roles')
    },
    create(data) {
      return api.post('/users/roles', data)
    },
    update(id, data) {
      return api.put(`/users/roles/${id}`, data)
    },
    delete(id) {
      return api.delete(`/users/roles/${id}`)
    }
  }
}

// Personal API tokens: authenticate scripts/integrations as a user without
// the hourly-expiring login JWT. The secret is returned ONCE, on create.
export const apitokensApi = {
  // all=true (admin) lists everyone's tokens; otherwise just the caller's.
  list(params = {}) {
    return api.get('/apitokens', { params })
  },
  create(data) {
    return api.post('/apitokens', data)
  },
  update(id, data) {
    return api.put(`/apitokens/${id}`, data)
  },
  remove(id) {
    return api.delete(`/apitokens/${id}`)
  }
}

// Network API (devices, subnets, and VLANs)
export const networkApi = {
  // Network devices
  list(params = {}) {
    return api.get('/network', { params })
  },
  // Every network device, paged past the backend's 100-row cap. Batch label
  // printing and "pick any record" dropdowns must use this: list() with a
  // large perpage is clamped to 100 and still returns a success response, so
  // the tail simply goes missing.
  listAll(params = {}) {
    return fetchAllPages('/network', params)
  },
  get(id) {
    return api.get(`/network/${id}`)
  },
  getByAsset(assetId) {
    return api.get(`/network/by-asset/${assetId}`)
  },
  getByHostname(hostname) {
    return api.get(`/network/by-hostname/${hostname}`)
  },
  create(data) {
    return api.post('/network', data)
  },
  update(id, data) {
    return api.put(`/network/${id}`, data)
  },
  delete(id) {
    return api.delete(`/network/${id}`)
  },
  dashboardSummary() {
    return api.get('/network/dashboard/summary')
  },
  // Network device types
  types: {
    list(params = {}) {
      return api.get('/network/types', { params })
    },
    get(id) {
      return api.get(`/network/types/${id}`)
    },
    create(data) {
      return api.post('/network/types', data)
    },
    update(id, data) {
      return api.put(`/network/types/${id}`, data)
    },
    remove(id) {
      return api.delete(`/network/types/${id}`)
    }
  },
  // VLANs
  vlans: {
    list(params = {}) {
      return api.get('/network/vlans', { params })
    },
    get(id) {
      return api.get(`/network/vlans/${id}`)
    },
    create(data) {
      return api.post('/network/vlans', data)
    },
    update(id, data) {
      return api.put(`/network/vlans/${id}`, data)
    },
    delete(id) {
      return api.delete(`/network/vlans/${id}`)
    }
  },
  // Subnets
  subnets: {
    list(params = {}) {
      return api.get('/network/subnets', { params })
    },
    get(id) {
      return api.get(`/network/subnets/${id}`)
    },
    create(data) {
      return api.post('/network/subnets', data)
    },
    update(id, data) {
      return api.put(`/network/subnets/${id}`, data)
    },
    delete(id) {
      return api.delete(`/network/subnets/${id}`)
    }
  }
}

// Custom fields: site-defined attributes per asset type.
export const customFieldsApi = {
  // Definitions
  list(params = {}) {
    return api.get('/customfields', { params })
  },
  create(data) {
    return api.post('/customfields', data)
  },
  update(fieldid, data) {
    return api.put(`/customfields/${fieldid}`, data)
  },
  remove(fieldid) {
    return api.delete(`/customfields/${fieldid}`)
  },
  // Per-asset values (defs merged with the asset's stored values)
  forAsset(assetid) {
    return api.get(`/customfields/asset/${assetid}`)
  },
  saveForAsset(assetid, values) {
    return api.put(`/customfields/asset/${assetid}`, { values })
  }
}

// Warranty plugin: asset warranty tracking (manual + provider lookups).
export const warrantyApi = {
  list(params = {}) {
    return api.get('/warranty', { params })
  },
  get(id) {
    return api.get(`/warranty/${id}`)
  },
  forAsset(assetid) {
    return api.get(`/warranty/asset/${assetid}`)
  },
  create(data) {
    return api.post('/warranty', data)
  },
  update(id, data) {
    return api.put(`/warranty/${id}`, data)
  },
  remove(id) {
    return api.delete(`/warranty/${id}`)
  },
  refresh(id) {
    return api.post(`/warranty/${id}/refresh`)
  },
  syncDell(all = false) {
    return api.post('/warranty/sync/dell', null, { params: all ? { all: 'true' } : {} })
  },
  report() {
    return api.get('/warranty/report')
  },
  // proof of cover: invoice, certificate, whatever the vendor sent
  uploadProof(id, file) {
    const form = new FormData()
    form.append('file', file)
    return api.post(`/warranty/${id}/proof`, form, { headers: { 'Content-Type': 'multipart/form-data' } })
  },
  removeProof(id) {
    return api.delete(`/warranty/${id}/proof`)
  }
}

// Measuring tools API (plugin)
export const measuringtoolsApi = {
  list(params = {}) {
    return api.get('/measuringtools', { params })
  },
  // Every measuring tool, paged past the backend's 100-row cap. Batch label printing
  // and "pick any record" dropdowns must use this: list() with a large
  // perpage is clamped to 100 and still returns a success response, so
  // the tail simply goes missing.
  listAll(params = {}) {
    return fetchAllPages('/measuringtools', params)
  },
  get(id) {
    return api.get(`/measuringtools/${id}`)
  },
  getByAsset(assetid) {
    return api.get(`/measuringtools/by-asset/${assetid}`)
  },
  create(data) {
    return api.post('/measuringtools', data)
  },
  update(id, data) {
    return api.put(`/measuringtools/${id}`, data)
  },
  remove(id) {
    return api.delete(`/measuringtools/${id}`)
  },
  calibrationReport() {
    return api.get('/measuringtools/report/calibration')
  },
  // Measuring-tool types
  types: {
    list(params = {}) {
      return api.get('/measuringtools/types', { params })
    },
    get(id) {
      return api.get(`/measuringtools/types/${id}`)
    },
    create(data) {
      return api.post('/measuringtools/types', data)
    },
    update(id, data) {
      return api.put(`/measuringtools/types/${id}`, data)
    },
    remove(id) {
      return api.delete(`/measuringtools/types/${id}`)
    }
  }
}

// 3D printed parts (printedparts plugin)
export const printedpartsApi = {
  list(params = {}) {
    return api.get('/printedparts/items', { params })
  },
  // Every printed item, paged past the backend's 100-row cap. The label sheet
  // must print the whole selection, not the first page of it.
  listAll(params = {}) {
    return fetchAllPages('/printedparts/items', params)
  },
  get(printeditemid) {
    return api.get(`/printedparts/items/${printeditemid}`)
  },
  create(data) {
    return api.post('/printedparts/items', data)
  },
  update(printeditemid, data) {
    return api.put(`/printedparts/items/${printeditemid}`, data)
  },
  remove(printeditemid) {
    return api.delete(`/printedparts/items/${printeditemid}`)
  },
  restore(printeditemid) {
    return api.post(`/printedparts/items/${printeditemid}/restore`)
  },
  uploadImage(printeditemid, file) {
    const formData = new FormData()
    formData.append('file', file)
    return api.post(`/printedparts/items/${printeditemid}/image`, formData, {
      headers: { 'Content-Type': 'multipart/form-data' }
    })
  },
  deleteImage(printeditemid) {
    return api.delete(`/printedparts/items/${printeditemid}/image`)
  },
  restock(printeditemid, data) {
    return api.post(`/printedparts/items/${printeditemid}/restock`, data)
  },
  adjust(printeditemid, data) {
    return api.post(`/printedparts/items/${printeditemid}/adjust`, data)
  },
  kioskItem(itemcode) {
    return api.get(`/printedparts/kiosk/item/${encodeURIComponent(itemcode)}`)
  },
  kioskTake(data) {
    return api.post('/printedparts/kiosk/take', data)
  },
  listFiles(printeditemid) {
    return api.get(`/printedparts/items/${printeditemid}/files`)
  },
  uploadFile(printeditemid, file, note) {
    const formData = new FormData()
    formData.append('file', file)
    if (note) formData.append('note', note)
    return api.post(`/printedparts/items/${printeditemid}/files`, formData, {
      headers: { 'Content-Type': 'multipart/form-data' }
    })
  },
  removeFile(fileid) {
    return api.delete(`/printedparts/files/${fileid}`)
  }
}
