# server/app.py

from flask import Flask, request, jsonify, session
from flask_cors import CORS
from flask_login import LoginManager, login_user, logout_user, login_required, current_user
from datetime import timedelta, datetime
from models import (
    db, User, Role, Link_Reset,
    # English System
    EnglishClass, EnglishLevel, EnglishStudent, EnglishBook,
    EnglishFormStock, EnglishRegistrationFee, EnglishReceipt,
    # Private System
    PrivateClass, PrivateStudent, PrivateBook, PrivateUniform, PrivateReceipt
)
from functions import (
    requires_roles, require_password_changed,
    generate_receipt_number, is_valid_email,
    send_reset_email, send_mail_vercel, send_new_user_message
)
import os
import uuid
import time
from constants_file import BASE_URL_FRONTEND
from itsdangerous import URLSafeTimedSerializer
from flask_mail import Mail

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-change-in-production'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///school_management.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['REMEMBER_COOKIE_DURATION'] = timedelta(days=7)

mail = Mail(app)
CORS(app, supports_credentials=True)
db.init_app(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'
s = URLSafeTimedSerializer(app.config['SECRET_KEY'])

app.config['MAIL_SERVER'] = 'smtp.gmail.com'
app.config['MAIL_PORT'] = 587
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME')
app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD')
app.config['MAIL_DEFAULT_SENDER'] = os.environ.get('MAIL_DEFAULT_SENDER')

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

@login_manager.unauthorized_handler
def unauthorized():
    return jsonify({'error': 'Authentication required.'}), 403


# ==================== AUTH ROUTES (Same as before) ====================

@app.route('/api/login', methods=['POST'])
def login():
    data = request.get_json()
    email = data.get('email').lower()
    password = data.get('password')
    
    user = User.query.filter_by(email=email).first()
    if user and user.check_password(password):
        login_user(user)
        return jsonify({
            'success': True,
            'user': {
                'id': user.id,
                'name': user.name,
                'email': user.email,
                'roles': [role.name for role in user.roles],
                'must_change_password': user.must_change_password
            }
        })
    return jsonify({'error': 'Invalid credentials.'}), 401

@app.route('/api/change-password', methods=['POST'])
@login_required
def change_password():
    data = request.get_json()
    current_password = data.get('current_password')
    new_password = data.get('new_password')
    
    if current_user.check_password(current_password):
        if new_password == '123456':
            return jsonify({'error': 'Cannot change back to default password.'}), 400
        if len(new_password) < 6:
            return jsonify({'error': 'Password must be at least 6 characters.'}), 400
        current_user.set_password(new_password)
        current_user.must_change_password = False
        db.session.commit()
        return jsonify({'success': True})
    return jsonify({'error': 'Current password is incorrect.'}), 401

@app.route('/api/logout', methods=['POST'])
@login_required
def logout():
    logout_user()
    return jsonify({'success': True})

@app.route('/api/current-user', methods=['GET'])
@login_required
def current_user_info():
    return jsonify({
        'id': current_user.id,
        'name': current_user.name,
        'email': current_user.email,
        'roles': [role.name for role in current_user.roles],
        'must_change_password': current_user.must_change_password
    })

@app.post('/api/reset-password')
def reset_password():
    data = request.get_json()
    email = data.get('email').lower()
    user = User.query.filter_by(email=email).first()
    if not user:
        time.sleep(3.5)
        return jsonify({'message': 'We have sent a reset-password email if you have an account with us.'}), 200

    unique_uuid = str(uuid.uuid4())
    token = s.dumps(email + str(unique_uuid), salt='password-reset')
    token += str(unique_uuid)
    verification_link = f"{BASE_URL_FRONTEND}/new-password/{token}"

    link_entry = Link_Reset(email=user.email, link=token)
    db.session.add(link_entry)
    db.session.commit()

    msg = send_reset_email(email, verification_link)
    try:
        res = send_mail_vercel(msg)
        if not res.get("status"):
            raise Exception(res.get("error"))
    except Exception as e:
        return jsonify({'error': f'Error: {str(e)}'}), 400
    return jsonify({'message': 'We have sent a reset-password email if you have an account with us.'}), 200

@app.route('/api/new-password/<token>', methods=["POST", "GET"])
def new_password(token):
    if request.method == "GET":
        link_entry = Link_Reset.query.filter_by(link=token).first()
        if not link_entry:
            return jsonify({"error": "Invalid or expired link."}), 403
        time_diff = datetime.utcnow() - link_entry.created_at
        if time_diff > timedelta(minutes=15):
            return jsonify({"error": "The confirmation link has expired."}), 403
        return jsonify({"message": "You can enter new password below."}), 200
    else:
        password = request.form.get("new-password")
        password_conf = request.form.get("confirm-password")
        if password == '123456':
            return jsonify({'error': 'Cannot change back to default password.'}), 400
        if len(password) < 6:
            return jsonify({'error': 'Password must be at least 6 characters.'}), 400
        if password_conf == password:
            link_entry_post = Link_Reset.query.filter_by(link=token).first()
            user = User.query.filter_by(email=link_entry_post.email).first()
            user.set_password(password)            
            link_entry_post.link = str(uuid.uuid4())
            db.session.commit()
            session.clear()
            return jsonify({"message": "Password reset successful."}), 200
        else:
            return jsonify({"error": "Passwords do not match."}), 403


# ==================== USER MANAGEMENT (Same as before) ====================

@app.route('/api/users', methods=['GET'])
@login_required
@requires_roles('Director')
def get_users():
    users = User.query.all()
    return jsonify([{
        'id': u.id,
        'name': u.name,
        'email': u.email,
        'roles': [r.name for r in u.roles],
        'created_at': u.created_at.isoformat()
    } for u in users])

@app.route('/api/users', methods=['POST'])
@login_required
@require_password_changed
@requires_roles('Director')
def create_user():
    data = request.get_json()
    email = data.get('email').lower()
    name = data.get('name')
    
    if User.query.filter_by(email=email).first():
        return jsonify({'error': 'Email already exists.'}), 400
    
    user = User(name=name, email=email, must_change_password=True)
    user.set_password('123456')
    
    role_names = data.get('roles', [])
    for role_name in role_names:
        role = Role.query.filter_by(name=role_name).first()
        if role:
            user.roles.append(role)
    
    msg = send_new_user_message(email, name)
    try:
        res = send_mail_vercel(msg)
        if not res.get("status"):
            raise Exception(res.get("error"))
    except Exception as e:
        return jsonify({'error': f'Error: {str(e)}'}), 400

    db.session.add(user)
    db.session.commit()
    return jsonify({'success': True, 'user_id': user.id})

@app.route('/api/users/<user_id>', methods=['PUT'])
@login_required
@require_password_changed
@requires_roles('Director')
def update_user(user_id):
    user = User.query.get_or_404(user_id)
    data = request.get_json()
    
    if 'name' in data:
        user.name = data['name']
    
    if 'roles' in data:
        current_has_director = any(role.name == 'Director' for role in user.roles)
        new_has_director = 'Director' in data['roles']
        
        if current_has_director and not new_has_director:
            total_directors = User.query.filter(User.roles.any(name='Director')).count()
            if total_directors <= 1:
                return jsonify({'error': 'Cannot remove the only Director from the system'}), 400
        
        user.roles = []
        for role_name in data['roles']:
            role = Role.query.filter_by(name=role_name).first()
            if role:
                user.roles.append(role)
    
    db.session.commit()
    return jsonify({'success': True})

@app.route('/api/users/<user_id>', methods=['DELETE'])
@login_required
@require_password_changed
@requires_roles('Director')
def delete_user(user_id):
    user = User.query.get_or_404(user_id)
    if user.id == current_user.id:
        return jsonify({'error': 'You cannot delete your own account.'}), 400
    is_director = any(role.name == 'Director' for role in user.roles)
    if is_director:
        total_directors = User.query.filter(User.roles.any(name='Director')).count()
        if total_directors <= 1:
            return jsonify({'error': 'Cannot delete the only Director from the system'}), 400
    try:
        db.session.delete(user)
        db.session.commit()
        return jsonify({'success': True})
    except Exception as e:
        db.session.rollback()
        return jsonify({'error': f'Failed to delete user: {str(e)}'}), 500


# ==================== ENGLISH SYSTEM ROUTES ====================

# English Classes
@app.route('/api/english/classes', methods=['GET'])
@login_required
def get_english_classes():
    classes = EnglishClass.query.all()
    return jsonify([{
        'id': c.id,
        'name': c.name,
        'code': c.code,
        'levels': [{'id': l.id, 'name': l.name, 'level_order': l.level_order} for l in c.levels]
    } for c in classes])

# English Students
@app.route('/api/english/students', methods=['GET'])
@login_required
def get_english_students():
    students = EnglishStudent.query.all()
    return jsonify([{
        'id': s.id,
        'name': s.name,
        'phone': s.phone,
        'email': s.email,
        'class_code': s.class_code,
        'level_name': s.level_name,
        'level_order': s.level_order,
        'created_at': s.created_at.isoformat(),
        'updated_at': s.updated_at.isoformat()
    } for s in students])

@app.route('/api/english/students/search', methods=['GET'])
@login_required
def search_english_students():
    query = request.args.get('q', '')
    students = EnglishStudent.query.filter(
        (EnglishStudent.name.contains(query)) | 
        (EnglishStudent.phone.contains(query))
    ).all()
    return jsonify([{
        'id': s.id,
        'name': s.name,
        'phone': s.phone,
        'email': s.email,
        'class_code': s.class_code,
        'level_name': s.level_name,
        'level_order': s.level_order
    } for s in students])

@app.route('/api/english/students', methods=['POST'])
@login_required
@require_password_changed
@requires_roles('Cashier')
def create_english_student():
    data = request.get_json()
    name = (data.get('name') or '').strip()
    phone = (data.get('phone') or '').strip()
    email = (data.get('email') or '').strip()
    
    if not name or not phone:
        return jsonify({'error': 'Name and phone are required'}), 400
    if email and not is_valid_email(email):
        return jsonify({'error': 'Invalid email address'}), 400
    
    student = EnglishStudent(name=name, phone=phone, email=email or None)
    db.session.add(student)
    db.session.commit()
    return jsonify({'success': True, 'student_id': student.id})

@app.route('/api/english/students/<student_id>', methods=['PUT'])
@login_required
@requires_roles('Cashier')
def update_english_student(student_id):
    student = EnglishStudent.query.get_or_404(student_id)
    data = request.get_json()
    
    if 'name' in data:
        student.name = data['name'].strip()
    if 'phone' in data:
        student.phone = data['phone'].strip()
    if 'email' in data:
        email = data['email'].strip()
        if email and not is_valid_email(email):
            return jsonify({'error': 'Invalid email address'}), 400
        student.email = email or None
    
    db.session.commit()
    return jsonify({'success': True})

# English Books
@app.route('/api/english/books', methods=['GET'])
@login_required
def get_english_books():
    books = EnglishBook.query.order_by(EnglishBook.created_at.desc()).all()
    return jsonify([{
        'id': b.id,
        'title': b.title,
        'author': b.author,
        'price': b.price,
        'quantity': b.quantity,
        'class_id': b.class_id,
        'level': b.level,
        'student_status': b.student_status
    } for b in books])

@app.route('/api/english/books', methods=['POST'])
@login_required
@require_password_changed
@requires_roles('InventoryManager')
def create_english_book():
    data = request.get_json()
    errors = {}
    
    title = (data.get('title') or '').strip()
    author = (data.get('author') or '').strip()
    class_id = data.get('class_id')
    level = data.get('level')
    student_status = data.get('student_status')
    
    if not title:
        errors['title'] = 'Title is required'
    if not author:
        errors['author'] = 'Author is required'
    if not class_id:
        errors['class_id'] = 'Class is required'
    if not level:
        errors['level'] = 'Level is required'
    if not student_status or student_status not in ['fresh', 'returning']:
        errors['student_status'] = 'Student status must be selected'
    
    try:
        price = float(data.get('price', 0))
        if price <= 0:
            errors['price'] = 'Price must be greater than 0'
    except (TypeError, ValueError):
        errors['price'] = 'Invalid price'
    
    try:
        quantity = int(data.get('quantity', 0))
        if quantity < 0:
            errors['quantity'] = 'Quantity cannot be negative'
    except (TypeError, ValueError):
        errors['quantity'] = 'Invalid quantity'
    
    if errors:
        return jsonify({'success': False, 'errors': errors}), 400
    
    book = EnglishBook(
        title=title, author=author, price=price, quantity=quantity,
        class_id=class_id, level=level, student_status=student_status
    )
    db.session.add(book)
    db.session.commit()
    return jsonify({'success': True, 'book_id': book.id})

@app.route('/api/english/books/<book_id>', methods=['PUT'])
@login_required
@require_password_changed
@requires_roles('InventoryManager')
def update_english_book(book_id):
    book = EnglishBook.query.get_or_404(book_id)
    data = request.get_json()
    
    if 'title' in data:
        book.title = data['title'].strip()
    if 'author' in data:
        book.author = data['author'].strip()
    if 'price' in data:
        book.price = data['price']
    if 'quantity' in data:
        book.quantity = data['quantity']
    if 'level' in data:
        book.level = data['level']
    if 'student_status' in data:
        book.student_status = data['student_status']
    
    db.session.commit()
    return jsonify({'success': True})

# English Form Stock
@app.route('/api/english/form-stock', methods=['GET'])
@login_required
def get_english_form_stock():
    forms = EnglishFormStock.query.order_by(EnglishFormStock.created_at.desc()).all()
    return jsonify([{
        'id': f.id,
        'class_code': f.class_code,
        'class_name': f.class_name,
        'price': float(f.price),
        'stock_quantity': f.stock_quantity
    } for f in forms])

@app.route('/api/english/form-stock', methods=['POST'])
@login_required
@require_password_changed
@requires_roles('InventoryManager')
def create_english_form_stock():
    data = request.get_json()
    class_code = data.get('class_code')
    price = data.get('price')
    stock_quantity = data.get('stock_quantity')
    
    existing = EnglishFormStock.query.filter_by(class_code=class_code).first()
    if existing:
        return jsonify({'error': 'Form stock already exists for this class'}), 400
    
    english_class = EnglishClass.query.filter_by(code=class_code).first()
    if not english_class:
        return jsonify({'error': 'Invalid class code'}), 400
    
    form_stock = EnglishFormStock(
        class_code=class_code,
        class_name=f"{english_class.code} - {english_class.name}",
        price=price,
        stock_quantity=stock_quantity
    )
    db.session.add(form_stock)
    db.session.commit()
    return jsonify({'success': True})

@app.route('/api/english/form-stock/<int:form_id>', methods=['PUT'])
@login_required
@require_password_changed
@requires_roles('InventoryManager')
def update_english_form_stock(form_id):
    form_stock = EnglishFormStock.query.get_or_404(form_id)
    data = request.get_json()
    form_stock.price = data.get('price')
    form_stock.stock_quantity = data.get('stock_quantity')
    db.session.commit()
    return jsonify({'success': True})

# English Registration Fees
@app.route('/api/english/registration-fees', methods=['GET'])
@login_required
def get_english_registration_fees():
    fees = EnglishRegistrationFee.query.all()
    return jsonify([{
        'id': f.id,
        'class_code': f.class_code,
        'amount': f.amount
    } for f in fees])

@app.route('/api/english/registration-fees', methods=['POST'])
@login_required
@require_password_changed
@requires_roles('InventoryManager')
def set_english_registration_fee():
    data = request.get_json()
    class_code = data.get('class_code')
    amount = data.get('amount')
    
    fee = EnglishRegistrationFee.query.filter_by(class_code=class_code).first()
    if fee:
        fee.amount = amount
    else:
        fee = EnglishRegistrationFee(class_code=class_code, amount=amount)
        db.session.add(fee)
    
    db.session.commit()
    return jsonify({'success': True})

# English Enrollment
@app.route('/api/english/calculate-fees/<class_code>', methods=['GET'])
@login_required
@require_password_changed
@requires_roles('Cashier')
def calculate_english_fees(class_code):
    charge_form_fee = request.args.get('charge_form_fee', 'true').lower() == 'true'
    charge_registration_fee = request.args.get('charge_registration_fee', 'true').lower() == 'true'
    
    form_fee_amount = 0
    registration_fee_amount = 0
    
    if charge_form_fee:
        form_stock = EnglishFormStock.query.filter_by(class_code=class_code).first()
        if form_stock:
            form_fee_amount = float(form_stock.price)
    
    if charge_registration_fee:
        reg_fee = EnglishRegistrationFee.query.filter_by(class_code=class_code).first()
        if reg_fee:
            registration_fee_amount = float(reg_fee.amount)
    
    form_stock = EnglishFormStock.query.filter_by(class_code=class_code).first()
    
    return jsonify({
        'form_fee': form_fee_amount,
        'registration_fee': registration_fee_amount,
        'subtotal': form_fee_amount + registration_fee_amount,
        'has_stock': form_stock.stock_quantity > 0 if form_stock else False,
        'stock_quantity': form_stock.stock_quantity if form_stock else 0
    })

@app.route('/api/english/enroll', methods=['POST'])
@login_required
@require_password_changed
@requires_roles('Cashier')
def enroll_english_student():
    data = request.get_json()
    
    student_id = data.get('student_id')
    new_student_data = data.get('new_student_data')
    class_code = data.get('class_code')
    level_order = data.get('level_order')
    payment_method = data.get('payment_method')
    discount_amount = data.get('discount_amount', 0)
    discount_reason = data.get('discount_reason', '')
    charge_form_fee = data.get('charge_form_fee', True)
    charge_registration_fee = data.get('charge_registration_fee', True)
    
    if discount_amount < 0:
        return jsonify({'error': 'Discount amount cannot be negative'}), 400
    
    student = None
    if new_student_data and not student_id:
        name = (new_student_data.get('name') or '').strip()
        phone = (new_student_data.get('phone') or '').strip()
        email = (new_student_data.get('email') or '').strip()
        
        if not name or not phone:
            return jsonify({'error': 'Name and phone are required'}), 400
        if email and not is_valid_email(email):
            return jsonify({'error': 'Invalid email address'}), 400
        
        student = EnglishStudent(name=name, phone=phone, email=email or None)
        db.session.add(student)
        db.session.flush()
        student_id = student.id
    elif student_id:
        student = EnglishStudent.query.get(student_id)
        if not student:
            return jsonify({'error': 'Student not found'}), 404
    else:
        return jsonify({'error': 'Student information is required'}), 400
    
    english_class = EnglishClass.query.filter_by(code=class_code).first()
    if not english_class:
        return jsonify({'error': 'Invalid class code'}), 400
    
    level = EnglishLevel.query.filter_by(class_code=class_code, level_order=level_order).first()
    if level_order and not level:
        return jsonify({'error': 'Invalid level'}), 400
    
    # Calculate fees
    subtotal = 0
    form_fee_amount = 0
    registration_fee_amount = 0
    form_stock = None
    
    if charge_form_fee:
        form_stock = EnglishFormStock.query.filter_by(class_code=class_code).first()
        if not form_stock:
            return jsonify({'error': f'Form fee not configured for class {class_code}'}), 400
        form_fee_amount = float(form_stock.price)
        subtotal += form_fee_amount
    
    if charge_registration_fee:
        reg_fee = EnglishRegistrationFee.query.filter_by(class_code=class_code).first()
        if not reg_fee:
            return jsonify({'error': f'Registration fee not configured for class {class_code}'}), 400
        registration_fee_amount = float(reg_fee.amount)
        subtotal += registration_fee_amount
    
    if discount_amount > subtotal:
        return jsonify({'error': f'Discount cannot exceed subtotal'}), 400
    
    total_amount = subtotal - discount_amount
    
    if charge_form_fee and form_stock:
        if form_stock.stock_quantity <= 0:
            return jsonify({'error': f'No forms in stock for {class_code}'}), 400
        form_stock.stock_quantity -= 1
    
    receipt = EnglishReceipt(
        receipt_number=generate_receipt_number(),
        student_id=student_id,
        class_code=class_code,
        level_name=level.name if level else None,
        level_order=level_order,
        form_fee_amount=form_fee_amount,
        registration_fee_amount=registration_fee_amount,
        discount_amount=discount_amount,
        discount_reason=discount_reason,
        total_amount=total_amount,
        payment_method=payment_method,
        cashier_id=current_user.id,
        receipt_type='enrollment',
        charge_form_fee=charge_form_fee,
        charge_registration_fee=charge_registration_fee
    )
    
    student.class_code = class_code
    student.level_name = level.name if level else None
    student.level_order = level_order
    student.english_class_id = english_class.id
    
    db.session.add(receipt)
    db.session.commit()
    
    return jsonify({'success': True, 'receipt': receipt.to_dict()})

# English Book Sales
@app.route('/api/english/student-level-books/<student_id>', methods=['GET'])
@login_required
@require_password_changed
@requires_roles('Cashier')
def get_student_level_books(student_id):
    student = EnglishStudent.query.get_or_404(student_id)
    student_status = request.args.get('student_status', 'fresh')
    
    books = EnglishBook.query.filter_by(
        class_id=student.english_class_id,
        level=str(student.level_order) if student.level_order else None,
        student_status=student_status
    ).all()
    
    return jsonify({
        'books': [{
            'id': b.id,
            'title': b.title,
            'author': b.author,
            'price': float(b.price),
            'quantity': b.quantity
        } for b in books]
    })


# ==================== PRIVATE SCHOOL SYSTEM ROUTES ====================

# Private Classes
@app.route('/api/private/classes', methods=['GET'])
@login_required
def get_private_classes():
    classes = PrivateClass.query.all()
    return jsonify([{
        'id': c.id,
        'name': c.name,
        'code': c.code
    } for c in classes])

# Private Students
@app.route('/api/private/students', methods=['GET'])
@login_required
def get_private_students():
    students = PrivateStudent.query.all()
    return jsonify([{
        'id': s.id,
        'name': s.name,
        'phone': s.phone,
        'email': s.email,
        'class_id': s.class_id,
        'class_name': s.private_class.name if s.private_class else None,
        'created_at': s.created_at.isoformat(),
        'updated_at': s.updated_at.isoformat()
    } for s in students])

@app.route('/api/private/students/search', methods=['GET'])
@login_required
def search_private_students():
    query = request.args.get('q', '')
    students = PrivateStudent.query.filter(
        (PrivateStudent.name.contains(query)) | 
        (PrivateStudent.phone.contains(query))
    ).all()
    return jsonify([{
        'id': s.id,
        'name': s.name,
        'phone': s.phone,
        'email': s.email,
        'class_id': s.class_id
    } for s in students])

@app.route('/api/private/students', methods=['POST'])
@login_required
@require_password_changed
@requires_roles('Cashier')
def create_private_student():
    data = request.get_json()
    name = (data.get('name') or '').strip()
    phone = (data.get('phone') or '').strip()
    email = (data.get('email') or '').strip()
    class_id = data.get('class_id')
    
    if not name or not phone or not class_id:
        return jsonify({'error': 'Name, phone, and class are required'}), 400
    if email and not is_valid_email(email):
        return jsonify({'error': 'Invalid email address'}), 400
    
    student = PrivateStudent(name=name, phone=phone, email=email or None, class_id=class_id)
    db.session.add(student)
    db.session.commit()
    return jsonify({'success': True, 'student_id': student.id})

@app.route('/api/private/students/<student_id>', methods=['PUT'])
@login_required
@requires_roles('Cashier')
def update_private_student(student_id):
    student = PrivateStudent.query.get_or_404(student_id)
    data = request.get_json()
    
    if 'name' in data:
        student.name = data['name'].strip()
    if 'phone' in data:
        student.phone = data['phone'].strip()
    if 'email' in data:
        email = data['email'].strip()
        if email and not is_valid_email(email):
            return jsonify({'error': 'Invalid email address'}), 400
        student.email = email or None
    if 'class_id' in data:
        student.class_id = data['class_id']
    
    db.session.commit()
    return jsonify({'success': True})

# Private Books
@app.route('/api/private/books', methods=['GET'])
@login_required
def get_private_books():
    books = PrivateBook.query.order_by(PrivateBook.created_at.desc()).all()
    return jsonify([{
        'id': b.id,
        'title': b.title,
        'author': b.author,
        'price': b.price,
        'quantity': b.quantity,
        'class_id': b.class_id,
        'class_name': b.private_class.name if b.private_class else None
    } for b in books])

@app.route('/api/private/books', methods=['POST'])
@login_required
@require_password_changed
@requires_roles('InventoryManager')
def create_private_book():
    data = request.get_json()
    errors = {}
    
    title = (data.get('title') or '').strip()
    author = (data.get('author') or '').strip()
    class_id = data.get('class_id')
    
    if not title:
        errors['title'] = 'Title is required'
    if not author:
        errors['author'] = 'Author is required'
    if not class_id:
        errors['class_id'] = 'Class is required'
    
    try:
        price = float(data.get('price', 0))
        if price <= 0:
            errors['price'] = 'Price must be greater than 0'
    except (TypeError, ValueError):
        errors['price'] = 'Invalid price'
    
    try:
        quantity = int(data.get('quantity', 0))
        if quantity < 0:
            errors['quantity'] = 'Quantity cannot be negative'
    except (TypeError, ValueError):
        errors['quantity'] = 'Invalid quantity'
    
    if errors:
        return jsonify({'success': False, 'errors': errors}), 400
    
    book = PrivateBook(title=title, author=author, price=price, quantity=quantity, class_id=class_id)
    db.session.add(book)
    db.session.commit()
    return jsonify({'success': True, 'book_id': book.id})

@app.route('/api/private/books/<book_id>', methods=['PUT'])
@login_required
@require_password_changed
@requires_roles('InventoryManager')
def update_private_book(book_id):
    book = PrivateBook.query.get_or_404(book_id)
    data = request.get_json()
    
    if 'title' in data:
        book.title = data['title'].strip()
    if 'author' in data:
        book.author = data['author'].strip()
    if 'price' in data:
        book.price = data['price']
    if 'quantity' in data:
        book.quantity = data['quantity']
    
    db.session.commit()
    return jsonify({'success': True})

# Private Uniforms
@app.route('/api/private/uniforms', methods=['GET'])
@login_required
def get_private_uniforms():
    uniforms = PrivateUniform.query.order_by(PrivateUniform.created_at.desc()).all()
    return jsonify([{
        'id': u.id,
        'title': u.title,
        'price': u.price,
        'quantity': u.quantity,
        'class_id': u.class_id,
        'class_name': u.private_class.name if u.private_class else None
    } for u in uniforms])

@app.route('/api/private/uniforms', methods=['POST'])
@login_required
@require_password_changed
@requires_roles('InventoryManager')
def create_private_uniform():
    data = request.get_json()
    errors = {}
    
    title = (data.get('title') or '').strip()
    class_id = data.get('class_id')
    
    if not title:
        errors['title'] = 'Title is required'
    if not class_id:
        errors['class_id'] = 'Class is required'
    
    try:
        price = float(data.get('price', 0))
        if price <= 0:
            errors['price'] = 'Price must be greater than 0'
    except (TypeError, ValueError):
        errors['price'] = 'Invalid price'
    
    try:
        quantity = int(data.get('quantity', 0))
        if quantity < 0:
            errors['quantity'] = 'Quantity cannot be negative'
    except (TypeError, ValueError):
        errors['quantity'] = 'Invalid quantity'
    
    if errors:
        return jsonify({'success': False, 'errors': errors}), 400
    
    uniform = PrivateUniform(title=title, price=price, quantity=quantity, class_id=class_id)
    db.session.add(uniform)
    db.session.commit()
    return jsonify({'success': True, 'uniform_id': uniform.id})

@app.route('/api/private/uniforms/<uniform_id>', methods=['PUT'])
@login_required
@require_password_changed
@requires_roles('InventoryManager')
def update_private_uniform(uniform_id):
    uniform = PrivateUniform.query.get_or_404(uniform_id)
    data = request.get_json()
    
    if 'title' in data:
        uniform.title = data['title'].strip()
    if 'price' in data:
        uniform.price = data['price']
    if 'quantity' in data:
        uniform.quantity = data['quantity']
    
    db.session.commit()
    return jsonify({'success': True})

# Private Class Books
@app.route('/api/private/class-books/<class_id>', methods=['GET'])
@login_required
@require_password_changed
@requires_roles('Cashier')
def get_private_class_books(class_id):
    books = PrivateBook.query.filter_by(class_id=class_id).all()
    return jsonify({
        'books': [{
            'id': b.id,
            'title': b.title,
            'author': b.author,
            'price': float(b.price),
            'quantity': b.quantity
        } for b in books]
    })

# Private Class Uniforms
@app.route('/api/private/class-uniforms/<class_id>', methods=['GET'])
@login_required
@require_password_changed
@requires_roles('Cashier')
def get_private_class_uniforms(class_id):
    uniforms = PrivateUniform.query.filter_by(class_id=class_id).all()
    return jsonify({
        'uniforms': [{
            'id': u.id,
            'title': u.title,
            'price': float(u.price),
            'quantity': u.quantity
        } for u in uniforms]
    })

# Private Purchases (Books & Uniforms)
@app.route('/api/private/purchase', methods=['POST'])
@login_required
@require_password_changed
@requires_roles('Cashier')
def private_purchase():
    data = request.get_json()
    
    student_id = data.get('student_id')
    new_student_data = data.get('new_student_data')
    items = data.get('items', [])
    payment_method = data.get('payment_method')
    discount_amount = data.get('discount_amount', 0)
    discount_reason = data.get('discount_reason', '')
    
    if discount_amount < 0:
        return jsonify({'error': 'Discount amount cannot be negative'}), 400
    
    if not items:
        return jsonify({'error': 'No items provided'}), 400
    
    try:
        student = None
        
        if new_student_data and not student_id:
            name = (new_student_data.get('name') or '').strip()
            phone = (new_student_data.get('phone') or '').strip()
            email = (new_student_data.get('email') or '').strip()
            class_id = new_student_data.get('class_id')
            
            if not name or not phone or not class_id:
                return jsonify({'error': 'Name, phone, and class are required'}), 400
            if email and not is_valid_email(email):
                return jsonify({'error': 'Invalid email address'}), 400
            
            student = PrivateStudent(name=name, phone=phone, email=email or None, class_id=class_id)
            db.session.add(student)
            db.session.flush()
            student_id = student.id
        elif student_id:
            student = PrivateStudent.query.get(student_id)
            if not student:
                return jsonify({'error': 'Student not found'}), 404
        else:
            return jsonify({'error': 'Student information is required'}), 400
        
        # Process items and calculate totals
        subtotal = 0
        purchase_items = []
        receipt_type = 'books'  # default
        
        for item in items:
            item_type = item.get('type')
            item_id = item.get('id')
            qty = item.get('quantity', 0)
            
            if qty <= 0:
                return jsonify({'error': 'Invalid quantity'}), 400
            
            if item_type == 'book':
                book = PrivateBook.query.get(item_id)
                if not book:
                    return jsonify({'error': 'Book not found'}), 404
                if book.quantity < qty:
                    return jsonify({'error': f'Not enough stock for {book.title}'}), 400
                
                item_total = book.price * qty
                subtotal += item_total
                purchase_items.append({
                    'type': 'book',
                    'id': book.id,
                    'title': book.title,
                    'quantity': qty,
                    'price': float(book.price),
                    'total': float(item_total)
                })
                book.quantity -= qty
                receipt_type = 'both' if receipt_type == 'uniforms' else 'books'
                
            elif item_type == 'uniform':
                uniform = PrivateUniform.query.get(item_id)
                if not uniform:
                    return jsonify({'error': 'Uniform not found'}), 404
                if uniform.quantity < qty:
                    return jsonify({'error': f'Not enough stock for {uniform.title}'}), 400
                
                item_total = uniform.price * qty
                subtotal += item_total
                purchase_items.append({
                    'type': 'uniform',
                    'id': uniform.id,
                    'title': uniform.title,
                    'quantity': qty,
                    'price': float(uniform.price),
                    'total': float(item_total)
                })
                uniform.quantity -= qty
                receipt_type = 'both' if receipt_type == 'books' else 'uniforms'
        
        if discount_amount > subtotal:
            return jsonify({'error': f'Discount cannot exceed subtotal'}), 400
        
        total_amount = subtotal - discount_amount
        
        receipt = PrivateReceipt(
            receipt_number=generate_receipt_number(),
            student_id=student.id,
            receipt_type=receipt_type,
            subtotal=subtotal,
            discount_amount=discount_amount,
            discount_reason=discount_reason,
            total_amount=total_amount,
            payment_method=payment_method,
            cashier_id=current_user.id,
            items=purchase_items
        )
        
        db.session.add(receipt)
        db.session.commit()
        
        return jsonify({'success': True, 'receipt': receipt.to_dict()})
        
    except Exception as e:
        db.session.rollback()
        return jsonify({'error': f'Purchase failed: {str(e)}'}), 500


# ==================== DASHBOARD STATS ====================

@app.route('/api/dashboard/stats', methods=['GET'])
@login_required
def get_dashboard_stats():
    if 'Director' in [r.name for r in current_user.roles]:
        stats = {
            'total_users': User.query.count(),
            'total_english_students': EnglishStudent.query.count(),
            'total_private_students': PrivateStudent.query.count(),
            'total_english_receipts': EnglishReceipt.query.count(),
            'total_private_receipts': PrivateReceipt.query.count(),
        }
    elif 'Cashier' in [r.name for r in current_user.roles]:
        stats = {
            'total_english_students': EnglishStudent.query.count(),
            'total_private_students': PrivateStudent.query.count(),
            'total_english_receipts': EnglishReceipt.query.filter_by(cashier_id=current_user.id).count(),
            'total_private_receipts': PrivateReceipt.query.filter_by(cashier_id=current_user.id).count(),
        }
    else:
        stats = {}
    
    return jsonify(stats)


if __name__ == '__main__':
    with app.app_context():
        db.create_all()
        
        # Create default roles
        roles = ['Director', 'Cashier', 'Analyst', 'InventoryManager']
        for role_name in roles:
            if not Role.query.filter_by(name=role_name).first():
                role = Role(name=role_name)
                db.session.add(role)
        
        # Create default English classes
        english_classes_data = [
            ('NC', 'Normal Class'),
            ('SP', 'Special Class'),
            ('PS', 'Private Special'),
            ('CTC', 'Children Tutorial Class'),
            ('PT', 'Personal Tutorial'),
            ('CBT', 'Computer Based Test'),
            ('WAEC/NECO', 'WAEC/NECO')
        ]
        
        for code, name in english_classes_data:
            if not EnglishClass.query.filter_by(code=code).first():
                english_class = EnglishClass(name=name, code=code)
                db.session.add(english_class)
                
                levels_data = {
                    'NC': ['NC 1', 'NC 2', 'NC 3', 'NC Intermediate', 'NC Post-Intermediate', 'NC Advanced'],
                    'SP': ['Pre-SP', 'SP 1', 'SP 2', 'SP 3', 'SP Intermediate', 'SP Post-Intermediate', 'SP Advanced'],
                    'PS': ['Pre-PS', 'PS 1', 'PS 2', 'PS 3', 'PS Intermediate', 'PS Post-Intermediate', 'PS Advanced'],
                    'CTC': ['Pre-CTC', 'CTC 1', 'CTC 2', 'CTC 3', 'CTC 4']
                }
                
                if code in levels_data:
                    for i, level_name in enumerate(levels_data[code], 1):
                        level = EnglishLevel(name=level_name, class_code=code, level_order=i)
                        db.session.add(level)
        
        # Create default Private classes
        private_classes = [
            'Pre-Nursery', 'Nursery 1', 'Nursery 2', 'Primary 1', 'Primary 2',
            'Primary 3', 'Primary 4', 'Primary 5', 'JSS 1', 'JSS 2', 'JSS 3',
            'SSS 1', 'SSS 2', 'SSS 3'
        ]
        
        for class_name in private_classes:
            if not PrivateClass.query.filter_by(name=class_name).first():
                private_class = PrivateClass(name=class_name, code=class_name.lower().replace(' ', '_'))
                db.session.add(private_class)
        
        # Create admin user
        admin = User.query.filter_by(email='admin@school.com').first()
        if not admin:
            admin = User(name='Admin', email='admin@school.com', must_change_password=False)
            admin.set_password('admin123')
            director_role = Role.query.filter_by(name='Director').first()
            if director_role:
                admin.roles.append(director_role)
            db.session.add(admin)
        
        db.session.commit()
    
    app.run(debug=True, port=5003)