"""
Coupon Management Routes
Admin routes for creating and managing coupons
"""
from flask import Blueprint, request, jsonify, g
from utils import require_api_key, format_api_response, create_error_response
from auth.jwt_manager import require_auth
from database import get_cached_connection
import logging
import json
from datetime import datetime, timedelta
from decimal import Decimal
import secrets
import string

logger = logging.getLogger(__name__)

coupon_bp = Blueprint('coupon', __name__, url_prefix='/api/coupons')

@coupon_bp.route('/create', methods=['POST'])
@require_api_key
@require_auth
def create_coupon():
    """
    Create a new coupon (Admin only)
    """
    conn = None
    cursor = None
    try:
        admin_id = g.current_user_id
        data = request.get_json()
        
        if not data:
            return create_error_response("Request data is required", 400)
        
        # Extract coupon data
        name = data.get('name')
        description = data.get('description', '')
        coupon_type = data.get('coupon_type', 'balance')  # balance, percentage, fixed
        code = data.get('code')  # If not provided, will be auto-generated
        
        if not name:
            return create_error_response("Coupon name is required", 400)
        
        if coupon_type not in ['balance', 'percentage', 'fixed']:
            return create_error_response("Invalid coupon type", 400)
        
        # Generate code if not provided
        if not code:
            code = _generate_coupon_code()
        
        # Validate and extract type-specific data
        balance = 0.0
        initial_balance = 0.0
        discount_value = 0.0
        
        if coupon_type == 'balance':
            balance = float(data.get('balance', 0))
            initial_balance = balance
            if balance <= 0:
                return create_error_response("Balance must be greater than 0 for balance coupons", 400)
        
        elif coupon_type in ['percentage', 'fixed']:
            discount_value = float(data.get('discount_value', 0))
            if discount_value <= 0:
                return create_error_response("Discount value must be greater than 0", 400)
            
            if coupon_type == 'percentage' and discount_value > 100:
                return create_error_response("Percentage discount cannot exceed 100%", 400)
        
        # Optional fields
        valid_from = data.get('valid_from')
        valid_until = data.get('valid_until')
        usage_limit = data.get('usage_limit')
        min_purchase_amount = float(data.get('min_purchase_amount', 0))
        max_discount_amount = data.get('max_discount_amount')
        applicable_courses = data.get('applicable_courses')  # List of course IDs
        applicable_institutes = data.get('applicable_institutes')  # List of institute IDs
        notes = data.get('notes', '')
        
        # Parse dates
        valid_from_dt = None
        valid_until_dt = None
        
        if valid_from:
            try:
                valid_from_dt = datetime.fromisoformat(valid_from.replace('Z', '+00:00'))
            except ValueError:
                return create_error_response("Invalid valid_from date format", 400)
        
        if valid_until:
            try:
                valid_until_dt = datetime.fromisoformat(valid_until.replace('Z', '+00:00'))
            except ValueError:
                return create_error_response("Invalid valid_until date format", 400)
        
        # Convert lists to JSON strings
        applicable_courses_json = None
        applicable_institutes_json = None
        
        if applicable_courses and isinstance(applicable_courses, list):
            applicable_courses_json = json.dumps(applicable_courses)
        
        if applicable_institutes and isinstance(applicable_institutes, list):
            applicable_institutes_json = json.dumps(applicable_institutes)
        
        conn = get_cached_connection()
        cursor = conn.cursor(dictionary=True)
        
        # Check if code already exists
        cursor.execute("SELECT id FROM coupons WHERE code = %s", (code,))
        if cursor.fetchone():
            return create_error_response("Coupon code already exists", 409)
        
        # Create coupon
        cursor.execute("""
            INSERT INTO coupons 
            (code, name, description, balance, initial_balance, coupon_type, discount_value,
             valid_from, valid_until, usage_limit, min_purchase_amount, max_discount_amount,
             applicable_courses, applicable_institutes, notes, created_by)
            VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
        """, (
            code, name, description, balance, initial_balance, coupon_type, discount_value,
            valid_from_dt, valid_until_dt, usage_limit, min_purchase_amount, max_discount_amount,
            applicable_courses_json, applicable_institutes_json, notes, admin_id
        ))
        
        coupon_id = cursor.lastrowid
        conn.commit()
        
        
        coupon_response = {
            'coupon_id': coupon_id,
            'code': code,
            'message': 'Coupon created successfully'
        }
        return format_api_response(
            data=coupon_response,
            message="Coupon created successfully",
            include_legacy_fields=coupon_response
        )
        
    except Exception as e:
        if conn:
            conn.rollback()
        logger.error(f"Error in create_coupon: {str(e)}")
        return create_error_response(f"Failed to create coupon: {str(e)}", 500)
    finally:
        if cursor:
            cursor.close()
        if conn:
            conn.close()

