/**
 * Workflow Validation Utilities
 *
 * Validiert Workflow-Graphen (Struktur + Logik).
 */

export function validateWorkflowGraph(nodes, edges) {
  const errors = []
  const warnings = []

  // 1. Strukturelle Validierung
  validateStructure(nodes, edges, errors, warnings)

  // 2. Logische Validierung
  validateLogic(nodes, edges, errors, warnings)

  return {
    errors,
    warnings,
    isValid: errors.length === 0
  }
}

/**
 * Strukturelle Validierung (DAG, START/END, Zyklen)
 */
function validateStructure(nodes, edges, errors, warnings) {
  // START Node
  const startNodes = nodes.filter(n => n.type === 'start')
  if (startNodes.length === 0) {
    errors.push({
      type: 'structure',
      message: 'Kein START-Node vorhanden',
      severity: 'error'
    })
  } else if (startNodes.length > 1) {
    errors.push({
      type: 'structure',
      message: `${startNodes.length} START-Nodes gefunden (max. 1 erlaubt)`,
      severity: 'error'
    })
  }

  // END Node
  const endNodes = nodes.filter(n => n.type === 'end')
  if (endNodes.length === 0) {
    errors.push({
      type: 'structure',
      message: 'Kein END-Node vorhanden',
      severity: 'error'
    })
  } else if (endNodes.length > 1) {
    // Backend unterstützt aktuell nur 1 End-Node (aggregate_results nimmt letzten)
    // Future: Multi-Exit Support würde Backend-Anpassung erfordern
    errors.push({
      type: 'structure',
      message: `${endNodes.length} END-Nodes gefunden (max. 1 erlaubt)`,
      severity: 'error'
    })
  }

  // Zyklen-Erkennung
  if (detectCycles(nodes, edges)) {
    errors.push({
      type: 'structure',
      message: 'Workflow enthält Zyklen (nicht erlaubt)',
      severity: 'error'
    })
  }

  // Isolierte Nodes
  nodes.forEach(node => {
    if (node.type === 'start' || node.type === 'end') return

    const hasIncoming = edges.some(e => e.target === node.id)
    const hasOutgoing = edges.some(e => e.source === node.id)

    if (!hasIncoming || !hasOutgoing) {
      warnings.push({
        type: 'isolation',
        message: `Node "${node.data.label}" ist isoliert (keine/fehlende Verbindungen)`,
        nodeId: node.id,
        severity: 'warning'
      })
    }
  })
}

/**
 * Logische Validierung (Node-Konfiguration)
 */
function validateLogic(nodes, edges, errors, warnings) {
  nodes.forEach(node => {
    // Analysis Nodes
    if (node.type === 'analysis') {
      const questions = node.data.questions || []
      const hasPromptSlug = node.data.prompt_slug != null && node.data.prompt_slug !== ''
      const hasInlineTemplate = node.data.inline_template != null && node.data.inline_template.trim() !== ''

      // Part 3: Validation - Entweder prompt_slug ODER inline_template
      if (!hasPromptSlug && !hasInlineTemplate) {
        errors.push({
          type: 'config',
          message: `Analysis-Node "${node.data.label}" benötigt entweder Basis-Prompt oder Inline-Template`,
          nodeId: node.id,
          severity: 'error'
        })
      }

      // Warning wenn beide gesetzt (sollte nicht passieren, aber zur Sicherheit)
      if (hasPromptSlug && hasInlineTemplate) {
        warnings.push({
          type: 'config',
          message: `Analysis-Node "${node.data.label}" hat sowohl Basis-Prompt als auch Inline-Template - Inline hat Vorrang`,
          nodeId: node.id,
          severity: 'warning'
        })
      }

      // Fragen validieren
      questions.forEach((q, idx) => {
        if (!q.question?.trim()) {
          errors.push({
            type: 'config',
            message: `Frage ${idx + 1} in "${node.data.label}" hat keinen Text`,
            nodeId: node.id,
            severity: 'error'
          })
        }

        if (!q.answer_spectrum || q.answer_spectrum.length < 2) {
          errors.push({
            type: 'config',
            message: `Frage ${idx + 1} in "${node.data.label}" braucht mind. 2 Antworten`,
            nodeId: node.id,
            severity: 'error'
          })
        }
      })
    }

    // Logic Nodes
    if (node.type === 'logic') {
      const condition = node.data.condition

      if (!condition || !condition.operator) {
        errors.push({
          type: 'config',
          message: `Logic-Node "${node.data.label}" hat keine Bedingung`,
          nodeId: node.id,
          severity: 'error'
        })
      } else {
        // Bedingung vollständig?
        const incomplete = findIncompleteConditions(condition)
        if (incomplete.length > 0) {
          errors.push({
            type: 'config',
            message: `Logic-Node "${node.data.label}" hat ${incomplete.length} unvollständige Bedingung(en)`,
            nodeId: node.id,
            severity: 'error'
          })
        }
      }

      // Mind. 2 Outgoing Edges (true/false Pfade)
      const outgoing = edges.filter(e => e.source === node.id)
      if (outgoing.length < 2) {
        warnings.push({
          type: 'config',
          message: `Logic-Node "${node.data.label}" hat nur ${outgoing.length} Ausgang (sollte mind. 2 haben)`,
          nodeId: node.id,
          severity: 'warning'
        })
      }
    }

    // Join Nodes
    if (node.type === 'join') {
      const incoming = edges.filter(e => e.target === node.id)

      if (incoming.length < 2) {
        warnings.push({
          type: 'config',
          message: `Join-Node "${node.data.label}" hat nur ${incoming.length} eingehende Kante (sollte mind. 2 haben)`,
          nodeId: node.id,
          severity: 'warning'
        })
      }
    }
  })
}

/**
 * Zyklen-Erkennung (DFS-basiert)
 */
function detectCycles(nodes, edges) {
  const visited = new Set()
  const recStack = new Set()

  function dfs(nodeId) {
    visited.add(nodeId)
    recStack.add(nodeId)

    const outgoing = edges.filter(e => e.source === nodeId)
    for (const edge of outgoing) {
      if (!visited.has(edge.target)) {
        if (dfs(edge.target)) return true
      } else if (recStack.has(edge.target)) {
        return true // Cycle detected
      }
    }

    recStack.delete(nodeId)
    return false
  }

  for (const node of nodes) {
    if (!visited.has(node.id)) {
      if (dfs(node.id)) return true
    }
  }

  return false
}

/**
 * Unvollständige Bedingungen finden (rekursiv)
 */
function findIncompleteConditions(condition) {
  const incomplete = []

  // Verschachtelte Gruppe?
  if (condition.operands && Array.isArray(condition.operands)) {
    for (const op of condition.operands) {
      incomplete.push(...findIncompleteConditions(op))
    }
  } else {
    // Einfache Bedingung: ref, operator, value müssen gesetzt sein
    if (!condition.ref || !condition.operator || condition.value === undefined || condition.value === '') {
      incomplete.push(condition)
    }
  }

  return incomplete
}
