#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
from datetime import datetime, timedelta
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, session, g
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
import json
from enum import Enum
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, TextAreaField, SelectField, HiddenField
from wtforms.validators import DataRequired, Email, Length, EqualTo, ValidationError
from functools import wraps
import secrets
from sqlalchemy.sql import func
from openai import OpenAI
from dotenv import load_dotenv

# Modelle importieren
from models import (
    db, User, Thought, Comment, MindMapNode, ThoughtRelation, ThoughtRating, 
    RelationType, Category, UserMindmap, UserMindmapNode, MindmapNote, 
    node_thought_association, user_thought_bookmark
)

# Lade .env-Datei
load_dotenv()  # force=True erzwingt die Synchronisierung

# Bestimme den absoluten Pfad zur Datenbank
basedir = os.path.abspath(os.path.dirname(__file__))
db_path = os.path.join(basedir, 'database', 'systades.db')
# Stellen Sie sicher, dass das Verzeichnis existiert
os.makedirs(os.path.dirname(db_path), exist_ok=True)

app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'default-dev-key')
app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{db_path}'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=365)  # Langlebige Session für Dark Mode-Einstellung

# OpenAI API-Konfiguration
api_key = os.environ.get("OPENAI_API_KEY")
if not api_key:
    print("WARNUNG: Kein OPENAI_API_KEY in Umgebungsvariablen gefunden. KI-Funktionalität wird nicht verfügbar sein.")
    api_key = "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"

client = OpenAI(api_key=api_key)

# Dark Mode Einstellung in Session speichern
@app.before_request
def handle_dark_mode():
    if 'dark_mode' not in session:
        session['dark_mode'] = False  # Standardmäßig Light Mode

# Context processor für Dark Mode
@app.context_processor
def inject_dark_mode():
    return {'dark_mode': session.get('dark_mode', False)}

# Route zum Umschalten des Dark Mode
@app.route('/toggle-dark-mode', methods=['POST'])
def toggle_dark_mode():
    session['dark_mode'] = not session.get('dark_mode', False)
    return jsonify({'success': True, 'dark_mode': session['dark_mode']})

# Context processor für globale Template-Variablen
@app.context_processor
def inject_globals():
    """Inject global variables into all templates."""
    return {
        'current_year': datetime.now().year
    }

# Context-Prozessor für alle Templates
@app.context_processor
def inject_current_year():
    return {'current_year': datetime.now().year}

# Initialisiere die Datenbank
db.init_app(app)

# Initialisiere den Login-Manager
login_manager = LoginManager(app)
login_manager.login_view = 'login'

# Erst nach der App-Initialisierung die DB-Check-Funktionen importieren
from utils.db_check import check_db_connection, initialize_db_if_needed

def create_default_categories():
    """Erstellt die Standardkategorien für die Mindmap"""
    # Hauptkategorien
    main_categories = [
        {
            "name": "Philosophie",
            "description": "Philosophisches Denken und Konzepte",
            "color_code": "#9F7AEA",
            "icon": "fa-brain",
            "subcategories": [
                {"name": "Ethik", "description": "Moralische Grundsätze", "icon": "fa-balance-scale"},
                {"name": "Logik", "description": "Gesetze des Denkens", "icon": "fa-project-diagram"},
                {"name": "Erkenntnistheorie", "description": "Natur des Wissens", "icon": "fa-lightbulb"}
            ]
        },
        {
            "name": "Wissenschaft",
            "description": "Wissenschaftliche Disziplinen und Forschung",
            "color_code": "#48BB78",
            "icon": "fa-flask",
            "subcategories": [
                {"name": "Physik", "description": "Gesetze der Materie und Energie", "icon": "fa-atom"},
                {"name": "Biologie", "description": "Wissenschaft des Lebens", "icon": "fa-dna"},
                {"name": "Mathematik", "description": "Abstrakte Strukturen", "icon": "fa-calculator"},
                {"name": "Informatik", "description": "Wissenschaft der Datenverarbeitung", "icon": "fa-laptop-code"}
            ]
        },
        {
            "name": "Technologie",
            "description": "Technologische Entwicklungen und Anwendungen",
            "color_code": "#ED8936",
            "icon": "fa-microchip",
            "subcategories": [
                {"name": "Künstliche Intelligenz", "description": "Intelligente Maschinen", "icon": "fa-robot"},
                {"name": "Programmierung", "description": "Softwareentwicklung", "icon": "fa-code"},
                {"name": "Elektronik", "description": "Elektronische Systeme", "icon": "fa-memory"}
            ]
        },
        {
            "name": "Künste",
            "description": "Kunstformen und kulturelle Ausdrucksweisen",
            "color_code": "#ED64A6",
            "icon": "fa-palette",
            "subcategories": [
                {"name": "Literatur", "description": "Schriftliche Werke", "icon": "fa-book"},
                {"name": "Musik", "description": "Klangkunst", "icon": "fa-music"},
                {"name": "Bildende Kunst", "description": "Visuelle Kunstformen", "icon": "fa-paint-brush"}
            ]
        },
        {
            "name": "Psychologie",
            "description": "Menschliches Verhalten und Geist",
            "color_code": "#4299E1",
            "icon": "fa-comments",
            "subcategories": [
                {"name": "Kognition", "description": "Denken und Wahrnehmen", "icon": "fa-brain"},
                {"name": "Emotionen", "description": "Gefühlswelt", "icon": "fa-heart"},
                {"name": "Persönlichkeit", "description": "Charaktereigenschaften", "icon": "fa-user"}
            ]
        }
    ]
    
    # Kategorien erstellen
    for main_cat_data in main_categories:
        # Prüfen, ob die Kategorie bereits existiert
        existing_cat = Category.query.filter_by(name=main_cat_data["name"]).first()
        if existing_cat:
            continue
            
        # Hauptkategorie erstellen
        main_category = Category(
            name=main_cat_data["name"],
            description=main_cat_data["description"],
            color_code=main_cat_data["color_code"],
            icon=main_cat_data["icon"]
        )
        db.session.add(main_category)
        db.session.flush()  # Um die ID zu generieren
        
        # Unterkategorien erstellen
        for sub_cat_data in main_cat_data.get("subcategories", []):
            sub_category = Category(
                name=sub_cat_data["name"],
                description=sub_cat_data["description"],
                color_code=main_cat_data["color_code"],
                icon=sub_cat_data.get("icon", main_cat_data["icon"]),
                parent_id=main_category.id
            )
            db.session.add(sub_category)
    
    db.session.commit()
    print("Standard-Kategorien wurden erstellt!")

