Source: services/platformApi.js

import axios from 'axios'
import logger from '../utils/logger.js'
import { types } from '../utils/logging.js'
import config from '../../config/index.js'

/**
 * Service for querying the Platform API (mainWebsiteUrl)
 * Unlike Datasette which uses SQL queries, this uses REST endpoints with query parameters
 */
export default {
  /**
   * Fetches entities from the Platform API /entity.json endpoint
   *
   * @param {Object} params - Query parameters
   * @param {string} [params.organisation_entity] - The organisation entity ID
   * @param {string} [params.dataset] - The dataset name
   * @param {string} [params.prefix] - Entity prefix filter (e.g., 'local-planning-group')
   * @param {number} [params.limit] - Maximum number of results
   * @param {number} [params.offset] - Number of results to skip
   * @param {string} [params.quality] - The quality level (e.g., 'authoritative', 'some')
   * @returns {Promise<{data: object, formattedData: object[]}>} - A promise that resolves to formatted entity data
   * @throws {Error} If the query fails or there is an error communicating with the Platform API
   */
  fetchEntities: async (params) => {
    if (!params.organisation_entity && !params.dataset && !params.prefix && !params.organisation) {
      throw new Error('organisation_entity, dataset, prefix, or organisation are required parameters')
    }
    const queryParams = new URLSearchParams()

    if (params.organisation_entity) queryParams.append('organisation_entity', params.organisation_entity)
    if (params.dataset) queryParams.append('dataset', params.dataset)
    if (params.prefix) queryParams.append('prefix', params.prefix)
    if (params.organisation) queryParams.append('organisation', params.organisation)
    if (params.limit) queryParams.append('limit', params.limit)
    if (params.offset) queryParams.append('offset', params.offset)
    if (params.quality) queryParams.append('quality', params.quality)

    const url = `${config.mainWebsiteUrl}/entity.json?${queryParams.toString()}`

    const data = await queryPlatformAPI(url, params)

    // Platform API returns { entities: [...] }
    const entities = data?.entities || []

    return {
      data,
      formattedData: entities
    }
  },

  /**
   * Fetches datasets from the Platform API /dataset.json endpoint
   *
   * @param {Object} params - Query params
   * @param {string} [params.dataset] - The dataset name
   * @returns {Promise<{data: object, formattedData: object[]}>} - A promise that resolves to formatted dataset data
   * @throws {Error} If the query fails or there is an error communicating with the Platform API
   */
  /**
   * Fetches all entities from the Platform API /entity.json endpoint, paginating through all results.
   * Accepts the same params as fetchEntities (except limit/offset which are managed internally).
   */
  fetchAllEntities: async (params) => {
    const pageSize = 100
    let offset = 0
    let allEntities = []

    while (true) {
      const queryParams = new URLSearchParams()
      if (params.organisation_entity) queryParams.append('organisation_entity', params.organisation_entity)
      if (params.dataset) queryParams.append('dataset', params.dataset)
      if (params.prefix) queryParams.append('prefix', params.prefix)
      if (params.organisation) queryParams.append('organisation', params.organisation)
      if (params.quality) queryParams.append('quality', params.quality)
      queryParams.append('limit', pageSize)
      queryParams.append('offset', offset)
      const data = await queryPlatformAPI(`${config.mainWebsiteUrl}/entity.json?${queryParams.toString()}`, params)
      const entities = data?.entities || []
      allEntities = allEntities.concat(entities)
      if (!data?.links?.next || entities.length < pageSize) break
      offset += pageSize
    }

    return { formattedData: allEntities }
  },

  /**
   * Fetches organisations from /organisation.json.
   *
   * @param {Object} [params]
   * @param {string} [params.organisations] - Optional dataset type to filter by (e.g. 'local-authority')
   * @returns {Promise<{data: object, grouped: object, flat: object[]}>}
   *   - data: raw response
   *   - grouped: organisations keyed by dataset type
   *   - flat: all organisations as a flat array across all types
   */
  fetchOrganisations: async (params = {}) => {
    const queryParams = new URLSearchParams()
    if (params.organisations) queryParams.append('organisations', params.organisations)
    const url = `${config.mainWebsiteUrl}/organisation.json${queryParams.toString() ? '?' + queryParams.toString() : ''}`
    const data = await queryPlatformAPI(url, params)
    const grouped = data?.organisations || {}
    const flat = Object.values(grouped).flat()
    return { data, grouped, flat }
  },

  fetchDatasets: async (params) => {
    const queryParams = new URLSearchParams()

    if (params.dataset) queryParams.append('dataset', params.dataset)

    const url = `${config.mainWebsiteUrl}/dataset.json?${queryParams.toString()}`

    const data = await queryPlatformAPI(url, params)

    // Platform API returns { datasets: [...] }
    const datasets = data?.datasets || []

    return {
      data,
      formattedData: datasets
    }
  }

}

/**
 * Generic query function for Platform API
 *
 * @param {string} url - The full URL to query
 * @param {Object} params - Query parameters for logging
 * @returns {Promise<Object>} - Raw response data from Platform API
 */
async function queryPlatformAPI (url, params = {}) {
  try {
    logger.debug({ message: 'Platform API request', type: types.DataFetch, url, params })
    const response = await axios.get(url, {
      timeout: 10000,
      headers: {
        'User-Agent': config.checkService.userAgentInternal
      }
    })

    return response.data
  } catch (error) {
    logger.warn({
      message: `queryPlatformAPI(): ${error.message}`,
      type: types.External,
      params,
      platformUrl: config.mainWebsiteUrl,
      url
    })
    throw error
  }
}