import os
import logging
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
from dateutil.relativedelta import relativedelta
from flask import Blueprint, render_template, request, redirect, session, url_for, flash, jsonify

import config
from auth.decorators import login_required
from utils.audit import log_audit
from utils.network import get_client_ip
from utils.license import validate_license_key
from db import get_connection, get_db_connection, get_db_cursor
from models import get_licenses, get_license_by_id

# Create Blueprint
license_bp = Blueprint('licenses', __name__)


@license_bp.route("/licenses")
@login_required
def licenses():
    from datetime import datetime, timedelta
    
    # Get filter parameters
    search = request.args.get('search', '').strip()
    data_source = request.args.get('data_source', 'real')  # real, fake, all
    license_type = request.args.get('license_type', '')  # '', full, test
    license_status = request.args.get('license_status', '')  # '', active, expiring, expired, inactive
    sort = request.args.get('sort', 'created_at')
    order = request.args.get('order', 'desc')
    page = request.args.get('page', 1, type=int)
    per_page = 50
    
    # Get licenses based on data source
    if data_source == 'fake':
        licenses_list = get_licenses(show_fake=True)
        licenses_list = [l for l in licenses_list if l.get('is_fake')]
    elif data_source == 'all':
        licenses_list = get_licenses(show_fake=True)
    else:  # real
        licenses_list = get_licenses(show_fake=False)
    
    # Type filtering
    if license_type:
        if license_type == 'full':
            licenses_list = [l for l in licenses_list if l.get('license_type') == 'full']
        elif license_type == 'test':
            licenses_list = [l for l in licenses_list if l.get('license_type') == 'test']
    
    # Status filtering
    if license_status:
        now = datetime.now().date()
        filtered_licenses = []
        
        for license in licenses_list:
            if license_status == 'active' and license.get('is_active'):
                # Active means is_active=true, regardless of expiration date
                filtered_licenses.append(license)
            elif license_status == 'expired' and license.get('valid_until') and license.get('valid_until') <= now:
                # Expired means past valid_until date, regardless of is_active
                filtered_licenses.append(license)
            elif license_status == 'inactive' and not license.get('is_active'):
                # Inactive means is_active=false, regardless of date
                filtered_licenses.append(license)
                
        licenses_list = filtered_licenses
    
    # Search filtering
    if search:
        search_lower = search.lower()
        licenses_list = [l for l in licenses_list if 
                        search_lower in str(l.get('license_key', '')).lower() or
                        search_lower in str(l.get('customer_name', '')).lower() or
                        search_lower in str(l.get('customer_email', '')).lower()]
    
    # Calculate pagination
    total = len(licenses_list)
    total_pages = (total + per_page - 1) // per_page
    start = (page - 1) * per_page
    end = start + per_page
    licenses_list = licenses_list[start:end]
    
    return render_template("licenses.html", 
                         licenses=licenses_list, 
                         search=search,
                         data_source=data_source,
                         license_type=license_type,
                         license_status=license_status,
                         sort=sort,
                         order=order,
                         page=page,
                         total=total,
                         total_pages=total_pages,
                         per_page=per_page,
                         now=datetime.now,
                         timedelta=timedelta)