@coupon_bp.route('/list', methods=['GET'])
@require_api_key
@require_auth
def list_coupons():
    """
    List all coupons (Admin only)
    """
    conn = None
    cursor = None
    try:
        # Get query parameters
        page = int(request.args.get('page', 1))
        limit = int(request.args.get('limit', 20))
        search = request.args.get('search', '')
        coupon_type = request.args.get('type', '')
        is_active = request.args.get('active', '')
        
        offset = (page - 1) * limit
        
        conn = get_cached_connection()
        cursor = conn.cursor(dictionary=True)
        
        # Build query conditions
        conditions = []
        params = []
        
        if search:
            conditions.append("(code LIKE %s OR name LIKE %s)")
            params.extend([f"%{search}%", f"%{search}%"])
        
        if coupon_type:
            conditions.append("coupon_type = %s")
            params.append(coupon_type)
        
        if is_active:
            conditions.append("is_active = %s")
            params.append(1 if is_active.lower() == 'true' else 0)
        
        where_clause = "WHERE " + " AND ".join(conditions) if conditions else ""
        
        # Get total count
        count_query = f"SELECT COUNT(*) as total FROM coupons {where_clause}"
        cursor.execute(count_query, params)
        total_count = cursor.fetchone()['total']
        
        # Get coupons
        query = f"""
            SELECT 
                id, code, name, description, balance, initial_balance, coupon_type,
                discount_value, is_active, valid_from, valid_until, usage_limit, used_count,
                min_purchase_amount, max_discount_amount, created_at, updated_at
            FROM coupons 
            {where_clause}
            ORDER BY created_at DESC
            LIMIT %s OFFSET %s
        """
        
        cursor.execute(query, params + [limit, offset])
        coupons = cursor.fetchall()
        
        # Format response
        formatted_coupons = []
        for coupon in coupons:
            formatted_coupons.append({
                'id': coupon['id'],
                'code': coupon['code'],
                'name': coupon['name'],
                'description': coupon['description'],
                'coupon_type': coupon['coupon_type'],
                'balance': float(coupon['balance']) if coupon['balance'] else 0.0,
                'initial_balance': float(coupon['initial_balance']) if coupon['initial_balance'] else 0.0,
                'discount_value': float(coupon['discount_value']) if coupon['discount_value'] else 0.0,
                'is_active': bool(coupon['is_active']),
                'valid_from': coupon['valid_from'].isoformat() if coupon['valid_from'] else None,
                'valid_until': coupon['valid_until'].isoformat() if coupon['valid_until'] else None,
                'usage_limit': coupon['usage_limit'],
                'used_count': coupon['used_count'],
                'min_purchase_amount': float(coupon['min_purchase_amount']) if coupon['min_purchase_amount'] else 0.0,
                'max_discount_amount': float(coupon['max_discount_amount']) if coupon['max_discount_amount'] else None,
                'created_at': coupon['created_at'].isoformat(),
                'updated_at': coupon['updated_at'].isoformat()
            })
        
        return format_api_response({
            'coupons': formatted_coupons,
            'pagination': {
                'page': page,
                'limit': limit,
                'total': total_count,
                'pages': (total_count + limit - 1) // limit
            }
        })
        
    except Exception as e:
        logger.error(f"Error in list_coupons: {str(e)}")
        return create_error_response(f"Failed to fetch coupons: {str(e)}", 500)
    finally:
        if cursor:
            cursor.close()
        if conn:
            conn.close()