def initialize_database():
    """Initialisiert die Datenbank mit Grunddaten, falls diese leer ist"""
    try:
        print("Initialisiere die Datenbank...")
        
        # Erstelle alle Tabellen
        db.create_all()
        
        # Prüfe, ob bereits Benutzer existieren
        if User.query.count() == 0:
            print("Erstelle Admin-Benutzer...")
            admin = User(
                username="admin",
                email="admin@example.com",
                is_admin=True
            )
            admin.set_password("admin123")  # In echter Umgebung ein sicheres Passwort verwenden!
            db.session.add(admin)
        
        # Prüfe, ob bereits Kategorien existieren
        if Category.query.count() == 0:
            print("Erstelle Standard-Kategorien...")
            create_default_categories()
        
        # Stelle sicher, dass die Standard-Knoten für die öffentliche Mindmap existieren
        if MindMapNode.query.count() == 0:
            print("Erstelle Standard-Knoten für die Mindmap...")
            
            # Hauptknoten: Wissen
            root_node = MindMapNode(
                name="Wissen",
                description="Zentrale Wissensbasis",
                color_code="#4299E1",
                is_public=True
            )
            db.session.add(root_node)
            db.session.flush()  # Um die ID zu generieren
            
            # Verwandte Kategorien finden
            philosophy = Category.query.filter_by(name="Philosophie").first()
            science = Category.query.filter_by(name="Wissenschaft").first()
            technology = Category.query.filter_by(name="Technologie").first()
            arts = Category.query.filter_by(name="Künste").first()
            
            # Erstelle Hauptthemenknoten
            nodes = [
                MindMapNode(
                    name="Philosophie", 
                    description="Philosophisches Denken",
                    color_code="#9F7AEA",
                    category=philosophy,
                    is_public=True
                ),
                MindMapNode(
                    name="Wissenschaft", 
                    description="Wissenschaftliche Erkenntnisse",
                    color_code="#48BB78",
                    category=science,
                    is_public=True
                ),
                MindMapNode(
                    name="Technologie", 
                    description="Technologische Entwicklungen",
                    color_code="#ED8936",
                    category=technology,
                    is_public=True
                ),
                MindMapNode(
                    name="Künste", 
                    description="Künstlerische Ausdrucksformen",
                    color_code="#ED64A6",
                    category=arts,
                    is_public=True
                )
            ]
            
            # Füge Knoten zur Datenbank hinzu
            for node in nodes:
                db.session.add(node)
            
            db.session.commit()
            
            # Nachdem wir die IDs haben, füge die Verbindungen hinzu
            all_nodes = MindMapNode.query.all()
            root = MindMapNode.query.filter_by(name="Wissen").first()
            
            if root:
                for node in all_nodes:
                    if node.id != root.id:
                        root.children.append(node)
            
            # Speichere die Änderungen
            db.session.commit()
            
        print("Datenbankinitialisierung abgeschlossen.")
    except Exception as e:
        print(f"Fehler bei der Datenbankinitialisierung: {str(e)}")
        db.session.rollback()
        raise

# Instead of before_first_request, which is deprecated in newer Flask versions
# Use a function to initialize the database that will be called during app creation
def init_app_database(app_instance):
    """Initialisiert die Datenbank für die Flask-App"""
    with app_instance.app_context():
        # Überprüfe und initialisiere die Datenbank bei Bedarf
        if not initialize_db_if_needed(db, initialize_database):
            print("WARNUNG: Datenbankinitialisierung fehlgeschlagen. Einige Funktionen könnten eingeschränkt sein.")

# Call the function to initialize the database
init_app_database(app)

# Benutzerdefinierter Decorator für Admin-Zugriff
def admin_required(f):
    @wraps(f)
    @login_required
    def decorated_function(*args, **kwargs):
        if not current_user.is_admin:
            flash('Zugriff verweigert. Nur Administratoren dürfen diese Seite aufrufen.', 'error')
            return redirect(url_for('index'))
        return f(*args, **kwargs)
    return decorated_function

@login_manager.user_loader
def load_user(id):
    return User.query.get(int(id))

# Routes for authentication
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        
        user = User.query.filter_by(username=username).first()
        if user and user.check_password(password):
            login_user(user)
            # Aktualisiere letzten Login-Zeitpunkt
            user.last_login = datetime.utcnow()
            db.session.commit()
            
            next_page = request.args.get('next')
            return redirect(next_page or url_for('index'))
        flash('Ungültiger Benutzername oder Passwort')
    return render_template('login.html')

@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        username = request.form.get('username')
        email = request.form.get('email')
        password = request.form.get('password')
        
        if User.query.filter_by(username=username).first():
            flash('Benutzername existiert bereits')
            return redirect(url_for('register'))
        
        if User.query.filter_by(email=email).first():
            flash('E-Mail ist bereits registriert')
            return redirect(url_for('register'))
        
        user = User(username=username, email=email)
        user.set_password(password)
        db.session.add(user)
        
        # Erstelle eine Standard-Mindmap für den neuen Benutzer
        default_mindmap = UserMindmap(
            name='Meine Mindmap',
            description='Meine persönliche Wissenslandschaft',
            user=user
        )
        db.session.add(default_mindmap)
        db.session.commit()
        
        login_user(user)
        flash('Dein Konto wurde erfolgreich erstellt!', 'success')
        return redirect(url_for('index'))
    return render_template('register.html')

@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('index'))

# Route for the homepage
@app.route('/')
def index():
    return render_template('index.html')