@license_bp.route("/license/edit/<int:license_id>", methods=["GET", "POST"])
@login_required
def edit_license(license_id):
    conn = get_connection()
    cur = conn.cursor()

    if request.method == "POST":
        try:
            # Get current license data for comparison
            current_license = get_license_by_id(license_id)
            if not current_license:
                flash('Lizenz nicht gefunden!', 'error')
                return redirect(url_for('licenses.licenses'))
            
            # Update license data
            new_values = {
                'license_key': request.form['license_key'],
                'license_type': request.form['license_type'],
                'valid_from': request.form['valid_from'],
                'valid_until': request.form['valid_until'],
                'is_active': 'is_active' in request.form,
                'device_limit': int(request.form.get('device_limit', 3))
            }
            
            cur.execute("""
                UPDATE licenses 
                SET license_key = %s, license_type = %s, valid_from = %s, 
                    valid_until = %s, is_active = %s, device_limit = %s
                WHERE id = %s
            """, (
                new_values['license_key'],
                new_values['license_type'],
                new_values['valid_from'],
                new_values['valid_until'],
                new_values['is_active'],
                new_values['device_limit'],
                license_id
            ))
            
            conn.commit()
            
            # Log changes
            log_audit('UPDATE', 'license', license_id,
                     old_values={
                         'license_key': current_license.get('license_key'),
                         'license_type': current_license.get('license_type'),
                         'valid_from': str(current_license.get('valid_from', '')),
                         'valid_until': str(current_license.get('valid_until', '')),
                         'is_active': current_license.get('is_active'),
                         'device_limit': current_license.get('device_limit', 3)
                     },
                     new_values=new_values)
            
            flash('Lizenz erfolgreich aktualisiert!', 'success')
            
            # Preserve show_test parameter if present
            show_test = request.args.get('show_test', 'false')
            return redirect(url_for('licenses.licenses', show_test=show_test))
            
        except Exception as e:
            conn.rollback()
            logging.error(f"Fehler beim Aktualisieren der Lizenz: {str(e)}")
            flash('Fehler beim Aktualisieren der Lizenz!', 'error')
        finally:
            cur.close()
            conn.close()

    # GET request
    license_data = get_license_by_id(license_id)
    if not license_data:
        flash('Lizenz nicht gefunden!', 'error')
        return redirect(url_for('licenses.licenses'))
    
    return render_template("edit_license.html", license=license_data)


@license_bp.route("/license/delete/<int:license_id>", methods=["POST"])
@login_required
def delete_license(license_id):
    # Check for force parameter
    force_delete = request.form.get('force', 'false').lower() == 'true'
    
    conn = get_connection()
    cur = conn.cursor()

    try:
        # Get license data before deletion
        license_data = get_license_by_id(license_id)
        if not license_data:
            flash('Lizenz nicht gefunden!', 'error')
            return redirect(url_for('licenses.licenses'))
        
        # Safety check: Don't delete active licenses unless forced
        if license_data.get('is_active') and not force_delete:
            flash(f'Lizenz {license_data["license_key"]} ist noch aktiv! Bitte deaktivieren Sie die Lizenz zuerst oder nutzen Sie "Erzwungenes Löschen".', 'warning')
            return redirect(url_for('licenses.licenses'))
        
        # Check for recent activity (heartbeats in last 24 hours)
        try:
            cur.execute("""
                SELECT COUNT(*) 
                FROM license_heartbeats 
                WHERE license_id = %s 
                AND timestamp > NOW() - INTERVAL '24 hours'
            """, (license_id,))
            recent_heartbeats = cur.fetchone()[0]
            
            if recent_heartbeats > 0 and not force_delete:
                flash(f'Lizenz {license_data["license_key"]} hatte in den letzten 24 Stunden {recent_heartbeats} Aktivitäten! '
                      f'Die Lizenz wird möglicherweise noch aktiv genutzt. Bitte prüfen Sie dies vor dem Löschen.', 'danger')
                return redirect(url_for('licenses.licenses'))
        except Exception as e:
            # If heartbeats table doesn't exist, continue
            logging.warning(f"Could not check heartbeats: {str(e)}")
        
        # Check for active devices/activations
        try:
            cur.execute("""
                SELECT COUNT(*) 
                FROM activations 
                WHERE license_id = %s 
                AND is_active = true
            """, (license_id,))
            active_devices = cur.fetchone()[0]
            
            if active_devices > 0 and not force_delete:
                flash(f'Lizenz {license_data["license_key"]} hat {active_devices} aktive Geräte! '
                      f'Bitte deaktivieren Sie alle Geräte vor dem Löschen.', 'danger')
                return redirect(url_for('licenses.licenses'))
        except Exception as e:
            # If activations table doesn't exist, continue
            logging.warning(f"Could not check activations: {str(e)}")
        
        # Delete from sessions first
        cur.execute("DELETE FROM sessions WHERE license_key = %s", (license_data['license_key'],))
        
        # Delete from license_heartbeats if exists
        try:
            cur.execute("DELETE FROM license_heartbeats WHERE license_id = %s", (license_id,))
        except:
            pass
        
        # Delete from activations if exists
        try:
            cur.execute("DELETE FROM activations WHERE license_id = %s", (license_id,))
        except:
            pass
        
        # Delete the license
        cur.execute("DELETE FROM licenses WHERE id = %s", (license_id,))
        
        conn.commit()
        
        # Log deletion with force flag
        log_audit('DELETE', 'license', license_id,
                 old_values={
                     'license_key': license_data['license_key'],
                     'customer_name': license_data['customer_name'],
                     'customer_email': license_data['customer_email'],
                     'was_active': license_data.get('is_active'),
                     'forced': force_delete
                 },
                 additional_info=f"{'Forced deletion' if force_delete else 'Normal deletion'}")
        
        flash(f'Lizenz {license_data["license_key"]} erfolgreich gelöscht!', 'success')
        
    except Exception as e:
        conn.rollback()
        logging.error(f"Fehler beim Löschen der Lizenz: {str(e)}")
        flash('Fehler beim Löschen der Lizenz!', 'error')
    finally:
        cur.close()
        conn.close()

    # Preserve show_test parameter if present
    show_test = request.args.get('show_test', 'false')
    return redirect(url_for('licenses.licenses', show_test=show_test))


