import type { Diagnostic } from '@codemirror/lint'
import type { EditorView } from '@codemirror/view'
import type { FunctionSchema } from '@pawel-up/jexl/schemas/types.js'

export function functionLinter(
  view: EditorView,
  functionSchemas: readonly FunctionSchema[],
  dispatchEvent: (e: CustomEvent) => void
): readonly Diagnostic[] {
  const diagnostics: Diagnostic[] = []
  const doc = view.state.doc
  const text = doc.toString()

  const functions = functionSchemas
  if (!functions || functions.length === 0) {
    return diagnostics
  }

  // Regular expression to match function calls like functionName(args)
  const functionCallRegex = /(\w+)\s*\(/g
  let match: RegExpExecArray | null

  while ((match = functionCallRegex.exec(text)) !== null) {
    const functionName = match[1]
    const startPos = match.index
    const endPos = startPos + functionName.length

    // Check if this function exists in our schemas
    const fn = functions.find((schema) => schema.name === functionName)

    if (!fn) {
      diagnostics.push({
        from: startPos,
        to: endPos,
        severity: 'error',
        message: `Unknown function "${functionName}". Make sure you have the correct syntax for the function call.`,
        actions: [
          {
            name: 'View available functions',
            apply: () => {
              dispatchEvent(
                new CustomEvent('show-available-functions', {
                  detail: { availableFunctions: [...functions] },
                  bubbles: true,
                })
              )
            },
          },
        ],
      })
    } else {
      // Extract the function call content to validate parameters
      const functionCallMatch = text.substring(startPos).match(/(\w+)\s*\(([^)]*)\)/)
      if (functionCallMatch && fn.parameters && fn.parameters.length > 0) {
        const argsString = functionCallMatch[2].trim()
        const args = argsString ? argsString.split(',').map((arg) => arg.trim()) : []

        // Check required parameters
        const requiredParams = fn.parameters.filter((p) => p.required)
        if (args.length < requiredParams.length) {
          const functionEndPos = startPos + functionCallMatch[0].length
          diagnostics.push({
            from: startPos,
            to: functionEndPos,
            severity: 'error',
            message: `Function "${functionName}" requires ${requiredParams.length} parameters but got ${args.length}. Required: ${requiredParams.map((p) => p.name).join(', ')}`,
          })
        }

        // Check if too many parameters
        const hasVariadicParam = fn.parameters.length > 0 && !!fn.parameters[fn.parameters.length - 1].variadic
        if (!hasVariadicParam && args.length > fn.parameters.length) {
          const functionEndPos = startPos + functionCallMatch[0].length
          diagnostics.push({
            from: startPos,
            to: functionEndPos,
            severity: 'warning',
            message: `Function "${functionName}" expects at most ${fn.parameters.length} parameters, but got ${args.length}.`,
          })
        }
      }
    }
  }

  return diagnostics
}