# Route for the mindmap page
@app.route('/mindmap')
def mindmap():
    """Zeigt die öffentliche Mindmap an."""
    try:
        # Sicherstellen, dass wir Kategorien haben
        if Category.query.count() == 0:
            create_default_categories()
        
        # Hole alle Kategorien der obersten Ebene
        categories = Category.query.filter_by(parent_id=None).all()
        
        # Transformiere Kategorien in ein anzeigbares Format für die Vorlage
        category_tree = [build_category_tree(cat) for cat in categories]
        
        return render_template('mindmap.html', categories=category_tree)
    except Exception as e:
        # Bei Fehler leere Kategorienliste übergeben und Fehler protokollieren
        print(f"Fehler beim Laden der Mindmap-Kategorien: {str(e)}")
        return render_template('mindmap.html', categories=[], error=str(e))

# Route for user profile
@app.route('/profile')
@login_required
def profile():
    # Lade Benutzer-Mindmaps
    user_mindmaps = UserMindmap.query.filter_by(user_id=current_user.id).all()
    
    # Lade Statistiken
    thought_count = Thought.query.filter_by(user_id=current_user.id).count()
    bookmark_count = db.session.query(user_thought_bookmark).filter(
        user_thought_bookmark.c.user_id == current_user.id).count()
    
    # Berechne tatsächliche Werte für Benutzerstatistiken
    contributions_count = Comment.query.filter_by(user_id=current_user.id).count()
    
    # Berechne Verbindungen (Anzahl der Gedankenverknüpfungen)
    connections_count = ThoughtRelation.query.filter(
        (ThoughtRelation.source_id.in_(
            db.session.query(Thought.id).filter_by(user_id=current_user.id)
        )) | 
        (ThoughtRelation.target_id.in_(
            db.session.query(Thought.id).filter_by(user_id=current_user.id)
        ))
    ).count()
    
    # Berechne durchschnittliche Bewertung der Gedanken des Benutzers
    avg_rating = db.session.query(func.avg(ThoughtRating.relevance_score)).join(
        Thought, Thought.id == ThoughtRating.thought_id
    ).filter(Thought.user_id == current_user.id).scalar() or 0
    
    # Hole die Anzahl der Follower (falls implementiert)
    # In diesem Beispiel nehmen wir an, dass es keine Follower-Funktionalität gibt
    followers_count = 0
    
    # Hole den Standort des Benutzers aus der Datenbank, falls vorhanden
    location = "Deutschland"  # Standardwert
    
    return render_template('profile.html', 
                          user=current_user,
                          user_mindmaps=user_mindmaps, 
                          thought_count=thought_count,
                          bookmark_count=bookmark_count,
                          connections_count=connections_count,
                          contributions_count=contributions_count,
                          followers_count=followers_count,
                          rating=round(avg_rating, 1),
                          location=location)

# Route für Benutzereinstellungen
@app.route('/settings', methods=['GET', 'POST'])
@login_required
def settings():
    if request.method == 'POST':
        action = request.form.get('action')
        
        if action == 'update_profile':
            current_user.bio = request.form.get('bio')
            
            # Update avatar if provided
            avatar_url = request.form.get('avatar_url')
            if avatar_url:
                current_user.avatar = avatar_url
                
            db.session.commit()
            flash('Profil erfolgreich aktualisiert!', 'success')
            
        elif action == 'update_password':
            current_password = request.form.get('current_password')
            new_password = request.form.get('new_password')
            confirm_password = request.form.get('confirm_password')
            
            if not current_user.check_password(current_password):
                flash('Aktuelles Passwort ist nicht korrekt', 'error')
            elif new_password != confirm_password:
                flash('Neue Passwörter stimmen nicht überein', 'error')
            else:
                current_user.set_password(new_password)
                db.session.commit()
                flash('Passwort erfolgreich aktualisiert!', 'success')
        
        return redirect(url_for('settings'))
        
    return render_template('settings.html')

# API-Endpunkt für Flash-Nachrichten
@app.route('/api/get_flash_messages')
def get_flash_messages():
    """Liefert aktuelle Flash-Nachrichten für API/JS-Clients."""
    # Hole alle gespeicherten Flash-Nachrichten
    messages = []
    flashed_messages = session.get('_flashes', [])
    
    # Formatierung der Nachrichten für die API-Antwort
    for category, message in flashed_messages:
        messages.append({
            'category': category,
            'message': message
        })
    
    # Lösche die Nachrichten aus der Session, nachdem sie abgerufen wurden
    session.pop('_flashes', None)
    
    return jsonify(messages)

# Routes für rechtliche Seiten
@app.route('/impressum/')
def impressum():
    return render_template('impressum.html')

@app.route('/datenschutz/')
def datenschutz():
    return render_template('datenschutz.html')

@app.route('/agb/')
def agb():
    return render_template('agb.html')

# Benutzer-Mindmap-Funktionalität
@app.route('/my-mindmap/<int:mindmap_id>')
@login_required
def user_mindmap(mindmap_id):
    mindmap = UserMindmap.query.get_or_404(mindmap_id)
    
    # Sicherheitscheck: Nur eigene Mindmaps oder öffentliche Mindmaps
    if mindmap.user_id != current_user.id and mindmap.is_private:
        flash("Du hast keinen Zugriff auf diese Mindmap.", "error")
        return redirect(url_for('profile'))
    
    return render_template('user_mindmap.html', mindmap=mindmap)

@app.route('/mindmap/create', methods=['GET', 'POST'])
@login_required
def create_mindmap():
    if request.method == 'POST':
        name = request.form.get('name')
        description = request.form.get('description')
        is_private = request.form.get('is_private') == 'on'
        
        new_mindmap = UserMindmap(
            name=name,
            description=description,
            user_id=current_user.id,
            is_private=is_private
        )
        
        db.session.add(new_mindmap)
        db.session.commit()
        
        flash('Neue Mindmap erfolgreich erstellt!', 'success')
        return redirect(url_for('user_mindmap', mindmap_id=new_mindmap.id))
    
    return render_template('create_mindmap.html')