@license_bp.route("/create", methods=["GET", "POST"])
@login_required
def create_license():
    if request.method == "POST":
        customer_id = request.form.get("customer_id")
        license_key = request.form["license_key"].upper()  # Immer Großbuchstaben
        license_type = request.form["license_type"]
        valid_from = request.form["valid_from"]
        # is_fake wird später vom Kunden geerbt
        
        # Berechne valid_until basierend auf Laufzeit
        duration = int(request.form.get("duration", 1))
        duration_type = request.form.get("duration_type", "years")
        
        start_date = datetime.strptime(valid_from, "%Y-%m-%d")
        
        if duration_type == "days":
            end_date = start_date + timedelta(days=duration)
        elif duration_type == "months":
            end_date = start_date + relativedelta(months=duration)
        else:  # years
            end_date = start_date + relativedelta(years=duration)
        
        # Ein Tag abziehen, da der Starttag mitgezählt wird
        end_date = end_date - timedelta(days=1)
        valid_until = end_date.strftime("%Y-%m-%d")

        # Validiere License Key Format
        if not validate_license_key(license_key):
            flash('Ungültiges License Key Format! Erwartet: AF-YYYYMMFT-XXXX-YYYY-ZZZZ', 'error')
            return redirect(url_for('licenses.create_license'))
        
        # Resource counts
        domain_count = int(request.form.get("domain_count", 1))
        ipv4_count = int(request.form.get("ipv4_count", 1))
        phone_count = int(request.form.get("phone_count", 1))
        device_limit = int(request.form.get("device_limit", 3))

        conn = get_connection()
        cur = conn.cursor()

        try:
            # Prüfe ob neuer Kunde oder bestehender
            if customer_id == "new":
                # Neuer Kunde
                name = request.form.get("customer_name")
                email = request.form.get("email")
                
                if not name:
                    flash('Kundenname ist erforderlich!', 'error')
                    return redirect(url_for('licenses.create_license'))
                
                # Prüfe ob E-Mail bereits existiert
                if email:
                    cur.execute("SELECT id, name FROM customers WHERE LOWER(email) = LOWER(%s)", (email,))
                    existing = cur.fetchone()
                    if existing:
                        flash(f'E-Mail bereits vergeben für Kunde: {existing[1]}', 'error')
                        return redirect(url_for('licenses.create_license'))
                
                # Neuer Kunde wird immer als Fake erstellt, da wir in der Testphase sind
                # TODO: Nach Testphase muss hier die Business-Logik angepasst werden
                is_fake = True
                cur.execute("""
                    INSERT INTO customers (name, email, is_fake, created_at)
                    VALUES (%s, %s, %s, NOW())
                    RETURNING id
                """, (name, email, is_fake))
                customer_id = cur.fetchone()[0]
                customer_info = {'name': name, 'email': email, 'is_fake': is_fake}
                
                # Audit-Log für neuen Kunden
                log_audit('CREATE', 'customer', customer_id, 
                         new_values={'name': name, 'email': email, 'is_fake': is_fake})
            else:
                # Bestehender Kunde - hole Infos für Audit-Log
                cur.execute("SELECT name, email, is_fake FROM customers WHERE id = %s", (customer_id,))
                customer_data = cur.fetchone()
                if not customer_data:
                    flash('Kunde nicht gefunden!', 'error')
                    return redirect(url_for('licenses.create_license'))
                customer_info = {'name': customer_data[0], 'email': customer_data[1]}
                
                # Lizenz erbt immer den is_fake Status vom Kunden
                is_fake = customer_data[2]

            # Lizenz hinzufügen
            cur.execute("""
                INSERT INTO licenses (license_key, customer_id, license_type, valid_from, valid_until, is_active, 
                                    domain_count, ipv4_count, phone_count, device_limit, is_fake)
                VALUES (%s, %s, %s, %s, %s, TRUE, %s, %s, %s, %s, %s)
                RETURNING id
            """, (license_key, customer_id, license_type, valid_from, valid_until, 
                  domain_count, ipv4_count, phone_count, device_limit, is_fake))
            license_id = cur.fetchone()[0]
            
            # Ressourcen zuweisen
            try:
                # Prüfe Verfügbarkeit
                cur.execute("""
                    SELECT 
                        (SELECT COUNT(*) FROM resource_pools WHERE resource_type = 'domain' AND status = 'available' AND is_fake = %s) as domains,
                        (SELECT COUNT(*) FROM resource_pools WHERE resource_type = 'ipv4' AND status = 'available' AND is_fake = %s) as ipv4s,
                        (SELECT COUNT(*) FROM resource_pools WHERE resource_type = 'phone' AND status = 'available' AND is_fake = %s) as phones
                """, (is_fake, is_fake, is_fake))
                available = cur.fetchone()
                
                if available[0] < domain_count:
                    raise ValueError(f"Nicht genügend Domains verfügbar (benötigt: {domain_count}, verfügbar: {available[0]})")
                if available[1] < ipv4_count:
                    raise ValueError(f"Nicht genügend IPv4-Adressen verfügbar (benötigt: {ipv4_count}, verfügbar: {available[1]})")
                if available[2] < phone_count:
                    raise ValueError(f"Nicht genügend Telefonnummern verfügbar (benötigt: {phone_count}, verfügbar: {available[2]})")
                
                # Domains zuweisen
                if domain_count > 0:
                    cur.execute("""
                        SELECT id FROM resource_pools
                        WHERE resource_type = 'domain' AND status = 'available' AND is_fake = %s
                        LIMIT %s FOR UPDATE
                    """, (is_fake, domain_count))
                    for (resource_id,) in cur.fetchall():
                        cur.execute("""
                            UPDATE resource_pools
                            SET status = 'allocated', allocated_to_license = %s,
                                status_changed_at = CURRENT_TIMESTAMP, status_changed_by = %s
                            WHERE id = %s
                        """, (license_id, session['username'], resource_id))
                        
                        cur.execute("""
                            INSERT INTO license_resources (license_id, resource_id, assigned_by)
                            VALUES (%s, %s, %s)
                        """, (license_id, resource_id, session['username']))
                        
                        cur.execute("""
                            INSERT INTO resource_history (resource_id, license_id, action, action_by, ip_address)
                            VALUES (%s, %s, 'allocated', %s, %s)
                        """, (resource_id, license_id, session['username'], get_client_ip()))
                
                # IPv4s zuweisen
                if ipv4_count > 0:
                    cur.execute("""
                        SELECT id FROM resource_pools
                        WHERE resource_type = 'ipv4' AND status = 'available' AND is_fake = %s
                        LIMIT %s FOR UPDATE
                    """, (is_fake, ipv4_count))
                    for (resource_id,) in cur.fetchall():
                        cur.execute("""
                            UPDATE resource_pools
                            SET status = 'allocated', allocated_to_license = %s,
                                status_changed_at = CURRENT_TIMESTAMP, status_changed_by = %s
                            WHERE id = %s
                        """, (license_id, session['username'], resource_id))
                        
                        cur.execute("""
                            INSERT INTO license_resources (license_id, resource_id, assigned_by)
                            VALUES (%s, %s, %s)
                        """, (license_id, resource_id, session['username']))
                        
                        cur.execute("""
                            INSERT INTO resource_history (resource_id, license_id, action, action_by, ip_address)
                            VALUES (%s, %s, 'allocated', %s, %s)
                        """, (resource_id, license_id, session['username'], get_client_ip()))
                
                # Telefonnummern zuweisen
                if phone_count > 0:
                    cur.execute("""
                        SELECT id FROM resource_pools
                        WHERE resource_type = 'phone' AND status = 'available' AND is_fake = %s
                        LIMIT %s FOR UPDATE
                    """, (is_fake, phone_count))
                    for (resource_id,) in cur.fetchall():
                        cur.execute("""
                            UPDATE resource_pools
                            SET status = 'allocated', allocated_to_license = %s,
                                status_changed_at = CURRENT_TIMESTAMP, status_changed_by = %s
                            WHERE id = %s
                        """, (license_id, session['username'], resource_id))
                        
                        cur.execute("""
                            INSERT INTO license_resources (license_id, resource_id, assigned_by)
                            VALUES (%s, %s, %s)
                        """, (license_id, resource_id, session['username']))
                        
                        cur.execute("""
                            INSERT INTO resource_history (resource_id, license_id, action, action_by, ip_address)
                            VALUES (%s, %s, 'allocated', %s, %s)
                        """, (resource_id, license_id, session['username'], get_client_ip()))
                
            except ValueError as e:
                conn.rollback()
                flash(str(e), 'error')
                return redirect(url_for('licenses.create_license'))

            conn.commit()
            
            # Audit-Log
            log_audit('CREATE', 'license', license_id, 
                     new_values={
                         'license_key': license_key,
                         'customer_name': customer_info['name'],
                         'customer_email': customer_info['email'],
                         'license_type': license_type,
                         'valid_from': valid_from,
                         'valid_until': valid_until,
                         'device_limit': device_limit,
                         'is_fake': is_fake
                     })
            
            flash(f'Lizenz {license_key} erfolgreich erstellt!', 'success')
            
        except Exception as e:
            conn.rollback()
            logging.error(f"Fehler beim Erstellen der Lizenz: {str(e)}")
            flash('Fehler beim Erstellen der Lizenz!', 'error')
        finally:
            cur.close()
            conn.close()

        # Preserve show_test parameter if present
        redirect_url = url_for('licenses.create_license')
        if request.args.get('show_test') == 'true':
            redirect_url += "?show_test=true"
        return redirect(redirect_url)

    # Unterstützung für vorausgewählten Kunden
    preselected_customer_id = request.args.get('customer_id', type=int)
    return render_template("index.html", username=session.get('username'), preselected_customer_id=preselected_customer_id)