@coupon_bp.route('/<int:coupon_id>', methods=['GET'])
@require_api_key
@require_auth
def get_coupon(coupon_id):
    """
    Get coupon details (Admin only)
    """
    conn = None
    cursor = None
    try:
        conn = get_cached_connection()
        cursor = conn.cursor(dictionary=True)
        
        cursor.execute("""
            SELECT 
                c.*,
                u.username as created_by_username
            FROM coupons c
            LEFT JOIN user u ON c.created_by = u.id
            WHERE c.id = %s
        """, (coupon_id,))
        
        coupon = cursor.fetchone()
        if not coupon:
            return create_error_response("Coupon not found", 404)
        
        # Get usage history
        cursor.execute("""
            SELECT 
                cuh.*,
                u.username,
                c.title as course_title
            FROM coupon_usage_history cuh
            LEFT JOIN user u ON cuh.user_id = u.id
            LEFT JOIN courses c ON cuh.course_id = c.id
            WHERE cuh.coupon_id = %s
            ORDER BY cuh.created_at DESC
            LIMIT 50
        """, (coupon_id,))
        
        usage_history = cursor.fetchall()
        
        # Format response
        formatted_coupon = {
            'id': coupon['id'],
            'code': coupon['code'],
            'name': coupon['name'],
            'description': coupon['description'],
            'coupon_type': coupon['coupon_type'],
            'balance': float(coupon['balance']) if coupon['balance'] else 0.0,
            'initial_balance': float(coupon['initial_balance']) if coupon['initial_balance'] else 0.0,
            'discount_value': float(coupon['discount_value']) if coupon['discount_value'] else 0.0,
            'is_active': bool(coupon['is_active']),
            'valid_from': coupon['valid_from'].isoformat() if coupon['valid_from'] else None,
            'valid_until': coupon['valid_until'].isoformat() if coupon['valid_until'] else None,
            'usage_limit': coupon['usage_limit'],
            'used_count': coupon['used_count'],
            'min_purchase_amount': float(coupon['min_purchase_amount']) if coupon['min_purchase_amount'] else 0.0,
            'max_discount_amount': float(coupon['max_discount_amount']) if coupon['max_discount_amount'] else None,
            'applicable_courses': json.loads(coupon['applicable_courses']) if coupon['applicable_courses'] else None,
            'applicable_institutes': json.loads(coupon['applicable_institutes']) if coupon['applicable_institutes'] else None,
            'notes': coupon['notes'],
            'created_by_username': coupon['created_by_username'],
            'created_at': coupon['created_at'].isoformat(),
            'updated_at': coupon['updated_at'].isoformat(),
            'usage_history': [
                {
                    'id': usage['id'],
                    'username': usage['username'],
                    'course_title': usage['course_title'],
                    'amount_used': float(usage['amount_used']),
                    'original_price': float(usage['original_price']),
                    'final_price': float(usage['final_price']),
                    'discount_applied': float(usage['discount_applied']),
                    'usage_type': usage['usage_type'],
                    'created_at': usage['created_at'].isoformat()
                }
                for usage in usage_history
            ]
        }
        
        
        return format_api_response(
            data=formatted_coupon,
            message="Coupon details retrieved successfully",
            include_legacy_fields=formatted_coupon
        )
        
    except Exception as e:
        logger.error(f"Error in get_coupon: {str(e)}")
        return create_error_response(f"Failed to fetch coupon: {str(e)}", 500)
    finally:
        if cursor:
            cursor.close()
        if conn:
            conn.close()

@coupon_bp.route('/<int:coupon_id>/toggle', methods=['POST'])
@require_api_key
@require_auth
def toggle_coupon_status(coupon_id):
    """
    Toggle coupon active status (Admin only)
    """
    conn = None
    cursor = None
    try:
        conn = get_cached_connection()
        cursor = conn.cursor(dictionary=True)
        
        # Get current status
        cursor.execute("SELECT is_active FROM coupons WHERE id = %s", (coupon_id,))
        coupon = cursor.fetchone()
        
        if not coupon:
            return create_error_response("Coupon not found", 404)
        
        new_status = not bool(coupon['is_active'])
        
        # Update status
        cursor.execute("""
            UPDATE coupons 
            SET is_active = %s, updated_at = NOW()
            WHERE id = %s
        """, (new_status, coupon_id))
        
        
        conn.commit()
        
        toggle_response = {
            'coupon_id': coupon_id,
            'is_active': new_status,
            'message': f'Coupon {"activated" if new_status else "deactivated"} successfully'
        }
        return format_api_response(
            data=toggle_response,
            message=f'Coupon {"activated" if new_status else "deactivated"} successfully',
            include_legacy_fields=toggle_response
        )
        
    except Exception as e:
        if conn:
            conn.rollback()
        logger.error(f"Error in toggle_coupon_status: {str(e)}")
        return create_error_response(f"Failed to toggle coupon status: {str(e)}", 500)
    finally:
        if cursor:
            cursor.close()
        if conn:
            conn.close()