@app.route('/mindmap/<int:mindmap_id>/edit', methods=['GET', 'POST'])
@login_required
def edit_mindmap(mindmap_id):
    mindmap = UserMindmap.query.get_or_404(mindmap_id)
    
    # Sicherheitscheck
    if mindmap.user_id != current_user.id:
        flash("Du kannst nur deine eigenen Mindmaps bearbeiten.", "error")
        return redirect(url_for('profile'))
    
    if request.method == 'POST':
        mindmap.name = request.form.get('name')
        mindmap.description = request.form.get('description')
        mindmap.is_private = request.form.get('is_private') == 'on'
        
        db.session.commit()
        flash('Mindmap erfolgreich aktualisiert!', 'success')
        return redirect(url_for('user_mindmap', mindmap_id=mindmap.id))
    
    return render_template('edit_mindmap.html', mindmap=mindmap)

@app.route('/mindmap/<int:mindmap_id>/delete', methods=['POST'])
@login_required
def delete_mindmap(mindmap_id):
    mindmap = UserMindmap.query.get_or_404(mindmap_id)
    
    # Sicherheitscheck
    if mindmap.user_id != current_user.id:
        flash("Du kannst nur deine eigenen Mindmaps löschen.", "error")
        return redirect(url_for('profile'))
    
    db.session.delete(mindmap)
    db.session.commit()
    
    flash('Mindmap erfolgreich gelöscht!', 'success')
    return redirect(url_for('profile'))

# API-Endpunkte für Mindmap-Daten
@app.route('/api/mindmap/public')
def get_public_mindmap():
    """Liefert die öffentliche Mindmap-Struktur."""
    # Hole alle Kategorien der obersten Ebene
    root_categories = Category.query.filter_by(parent_id=None).all()
    
    # Baue Baumstruktur auf
    result = []
    for category in root_categories:
        result.append(build_category_tree(category))
    
    return jsonify(result)

def build_category_tree(category):
    """
    Erstellt eine Baumstruktur für eine Kategorie mit all ihren Unterkategorien
    und dazugehörigen Knoten
    
    Args:
        category: Ein Category-Objekt
        
    Returns:
        dict: Eine JSON-serialisierbare Darstellung der Kategoriestruktur
    """
    # Kategorie-Basisinformationen
    category_dict = {
        'id': category.id,
        'name': category.name,
        'description': category.description,
        'color_code': category.color_code,
        'icon': category.icon,
        'nodes': [],
        'children': []
    }
    
    # Knoten zur Kategorie hinzufügen
    if category.nodes:
        for node in category.nodes:
            category_dict['nodes'].append({
                'id': node.id,
                'name': node.name,
                'description': node.description or '',
                'color_code': node.color_code or '#9F7AEA',
                'thought_count': len(node.thoughts) if hasattr(node, 'thoughts') else 0
            })
    
    # Rekursiv Unterkategorien hinzufügen
    if category.children:
        for child in category.children:
            category_dict['children'].append(build_category_tree(child))
    
    return category_dict

@app.route('/api/mindmap/user/<int:mindmap_id>')
@login_required
def get_user_mindmap(mindmap_id):
    """Liefert die benutzerdefinierte Mindmap-Struktur."""
    mindmap = UserMindmap.query.get_or_404(mindmap_id)
    
    # Sicherheitscheck
    if mindmap.user_id != current_user.id and mindmap.is_private:
        return jsonify({'error': 'Nicht autorisiert'}), 403
    
    # Hole alle verknüpften Knoten mit Position
    nodes_data = db.session.query(
        MindMapNode, UserMindmapNode
    ).join(
        UserMindmapNode, UserMindmapNode.node_id == MindMapNode.id
    ).filter(
        UserMindmapNode.user_mindmap_id == mindmap_id
    ).all()
    
    # Knoten formatieren
    nodes = []
    for node, user_node in nodes_data:
        nodes.append({
            'id': node.id,
            'name': node.name,
            'description': node.description,
            'color_code': node.color_code,
            'x': user_node.x_position,
            'y': user_node.y_position,
            'scale': user_node.scale,
            'thought_count': len(node.thoughts)
        })
    
    # Hole Notizen zu dieser Mindmap
    notes = MindmapNote.query.filter_by(
        mindmap_id=mindmap_id, 
        user_id=current_user.id
    ).all()
    
    notes_data = [{
        'id': note.id,
        'content': note.content,
        'node_id': note.node_id,
        'thought_id': note.thought_id,
        'color_code': note.color_code,
        'created_at': note.created_at.isoformat()
    } for note in notes]
    
    return jsonify({
        'id': mindmap.id,
        'name': mindmap.name,
        'description': mindmap.description,
        'is_private': mindmap.is_private,
        'nodes': nodes,
        'notes': notes_data
    })

@app.route('/api/mindmap/<int:mindmap_id>/add_node', methods=['POST'])
@login_required
def add_node_to_mindmap(mindmap_id):
    """Fügt einen öffentlichen Knoten zur Benutzer-Mindmap hinzu."""
    data = request.json
    mindmap = UserMindmap.query.get_or_404(mindmap_id)
    
    # Sicherheitscheck
    if mindmap.user_id != current_user.id:
        return jsonify({'error': 'Nicht autorisiert'}), 403
    
    node_id = data.get('node_id')
    x_pos = data.get('x', 0)
    y_pos = data.get('y', 0)
    
    node = MindMapNode.query.get_or_404(node_id)
    
    # Prüfen, ob der Knoten bereits in der Mindmap existiert
    existing = UserMindmapNode.query.filter_by(
        user_mindmap_id=mindmap_id,
        node_id=node_id
    ).first()
    
    if existing:
        # Update Position
        existing.x_position = x_pos
        existing.y_position = y_pos
    else:
        # Neuen Knoten hinzufügen
        user_node = UserMindmapNode(
            user_mindmap_id=mindmap_id,
            node_id=node_id,
            x_position=x_pos,
            y_position=y_pos
        )
        db.session.add(user_node)
    
    db.session.commit()
    
    return jsonify({
        'success': True,
        'node_id': node_id,
        'x': x_pos,
        'y': y_pos
    })

