import { EntityManager } from 'typeorm'
import { Domain, ListParam } from '@things-factory/shell'

export const pgUdfQuery = {
  async pgUdfQuery(_: any, params: ListParam, context: any) {
    const { domain, tx }: { domain: Domain; tx: EntityManager } = context.state
    let udfName = params.filters.find(param => param.name === 'udf')
    let domainParam = params.filters.find(param => param.name === 'domain_id')

    try {
      if (!udfName?.value) {
        throw new Error('UDF name is required')
      }

      if (!domainParam?.value) {
        params.filters.push({ name: 'domain_id', operator: 'eq', value: domain.id })
      }

      // Remove the udf filter and convert remaining filters to an object
      const parameters = params.filters
        .filter(f => f.name !== 'udf')
        .map(filter => ({
          [filter.name]: filter.value
        }))

      return { items: JSON.stringify(await callPostgresFunction(udfName.value, tx, parameters)) }
    } catch (error) {
      throw error
    }
  }
}

/**
 * Calls a PostgreSQL user-defined function through TypeORM
 *
 * @param functionName - Name of the PostgreSQL UDF to call
 * @param entityManager - EntityManager instance
 * @param parameters - Array of parameters or string to pass to the function
 * @returns Promise with the function result
 */
async function callPostgresFunction(functionName: string, entityManager: EntityManager, parameters: any[] | string) {
  try {
    const exists = await entityManager.query(
      `
    SELECT EXISTS (
      SELECT 1 FROM pg_proc p
      JOIN pg_namespace n ON p.pronamespace = n.oid
      WHERE p.proname = $1
      AND n.nspname = 'public'
    )`,
      [functionName]
    )

    if (!exists[0].exists) {
      throw new Error(`PostgreSQL function '${functionName}' does not exist in the database.`)
    }

    let query: string, params: any[]

    if (typeof parameters === 'string') {
      // Pass string parameter directly
      query = `SELECT * FROM ${functionName}($1)`
      params = [parameters]
    } else {
      // Handle array of parameters
      const paramPlaceholders = parameters.map((_, index) => `$${index + 1}`).join(', ')
      query = `SELECT * FROM ${functionName}(${paramPlaceholders})`
      params = parameters.map(param => Object.values(param)).flat()
    }

    // Execute the query with the provided parameters
    const result = await entityManager.query(query, params)

    // PostgreSQL functions typically return rows, so we handle both single and multiple results
    return Array.isArray(result) && result.length === 1 ? result[0] : result
  } catch (error) {
    throw new Error(`Error calling PostgreSQL function '${functionName}': ${error.message}`)
  }
}