@coupon_bp.route('/<int:coupon_id>/add-balance', methods=['POST'])
@require_api_key
@require_auth
def add_coupon_balance(coupon_id):
    """
    Add balance to a balance-type coupon (Admin only)
    """
    conn = None
    cursor = None
    try:
        data = request.get_json()
        if not data:
            return create_error_response("Request data is required", 400)
        
        amount = float(data.get('amount', 0))
        if amount <= 0:
            return create_error_response("Amount must be greater than 0", 400)
        
        conn = get_cached_connection()
        cursor = conn.cursor(dictionary=True)
        
        # Get coupon
        cursor.execute("""
            SELECT id, code, coupon_type, balance 
            FROM coupons 
            WHERE id = %s
        """, (coupon_id,))
        
        coupon = cursor.fetchone()
        if not coupon:
            return create_error_response("Coupon not found", 404)
        
        if coupon['coupon_type'] != 'balance':
            return create_error_response("Can only add balance to balance-type coupons", 400)
        
        # Update balance
        new_balance = float(coupon['balance']) + amount
        
        cursor.execute("""
            UPDATE coupons 
            SET balance = %s, updated_at = NOW()
            WHERE id = %s
        """, (new_balance, coupon_id))
        
        conn.commit()
        
        
        balance_response = {
            'coupon_id': coupon_id,
            'code': coupon['code'],
            'previous_balance': float(coupon['balance']),
            'amount_added': amount,
            'new_balance': new_balance,
            'message': f'Added NPR {amount} to coupon balance'
        }
        return format_api_response(
            data=balance_response,
            message=f'Added NPR {amount} to coupon balance',
            include_legacy_fields=balance_response
        )
        
    except Exception as e:
        if conn:
            conn.rollback()
        logger.error(f"Error in add_coupon_balance: {str(e)}")
        return create_error_response(f"Failed to add coupon balance: {str(e)}", 500)
    finally:
        if cursor:
            cursor.close()
        if conn:
            conn.close()

@coupon_bp.route('/assign', methods=['POST'])
@require_api_key
@require_auth   
def assign_coupon_to_user():
    """
    Assign a coupon to a specific user (Admin only)
    """
    conn = None
    cursor = None
    try:
        admin_id = g.current_user_id
        data = request.get_json()
        
        if not data:
            return create_error_response("Request data is required", 400)
        
        user_id = data.get('user_id')
        coupon_id = data.get('coupon_id')
        notes = data.get('notes', '')
        
        if not user_id or not coupon_id:
            return create_error_response("User ID and Coupon ID are required", 400)
        
        conn = get_cached_connection()
        cursor = conn.cursor(dictionary=True)
        
        # Verify user exists
        cursor.execute("SELECT id, username FROM user WHERE id = %s", (user_id,))
        user = cursor.fetchone()
        if not user:
            return create_error_response("User not found", 404)
        
        # Verify coupon exists
        cursor.execute("SELECT id, code, name FROM coupons WHERE id = %s", (coupon_id,))
        coupon = cursor.fetchone()
        if not coupon:
            return create_error_response("Coupon not found", 404)
        
        # Check if already assigned
        cursor.execute("""
            SELECT id FROM user_coupon_assignments 
            WHERE user_id = %s AND coupon_id = %s
        """, (user_id, coupon_id))
        
        if cursor.fetchone():
            return create_error_response("Coupon already assigned to this user", 409)
        
        # Create assignment
        cursor.execute("""
            INSERT INTO user_coupon_assignments 
            (user_id, coupon_id, assigned_by, notes)
            VALUES (%s, %s, %s, %s)
        """, (user_id, coupon_id, admin_id, notes))
        
        assignment_id = cursor.lastrowid
        conn.commit()
        
        
        assign_response = {
            'assignment_id': assignment_id,
            'user_id': user_id,
            'username': user['username'],
            'coupon_id': coupon_id,
            'coupon_code': coupon['code'],
            'coupon_name': coupon['name'],
            'message': f'Coupon "{coupon["code"]}" assigned to user "{user["username"]}" successfully'
        }
        return format_api_response(
            data=assign_response,
            message=f'Coupon "{coupon["code"]}" assigned to user "{user["username"]}" successfully',
            include_legacy_fields=assign_response
        )
        
    except Exception as e:
        if conn:
            conn.rollback()
        logger.error(f"Error in assign_coupon_to_user: {str(e)}")
        return create_error_response(f"Failed to assign coupon: {str(e)}", 500)
    finally:
        if cursor:
            cursor.close()
        if conn:
            conn.close()

# Helper functions

def _generate_coupon_code(length=8):
    """Generate a random coupon code"""
    characters = string.ascii_uppercase + string.digits
    return ''.join(secrets.choice(characters) for _ in range(length))