@app.route('/api/mindmap/<int:mindmap_id>/remove_node/<int:node_id>', methods=['DELETE'])
@login_required
def remove_node_from_mindmap(mindmap_id, node_id):
    """Entfernt einen Knoten aus der Benutzer-Mindmap."""
    mindmap = UserMindmap.query.get_or_404(mindmap_id)
    
    # Sicherheitscheck
    if mindmap.user_id != current_user.id:
        return jsonify({'error': 'Nicht autorisiert'}), 403
    
    user_node = UserMindmapNode.query.filter_by(
        user_mindmap_id=mindmap_id,
        node_id=node_id
    ).first_or_404()
    
    db.session.delete(user_node)
    db.session.commit()
    
    return jsonify({'success': True})

@app.route('/api/mindmap/<int:mindmap_id>/update_node_position', methods=['POST'])
@login_required
def update_node_position(mindmap_id):
    """Aktualisiert die Position eines Knotens in der Benutzer-Mindmap."""
    data = request.json
    mindmap = UserMindmap.query.get_or_404(mindmap_id)
    
    # Sicherheitscheck
    if mindmap.user_id != current_user.id:
        return jsonify({'error': 'Nicht autorisiert'}), 403
    
    node_id = data.get('node_id')
    x_pos = data.get('x')
    y_pos = data.get('y')
    scale = data.get('scale')
    
    user_node = UserMindmapNode.query.filter_by(
        user_mindmap_id=mindmap_id,
        node_id=node_id
    ).first_or_404()
    
    if x_pos is not None:
        user_node.x_position = x_pos
    if y_pos is not None:
        user_node.y_position = y_pos
    if scale is not None:
        user_node.scale = scale
    
    db.session.commit()
    
    return jsonify({'success': True})

# Notizen-Funktionalität
@app.route('/api/mindmap/<int:mindmap_id>/notes', methods=['GET'])
@login_required
def get_mindmap_notes(mindmap_id):
    """Liefert alle Notizen zu einer Mindmap."""
    mindmap = UserMindmap.query.get_or_404(mindmap_id)
    
    # Sicherheitscheck
    if mindmap.user_id != current_user.id:
        return jsonify({'error': 'Nicht autorisiert'}), 403
    
    notes = MindmapNote.query.filter_by(
        mindmap_id=mindmap_id, 
        user_id=current_user.id
    ).all()
    
    return jsonify([{
        'id': note.id,
        'content': note.content,
        'node_id': note.node_id,
        'thought_id': note.thought_id,
        'color_code': note.color_code,
        'created_at': note.created_at.isoformat(),
        'last_modified': note.last_modified.isoformat()
    } for note in notes])

@app.route('/api/mindmap/<int:mindmap_id>/notes', methods=['POST'])
@login_required
def add_mindmap_note(mindmap_id):
    """Fügt eine neue Notiz zur Mindmap hinzu."""
    data = request.json
    mindmap = UserMindmap.query.get_or_404(mindmap_id)
    
    # Sicherheitscheck
    if mindmap.user_id != current_user.id:
        return jsonify({'error': 'Nicht autorisiert'}), 403
    
    content = data.get('content')
    node_id = data.get('node_id')
    thought_id = data.get('thought_id')
    color_code = data.get('color_code', "#FFF59D")  # Gelber Standard
    
    note = MindmapNote(
        user_id=current_user.id,
        mindmap_id=mindmap_id,
        node_id=node_id,
        thought_id=thought_id,
        content=content,
        color_code=color_code
    )
    
    db.session.add(note)
    db.session.commit()
    
    return jsonify({
        'id': note.id,
        'content': note.content,
        'node_id': note.node_id,
        'thought_id': note.thought_id,
        'color_code': note.color_code,
        'created_at': note.created_at.isoformat(),
        'last_modified': note.last_modified.isoformat()
    })

@app.route('/api/notes/<int:note_id>', methods=['PUT'])
@login_required
def update_note(note_id):
    """Aktualisiert eine bestehende Notiz."""
    data = request.json
    note = MindmapNote.query.get_or_404(note_id)
    
    # Sicherheitscheck
    if note.user_id != current_user.id:
        return jsonify({'error': 'Nicht autorisiert'}), 403
    
    content = data.get('content')
    color_code = data.get('color_code')
    
    if content:
        note.content = content
    if color_code:
        note.color_code = color_code
    
    note.last_modified = datetime.utcnow()
    db.session.commit()
    
    return jsonify({
        'id': note.id,
        'content': note.content,
        'color_code': note.color_code,
        'last_modified': note.last_modified.isoformat()
    })

@app.route('/api/notes/<int:note_id>', methods=['DELETE'])
@login_required
def delete_note(note_id):
    """Löscht eine Notiz."""
    note = MindmapNote.query.get_or_404(note_id)
    
    # Sicherheitscheck
    if note.user_id != current_user.id:
        return jsonify({'error': 'Nicht autorisiert'}), 403
    
    db.session.delete(note)
    db.session.commit()
    
    return jsonify({'success': True})

# API routes for mindmap and thoughts
@app.route('/api/mindmap')
def get_mindmap():
    """API-Endpunkt zur Bereitstellung der Mindmap-Daten in hierarchischer Form."""
    # Alle root-Nodes (ohne parent) abrufen
    root_nodes = MindMapNode.query.filter_by(parent_id=None).all()
    
    if not root_nodes:
        # Wenn keine Nodes existieren, rufen wir initialize_database direkt auf
        # anstatt create_sample_mindmap zu verwenden
        with app.app_context():
            initialize_database()
        root_nodes = MindMapNode.query.filter_by(parent_id=None).all()
    
    # Ergebnisse in hierarchischer Struktur zurückgeben
    result = []
    
    for node in root_nodes:
        node_data = build_node_tree(node)
        result.append(node_data)
    
    return jsonify({"nodes": result})

