import type { Endpoint, Where } from 'payload'
import type { FormSubmission } from '@/payload-types'

type SubmissionDataEntry = NonNullable<FormSubmission['submissionData']>[number]

const escapeCSV = (value: unknown): string => {
  if (value === null || value === undefined) return ''

  const stringValue = String(value)

  if (stringValue.includes(',') || stringValue.includes('\n') || stringValue.includes('"')) {
    return `"${stringValue.replace(/"/g, '""')}"`
  }

  return stringValue
}

const flattenSubmissionData = (
  submissionData: FormSubmission['submissionData'],
): Map<string, string> => {
  const flattened = new Map<string, string>()

  if (!Array.isArray(submissionData)) return flattened

  for (const entry of submissionData as SubmissionDataEntry[]) {
    if (!entry?.field) continue
    flattened.set(entry.field, entry.value ?? '')
  }

  return flattened
}

const discoverFieldNames = (submissions: FormSubmission[]): string[] => {
  const fieldNames = new Set<string>()

  for (const submission of submissions) {
    for (const fieldName of flattenSubmissionData(submission.submissionData).keys()) {
      fieldNames.add(fieldName)
    }
  }

  return [...fieldNames].sort((a, b) => a.localeCompare(b))
}

const createHeaderRow = (fieldNames: string[]): string => {
  return ['ID', 'First Name', 'Last Name', 'Email', ...fieldNames, 'Submitted At']
    .map(escapeCSV)
    .join(',')
}

const createDataRow = (submission: FormSubmission, fieldNames: string[]): string => {
  const flattened = flattenSubmissionData(submission.submissionData)

  return [
    submission.id,
    submission.firstName ?? '',
    submission.lastName ?? '',
    submission.email ?? '',
    ...fieldNames.map((fieldName) => flattened.get(fieldName) ?? ''),
    submission.createdAt,
  ]
    .map(escapeCSV)
    .join(',')
}

const parseWhere = (rawWhere: string | null): Where | undefined => {
  if (!rawWhere) return undefined

  try {
    return JSON.parse(rawWhere) as Where
  } catch {
    return undefined
  }
}

const getSearchParam = (req: Parameters<NonNullable<Endpoint['handler']>>[0], key: string) => {
  const url = req.url ? new URL(req.url) : null
  return url?.searchParams.get(key) ?? null
}

export const formSubmissionsExportEndpoint: Endpoint = {
  path: '/form-submissions-export',
  method: 'get',
  handler: async (req) => {
    if (!req.user) {
      return Response.json({ error: 'Unauthorized' }, { status: 401 })
    }

    try {
      const where = parseWhere(getSearchParam(req, 'where'))
      const allSubmissions: FormSubmission[] = []
      let page = 1
      let totalPages = 1

      while (page <= totalPages) {
        const result = await req.payload.find({
          collection: 'form-submissions',
          depth: 0,
          page,
          overrideAccess: false,
          req,
          user: req.user,
          where,
        })

        allSubmissions.push(...result.docs)
        totalPages = result.totalPages
        page += 1
      }

      const fieldNames = discoverFieldNames(allSubmissions)
      const csv = [
        createHeaderRow(fieldNames),
        ...allSubmissions.map((submission) => createDataRow(submission, fieldNames)),
      ].join('\n')

      const timestamp = new Date().toISOString().slice(0, 10)

      return new Response(csv, {
        status: 200,
        headers: {
          'Content-Disposition': `attachment; filename="form-submissions-${timestamp}.csv"`,
          'Content-Type': 'text/csv; charset=utf-8',
        },
      })
    } catch (error) {
      req.payload.logger.error({
        err: error,
        msg: 'Failed to export form submissions',
      })

      return Response.json({ error: 'Failed to export submissions' }, { status: 500 })
    }
  },
}