def build_node_tree(node):
    """Erzeugt eine hierarchische Darstellung eines Knotens inkl. seiner Kindknoten."""
    # Gedankenzähler abrufen von der many-to-many Beziehung
    thought_count = len(node.thoughts)
    
    # Daten für aktuellen Knoten
    node_data = {
        "id": node.id,
        "name": node.name,
        "description": f"Knoten mit {thought_count} Gedanken",
        "thought_count": thought_count,
        "children": []
    }
    
    # Rekursiv Kinder hinzufügen
    child_nodes = MindMapNode.query.filter_by(parent_id=node.id).all()
    for child_node in child_nodes:
        child_data = build_node_tree(child_node)
        node_data["children"].append(child_data)
    
    return node_data

@app.route('/api/nodes/<int:node_id>/thoughts')
def get_node_thoughts(node_id):
    """Liefert alle Gedanken, die mit einem Knoten verknüpft sind."""
    node = MindMapNode.query.get_or_404(node_id)
    
    thoughts = []
    for thought in node.thoughts:
        author = thought.author
        thoughts.append({
            'id': thought.id,
            'title': thought.title,
            'abstract': thought.abstract,
            'content': thought.content[:200] + '...' if len(thought.content) > 200 else thought.content,
            'keywords': thought.keywords,
            'author': {
                'id': author.id,
                'username': author.username
            },
            'created_at': thought.created_at.isoformat(),
            'color_code': thought.color_code,
            'avg_rating': thought.average_rating,
            'bookmarked': current_user.is_authenticated and thought in current_user.bookmarked_thoughts
        })
    
    return jsonify(thoughts)

@app.route('/api/nodes/<int:node_id>/thoughts', methods=['POST'])
@login_required
def add_node_thought(node_id):
    """Fügt einen neuen Gedanken zu einem Knoten hinzu."""
    data = request.json
    node = MindMapNode.query.get_or_404(node_id)
    
    title = data.get('title')
    content = data.get('content')
    abstract = data.get('abstract', '')
    keywords = data.get('keywords', '')
    color_code = data.get('color_code', '#B39DDB')  # Standard-Lila
    
    # Kategorie des Knotens bestimmen
    category_name = node.category.name if node.category else "Allgemein"
    
    thought = Thought(
        title=title,
        content=content,
        abstract=abstract,
        keywords=keywords,
        color_code=color_code,
        branch=category_name,
        user_id=current_user.id,
        source_type='User Input'
    )
    
    node.thoughts.append(thought)
    db.session.add(thought)
    db.session.commit()
    
    return jsonify({
        'id': thought.id,
        'title': thought.title,
        'success': True
    })

@app.route('/api/thoughts/<int:thought_id>', methods=['GET'])
def get_thought(thought_id):
    """Liefert Details zu einem Gedanken."""
    thought = Thought.query.get_or_404(thought_id)
    
    author = thought.author
    is_bookmarked = False
    if current_user.is_authenticated:
        is_bookmarked = thought in current_user.bookmarked_thoughts
    
    # Verknüpfte Knoten abrufen
    nodes = [{
        'id': node.id,
        'name': node.name,
        'category': node.category.name if node.category else None
    } for node in thought.nodes]
    
    return jsonify({
        'id': thought.id,
        'title': thought.title,
        'content': thought.content,
        'abstract': thought.abstract,
        'keywords': thought.keywords,
        'branch': thought.branch,
        'color_code': thought.color_code,
        'created_at': thought.created_at.isoformat(),
        'author': {
            'id': author.id,
            'username': author.username,
            'avatar': author.avatar
        },
        'avg_rating': thought.average_rating,
        'bookmarked': is_bookmarked,
        'nodes': nodes,
        'source_type': thought.source_type
    })

@app.route('/api/thoughts', methods=['POST'])
@login_required
def add_thought():
    """Erstellt einen neuen Gedanken."""
    data = request.json
    
    title = data.get('title')
    content = data.get('content')
    abstract = data.get('abstract', '')
    keywords = data.get('keywords', '')
    branch = data.get('branch', 'Allgemein')
    color_code = data.get('color_code', '#B39DDB')
    
    thought = Thought(
        title=title,
        content=content,
        abstract=abstract,
        keywords=keywords,
        branch=branch,
        color_code=color_code,
        user_id=current_user.id,
        source_type='User Input'
    )
    
    # Knoten-IDs, falls vorhanden
    node_ids = data.get('node_ids', [])
    for node_id in node_ids:
        node = MindMapNode.query.get(node_id)
        if node:
            thought.nodes.append(node)
    
    db.session.add(thought)
    db.session.commit()
    
    return jsonify({
        'id': thought.id,
        'title': thought.title,
        'success': True
    })

@app.route('/api/thoughts/<int:thought_id>', methods=['PUT'])
@login_required
def update_thought(thought_id):
    """Aktualisiert einen bestehenden Gedanken."""
    thought = Thought.query.get_or_404(thought_id)
    
    # Sicherheitscheck
    if thought.user_id != current_user.id and not current_user.is_admin:
        return jsonify({'error': 'Nicht autorisiert'}), 403
    
    data = request.json
    
    # Aktualisiere Felder, die gesendet wurden
    if 'title' in data:
        thought.title = data['title']
    if 'content' in data:
        thought.content = data['content']
    if 'abstract' in data:
        thought.abstract = data['abstract']
    if 'keywords' in data:
        thought.keywords = data['keywords']
    if 'color_code' in data:
        thought.color_code = data['color_code']
    
    thought.last_modified = datetime.utcnow()
    db.session.commit()
    
    return jsonify({
        'id': thought.id,
        'success': True
    })

@app.route('/api/thoughts/<int:thought_id>', methods=['DELETE'])
@login_required
def delete_thought(thought_id):
    """Löscht einen Gedanken."""
    thought = Thought.query.get_or_404(thought_id)
    
    # Sicherheitscheck
    if thought.user_id != current_user.id and not current_user.is_admin:
        return jsonify({'error': 'Nicht autorisiert'}), 403
    
    db.session.delete(thought)
    db.session.commit()
    
    return jsonify({'success': True})

@app.route('/api/thoughts/<int:thought_id>/bookmark', methods=['POST'])
@login_required
def bookmark_thought(thought_id):
    """Fügt einen Gedanken zu den Bookmarks des Benutzers hinzu oder entfernt ihn."""
    thought = Thought.query.get_or_404(thought_id)
    
    # Toggle Bookmark-Status
    if thought in current_user.bookmarked_thoughts:
        current_user.bookmarked_thoughts.remove(thought)
        action = 'removed'
    else:
        current_user.bookmarked_thoughts.append(thought)
        action = 'added'
    
    db.session.commit()
    
    return jsonify({
        'success': True,
        'action': action
    })

@app.route('/api/categories')
def get_categories():
    """API-Endpunkt, der alle Kategorien als hierarchische Struktur zurückgibt"""
    try:
        # Hole alle Kategorien der obersten Ebene
        categories = Category.query.filter_by(parent_id=None).all()
        
        # Transformiere zu einer Baumstruktur
        category_tree = [build_category_tree(cat) for cat in categories]
        
        return jsonify(category_tree)
    except Exception as e:
        print(f"Fehler beim Abrufen der Kategorien: {str(e)}")
        return jsonify({
            'success': False,
            'error': 'Kategorien konnten nicht geladen werden'
        }), 500

@app.route('/api/set_dark_mode', methods=['POST'])
def set_dark_mode():
    """Speichert die Dark Mode-Einstellung in der Session."""
    data = request.json
    dark_mode = data.get('darkMode', False)
    
    session['dark_mode'] = 'true' if dark_mode else 'false'
    session.permanent = True
    
    return jsonify({
        'success': True,
        'darkMode': dark_mode
    })

@app.route('/api/get_dark_mode', methods=['GET'])
def get_dark_mode():
    """Liefert die aktuelle Dark Mode-Einstellung."""
    dark_mode = session.get('dark_mode', 'true')  # Standard: Dark Mode aktiviert
    
    return jsonify({
        'success': True,
        'darkMode': dark_mode
    })

# Fehlerhandler
@app.errorhandler(404)
def page_not_found(e):
    return render_template('errors/404.html'), 404

@app.errorhandler(403)
def forbidden(e):
    return render_template('errors/403.html'), 403

@app.errorhandler(500)
def internal_server_error(e):
    return render_template('errors/500.html'), 500

@app.errorhandler(429)
def too_many_requests(e):
    return render_template('errors/429.html'), 429

# OpenAI-Integration für KI-Assistenz
@app.route('/api/assistant', methods=['POST'])
def chat_with_assistant():
    """Chatbot-API mit OpenAI Integration und Datenbankzugriff."""
    data = request.json
    
    # Prüfen, ob wir ein einzelnes Prompt oder ein messages-Array haben
    if 'messages' in data:
        messages = data.get('messages', [])
        if not messages:
            return jsonify({
                'error': 'Keine Nachrichten vorhanden.'
            }), 400
            
        # Extrahiere Systemnachricht falls vorhanden, sonst Standard-Systemnachricht
        system_message = next((msg['content'] for msg in messages if msg['role'] == 'system'), 
            "Du bist ein hilfreicher Assistent, der Zugriff auf die Wissensdatenbank hat. "
            "Du kannst Informationen zu Gedanken, Kategorien und Mindmaps liefern. "
            "Antworte informativ, sachlich und gut strukturiert auf Deutsch.")
        
        # Formatiere Nachrichten für OpenAI API
        api_messages = [{"role": "system", "content": system_message}]
        
        # Füge Benutzer- und Assistenten-Nachrichten hinzu
        for msg in messages:
            if msg['role'] in ['user', 'assistant']:
                api_messages.append({"role": msg['role'], "content": msg['content']})
    else:
        # Alte Implementierung für direktes Prompt
        prompt = data.get('prompt', '')
        context = data.get('context', '')
        
        if not prompt:
            return jsonify({
                'error': 'Prompt darf nicht leer sein.'
            }), 400
        
        # Zusammenfassen mehrerer Gedanken oder Analyse anfordern
        system_message = (
            "Du bist ein hilfreicher Assistent, der Zugriff auf die Wissensdatenbank hat. "
            "Du kannst Informationen zu Gedanken, Kategorien und Mindmaps liefern. "
            "Antworte informativ, sachlich und gut strukturiert auf Deutsch."
        )
        
        if context:
            system_message += f"\n\nKontext: {context}"
        
        api_messages = [
            {"role": "system", "content": system_message},
            {"role": "user", "content": prompt}
        ]
    
    # Extrahiere die letzte Benutzernachricht für Datenbankabfragen
    user_message = next((msg['content'] for msg in reversed(api_messages) if msg['role'] == 'user'), '')
    
    # Prüfen, ob die Anfrage nach Datenbankinformationen sucht
    db_context = check_database_query(user_message)
    
    if db_context:
        # Erweitere den Kontext mit Datenbankinformationen
        api_messages.append({
            "role": "system", 
            "content": f"Hier sind relevante Informationen aus der Datenbank:\n\n{db_context}"
        })
    
    try:
        # Überprüfen ob OpenAI API-Key konfiguriert ist
        if not client.api_key or client.api_key.startswith("sk-dummy"):
            print("Warnung: OpenAI API-Key ist nicht oder nur als Dummy konfiguriert!")
            return jsonify({
                'error': 'Der OpenAI API-Key ist nicht korrekt konfiguriert. Bitte konfigurieren Sie die Umgebungsvariable OPENAI_API_KEY.'
            }), 500
            
        # API-Aufruf mit Timeout
        import time
        start_time = time.time()
        
        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=api_messages,
            max_tokens=600,  # Erhöht für längere, detailliertere Antworten
            temperature=0.7,
            timeout=20  # 20 Sekunden Timeout
        )
        
        print(f"OpenAI API-Antwortzeit: {time.time() - start_time:.2f} Sekunden")
        
        answer = response.choices[0].message.content
        
        # Für das neue Format erwarten wir response statt answer
        return jsonify({
            'response': answer
        })
    
    except Exception as e:
        import traceback
        print(f"Fehler bei der OpenAI-Anfrage: {str(e)}")
        print(traceback.format_exc())
        
        return jsonify({
            'error': f'Fehler bei der OpenAI-Anfrage: {str(e)}'
        }), 500

def check_database_query(user_message):
    """
    Überprüft, ob die Benutzeranfrage nach Datenbankinformationen sucht und extrahiert 
    relevante Daten aus der Datenbank.
    """
    context = []
    
    # Prüfen auf verschiedene Datenbankabfragemuster
    if any(keyword in user_message.lower() for keyword in ['gedanken', 'thought', 'beitrag', 'inhalt']):
        # Suche nach relevanten Gedanken
        thoughts = Thought.query.filter(
            db.or_(
                Thought.title.ilike(f'%{word}%') for word in user_message.split()
                if len(word) > 3  # Nur längere Wörter zur Suche verwenden
            )
        ).limit(5).all()
        
        if thoughts:
            context.append("Relevante Gedanken:")
            for thought in thoughts:
                context.append(f"- Titel: {thought.title}")
                context.append(f"  Zusammenfassung: {thought.abstract if thought.abstract else 'Keine Zusammenfassung verfügbar'}")
                context.append(f"  Keywords: {thought.keywords if thought.keywords else 'Keine Keywords verfügbar'}")
                context.append("")
    
    if any(keyword in user_message.lower() for keyword in ['kategorie', 'category', 'themengebiet', 'bereich']):
        # Suche nach Kategorien
        categories = Category.query.filter(
            db.or_(
                Category.name.ilike(f'%{word}%') for word in user_message.split()
                if len(word) > 3
            )
        ).limit(5).all()
        
        if categories:
            context.append("Relevante Kategorien:")
            for category in categories:
                context.append(f"- Name: {category.name}")
                context.append(f"  Beschreibung: {category.description}")
                context.append("")
    
    if any(keyword in user_message.lower() for keyword in ['mindmap', 'karte', 'visualisierung']):
        # Suche nach öffentlichen Mindmaps
        mindmap_nodes = MindMapNode.query.filter(
            db.and_(
                MindMapNode.is_public == True,
                db.or_(
                    MindMapNode.name.ilike(f'%{word}%') for word in user_message.split()
                    if len(word) > 3
                )
            )
        ).limit(5).all()
        
        if mindmap_nodes:
            context.append("Relevante Mindmap-Knoten:")
            for node in mindmap_nodes:
                context.append(f"- Name: {node.name}")
                context.append(f"  Beschreibung: {node.description if node.description else 'Keine Beschreibung verfügbar'}")
                if node.category:
                    context.append(f"  Kategorie: {node.category.name}")
                context.append("")
    
    return "\n".join(context) if context else ""

@app.route('/search')
def search_thoughts_page():
    """Seite zur Gedankensuche anzeigen."""
    return render_template('search.html')

@app.route('/my_account')
def my_account():
    """Zeigt die persönliche Merkliste an."""
    if not current_user.is_authenticated:
        flash('Bitte melde dich an, um auf deine Merkliste zuzugreifen.', 'warning')
        return redirect(url_for('login'))
    
    # Hole die Lesezeichen des Benutzers
    bookmarked_thoughts = current_user.bookmarked_thoughts
    
    return render_template('my_account.html', bookmarked_thoughts=bookmarked_thoughts)

# Dummy-Route, um 404-Fehler für fehlende Netzwerk-Hintergrundbilder zu vermeiden
@app.route('/static/network-bg.jpg')
@app.route('/static/network-bg.svg')
def dummy_network_bg():
    """Leere Antwort für die nicht mehr verwendeten Netzwerk-Hintergrundbilder."""
    return '', 200

# Route zum expliziten Neu-Laden der Umgebungsvariablen
@app.route('/admin/reload-env', methods=['POST'])
@admin_required
def reload_env():
    """Lädt die Umgebungsvariablen aus der .env-Datei neu."""
    try:
        # Erzwinge das Neuladen der .env-Datei
        load_dotenv(override=True, force=True)
        
        # OpenAI API-Key ist bereits fest kodiert
        # client wurde bereits mit festem API-Key initialisiert
        
        # Weitere Umgebungsvariablen hier aktualisieren, falls nötig
        app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', app.config['SECRET_KEY'])
        
        return jsonify({
            'success': True,
            'message': 'Umgebungsvariablen wurden erfolgreich neu geladen.'
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'message': f'Fehler beim Neuladen der Umgebungsvariablen: {str(e)}'
        }), 500

# Flask starten
if __name__ == '__main__':
    with app.app_context():
        # Make sure tables exist
        db.create_all()
    app.run(host="0.0.0.0", debug=True)

@app.route('/api/refresh-mindmap')
def refresh_mindmap():
    """
    API-Endpunkt zum Neuladen der Mindmap-Daten,
    wenn die Datenbank-Verbindung vorübergehend fehlgeschlagen ist
    """
    try:
        # Stelle sicher, dass wir Kategorien haben
        if Category.query.count() == 0:
            create_default_categories()
        
        # Hole alle Kategorien und Knoten
        categories = Category.query.filter_by(parent_id=None).all()
        category_tree = [build_category_tree(cat) for cat in categories]
        
        # Hole alle Mindmap-Knoten
        nodes = MindMapNode.query.all()
        node_data = []
        
        for node in nodes:
            node_obj = {
                'id': node.id,
                'name': node.name,
                'description': node.description or '',
                'color_code': node.color_code or '#9F7AEA',
                'thought_count': len(node.thoughts),
                'category_id': node.category_id
            }
            
            # Verbindungen hinzufügen
            node_obj['connections'] = [{'target': child.id} for child in node.children]
            
            node_data.append(node_obj)
        
        return jsonify({
            'success': True,
            'categories': category_tree,
            'nodes': node_data
        })
        
    except Exception as e:
        print(f"Fehler beim Neuladen der Mindmap: {str(e)}")
        return jsonify({
            'success': False,
            'error': 'Datenbankverbindung konnte nicht hergestellt werden'
        }), 500