from flask import Blueprint, render_template, request, redirect, url_for, send_file, flash, jsonify
from flask_login import login_required, current_user
from functools import wraps
from cpm_assessment.models import Assessment, Question, Choice, AssessmentRequest, User, Client, AssessmentAssignment, Response
from cpm_assessment import db
from cpm_assessment.models.assessment_invitation import AssessmentInvitation
from cpm_assessment.utils import send_email
import io
from cpm_assessment.forms import CreateClientForm, EditClientForm
from sqlalchemy import or_
from collections import defaultdict

admin_bp = Blueprint('admin', __name__)

def admin_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if not current_user.is_authenticated or current_user.role != 'admin':
            flash('You do not have permission to access this page.', 'danger')
            return redirect(url_for('main.index'))
        return f(*args, **kwargs)
    return decorated_function

@admin_bp.route('/')
@login_required
@admin_required
def dashboard():
    return render_template('admin/dashboard.html')

@admin_bp.route('/reports', methods=['GET', 'POST'])
@login_required
@admin_required
def reports():
    search_query = request.form.get('search_query', '')
    query = AssessmentAssignment.query.filter_by(is_completed=True)

    if search_query:
        query = query.join(Client, isouter=True).join(User, AssessmentAssignment.user, isouter=True).filter(
            or_(
                Client.center_case_id.ilike(f'%{search_query}%'),
                User.registration_number.ilike(f'%{search_query}%')
            )
        )

    completed_assignments = query.order_by(AssessmentAssignment.completed_date.desc()).all()
    return render_template('admin/reports.html', assignments=completed_assignments, search_query=search_query)

@admin_bp.route('/assessments')
@login_required
@admin_required
def manage_assessments():
    assessments = Assessment.query.all()
    return render_template('admin/assessments.html', assessments=assessments)

@admin_bp.route('/assessments/create', methods=['GET', 'POST'])
@login_required
@admin_required
def create_assessment():
    if request.method == 'POST':
        name = request.form['name']
        description = request.form['description']
        is_free = 'is_free' in request.form
        show_domain_wise = 'show_domain_wise' in request.form
        assessment_type = request.form['assessment_type']
        
        price = 0.0
        min_quantity = 1
        if not is_free:
            try:
                price = float(request.form.get('price', 0.0))
                min_quantity = int(request.form.get('min_quantity', 1))
            except (ValueError, TypeError):
                price = 0.0
                min_quantity = 1
        
        new_assessment = Assessment(name=name, description=description, is_free=is_free, assessment_type=assessment_type, show_domain_wise=show_domain_wise, price=price, min_quantity=min_quantity)
        db.session.add(new_assessment)
        db.session.commit()
        return redirect(url_for('admin.manage_assessments'))
    return render_template('admin/create_assessment.html')

@admin_bp.route('/assessments/<int:assessment_id>/edit', methods=['GET', 'POST'])
@login_required
@admin_required
def edit_assessment(assessment_id):
    assessment = Assessment.query.get_or_404(assessment_id)
    if request.method == 'POST':
        assessment.name = request.form['name']
        assessment.description = request.form['description']
        assessment.is_free = 'is_free' in request.form
        assessment.show_domain_wise = 'show_domain_wise' in request.form
        assessment.assessment_type = request.form['assessment_type']
        
        if assessment.is_free:
            assessment.price = 0.0
            assessment.min_quantity = 1
        else:
            try:
                assessment.price = float(request.form.get('price', 0.0))
                assessment.min_quantity = int(request.form.get('min_quantity', 1))
            except (ValueError, TypeError):
                assessment.price = 0.0
                assessment.min_quantity = 1
        
        db.session.commit()
        flash('Assessment updated successfully!', 'success')
        return redirect(url_for('admin.manage_assessments'))
    return render_template('admin/edit_assessment.html', assessment=assessment)

@admin_bp.route('/assessments/<int:assessment_id>/delete', methods=['POST'])
@login_required
@admin_required
def delete_assessment(assessment_id):
    assessment = Assessment.query.get_or_404(assessment_id)
    db.session.delete(assessment)
    db.session.commit()
    flash('Assessment deleted successfully!', 'success')
    return redirect(url_for('admin.manage_assessments'))

@admin_bp.route('/assessments/<int:assessment_id>/questions', methods=['GET', 'POST'])
@login_required
@admin_required
def manage_questions(assessment_id):
    import pandas as pd
    assessment = Assessment.query.get_or_404(assessment_id)
        
    if request.method == 'POST':
        if 'file' in request.files and request.files['file'].filename != '':
            # If a file is uploaded, delete all existing questions for this assessment
            Question.query.filter_by(assessment_id=assessment.id).delete()
            db.session.commit()

            file = request.files['file']
            df = pd.read_excel(file)
            df.columns = df.columns.str.strip()
            for index, row in df.iterrows():
                question_type = row.get('question_type')
                if pd.isna(question_type):
                    question_type = 'multiple_choice'
                new_question = Question(
                    text=row['text'], 
                    question_type=question_type,
                    assessment_id=assessment.id,
                    domain=row.get('domain')
                )
                db.session.add(new_question)
                db.session.flush()

                if new_question.question_type == 'yes_no' and 'yes_choice_text' in row and 'yes_score' in row and 'no_choice_text' in row and 'no_score' in row:
                    try:
                        yes_score = int(row['yes_score'])
                    except ValueError:
                        yes_score = 0
                    new_choice_yes = Choice(text=row['yes_choice_text'].strip(), question_id=new_question.id, weightage=yes_score)
                    db.session.add(new_choice_yes)
                    
                    try:
                        no_score = int(row['no_score'])
                    except ValueError:
                        no_score = 0
                    new_choice_no = Choice(text=row['no_choice_text'].strip(), question_id=new_question.id, weightage=no_score)
                    db.session.add(new_choice_no)
                else:
                    choices_str = row.get('choices_and_scores')
                    if choices_str:
                        for choice_pair in str(choices_str).split('|'):
                            if ':' in choice_pair:
                                choice_text, weightage_str = choice_pair.split(':', 1)
                                try:
                                    weightage = int(weightage_str)
                                except ValueError:
                                    weightage = 0
                                new_choice = Choice(text=choice_text.strip(), question_id=new_question.id, weightage=weightage)
                                db.session.add(new_choice)
            db.session.commit()
            flash('Questions uploaded successfully!', 'success')
            return redirect(url_for('admin.manage_questions', assessment_id=assessment.id))
        else:
            text = request.form['text']
            question_type = request.form['question_type']
            domain = request.form.get('domain')
            choices_str = request.form.get('choices_and_scores')

            new_question = Question(text=text, assessment_id=assessment.id, question_type=question_type, domain=domain)
            db.session.add(new_question)
            db.session.flush()

            if choices_str:
                for choice_pair in choices_str.split('|'):
                    if ':' in choice_pair:
                        choice_text, weightage_str = choice_pair.split(':', 1)
                        try:
                            weightage = int(weightage_str)
                        except ValueError:
                            weightage = 0
                        new_choice = Choice(text=choice_text.strip(), question_id=new_question.id, weightage=weightage)
                        db.session.add(new_choice)
            db.session.commit()
            flash('Question added successfully!', 'success')
            return redirect(url_for('admin.manage_questions', assessment_id=assessment.id))
    
    questions = Question.query.filter_by(assessment_id=assessment.id).all()
    return render_template('admin/manage_questions.html', assessment=assessment, questions=questions)

@admin_bp.route('/assessments/<int:assessment_id>/questions/<int:question_id>/edit', methods=['GET', 'POST'])
@login_required
@admin_required
def edit_question(assessment_id, question_id):
    question = Question.query.get_or_404(question_id)
    if request.method == 'POST':
        question.text = request.form['text']
        question.question_type = request.form['question_type']
        question.domain = request.form.get('domain')
        
        # Clear existing choices
        Choice.query.filter_by(question_id=question.id).delete()
        
        choices_str = request.form.get('choices_and_scores')
        if choices_str:
            for choice_pair in choices_str.split('|'):
                if ':' in choice_pair:
                    choice_text, weightage_str = choice_pair.split(':', 1)
                    try:
                        weightage = int(weightage_str)
                    except ValueError:
                        weightage = 0
                    new_choice = Choice(text=choice_text.strip(), question_id=question.id, weightage=weightage)
                    db.session.add(new_choice)
        
        db.session.commit()
        flash('Question updated successfully!', 'success')
        return redirect(url_for('admin.manage_questions', assessment_id=assessment_id))
    
    choices_str = '|'.join([f"{c.text}:{c.weightage}" for c in question.choices])
    return render_template('admin/edit_question.html', question=question, choices_str=choices_str)

@admin_bp.route('/assessments/<int:assessment_id>/questions/delete', methods=['POST'])
@login_required
@admin_required
def delete_questions(assessment_id):
    question_ids = request.form.getlist('question_ids')
    if not question_ids:
        flash('No questions selected for deletion.', 'warning')
        return redirect(url_for('admin.manage_questions', assessment_id=assessment_id))
    
    Question.query.filter(Question.id.in_(question_ids)).delete(synchronize_session=False)
    db.session.commit()
    flash('Selected questions have been deleted.', 'success')
    return redirect(url_for('admin.manage_questions', assessment_id=assessment_id))

@admin_bp.route('/assessments/<int:assessment_id>/questions/<int:question_id>/delete', methods=['GET'])
@login_required
@admin_required
def delete_question(assessment_id, question_id):
    question = Question.query.get_or_404(question_id)
    db.session.delete(question)
    db.session.commit()
    flash('Question deleted successfully!', 'success')
    return redirect(url_for('admin.manage_questions', assessment_id=assessment_id))

@admin_bp.route('/assessments/<int:assessment_id>/download_template')
@login_required
@admin_required
def download_question_template(assessment_id):
    import pandas as pd
    assessment = Assessment.query.get_or_404(assessment_id)
    
    download_name = f"{assessment.assessment_type.replace(' ', '_')}_question_template.xlsx"

    if assessment.assessment_type == 'Yes/No':
        df = pd.DataFrame({
            'text': ['Sample Yes/No question'],
            'question_type': ['yes_no'],
            'domain': ['General'],
            'yes_choice_text': ['Yes'],
            'yes_score': [1],
            'no_choice_text': ['No'],
            'no_score': [0]
        })
    elif assessment.assessment_type == 'Likert Scale':
        df = pd.DataFrame({
            'text': ['Sample Likert question'],
            'question_type': ['likert'],
            'domain': ['Attitude'],
            'choices_and_scores': ['Strongly Disagree:1|Disagree:2|Neutral:3|Agree:4|Strongly Agree:5']
        })
    else:
        df = pd.DataFrame({
            'text': ['Sample Multiple Choice question', 'Sample Yes/No question', 'Sample Fill-in-the-Blank question'],
            'question_type': ['multiple_choice', 'yes_no', 'blank'],
            'domain': ['Problem Solving', 'Communication', 'General'],
            'choices_and_scores': ['Option A:1|Option B:2|Option C:3', 'Yes:1|No:0', '']
        })

    output = io.BytesIO()
    with pd.ExcelWriter(output, engine='openpyxl') as writer:
        df.to_excel(writer, index=False, sheet_name='questions')
    output.seek(0)
    
    return send_file(
        output,
        download_name=download_name,
        as_attachment=True,
        mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    )

@admin_bp.route('/assessments/<int:assessment_id>/send_link', methods=['POST'])
@login_required
@admin_required
def send_assessment_link(assessment_id):
    assessment = Assessment.query.get_or_404(assessment_id)
    flash('Email sending is disabled. Use generated links instead.', 'info')
    return redirect(url_for('admin.manage_assessments'))

@admin_bp.route('/generate_assessment_link', methods=['POST'])
@login_required
@admin_required
def generate_assessment_link():
    try:
        data = request.get_json()
        if not data:
             return jsonify({'error': 'Invalid JSON data'}), 400
        
        # 'email' is optional for manual link generation
        if 'assessment_id' not in data:
            return jsonify({'error': 'Missing assessment_id'}), 400

        email = data.get('email')
        assessment_id = data['assessment_id']

        assessment = Assessment.query.get(assessment_id)
        if not assessment:
            return jsonify({'error': 'Assessment not found'}), 404

        client = None
        if email:
             client = Client.query.filter_by(email=email).first()
        
        if not client:
            # Create a placeholder client if no email or client not found
            client = Client(email=email, name='Invited Client')
            db.session.add(client)
            db.session.flush() 

        # Use placeholder if email is empty
        recipient_email = email
        if not recipient_email:
             recipient_email = f"client_{client.id}_placeholder@local.test"
             
        invitation = AssessmentInvitation(
            recipient_email=recipient_email,
            assessment_id=assessment.id
        )
        db.session.add(invitation)
        db.session.commit()

        token = invitation.get_assessment_token()
        link = url_for('main.start_invited_assessment', token=token, _external=True)

        return jsonify({'link': link})
    except Exception as e:
        db.session.rollback()
        import traceback
        traceback.print_exc()
        return jsonify({'error': str(e)}), 500

@admin_bp.route('/requests')
@login_required
@admin_required
def manage_requests():
    return redirect(url_for('admin.manage_open_requests'))

def group_requests(requests):
    grouped = defaultdict(lambda: {'requests': [], 'total_cost': 0, 'consultant': None, 'request_date': None, 'payment_slip_url': ''})
    for req in requests:
        group_id = req.payment_slip_url
        group = grouped[group_id]
        group['requests'].append(req)
        group['total_cost'] += req.total_cost
        if not group['consultant']:
            group['consultant'] = req.consultant
        if not group['request_date'] or req.request_date > group['request_date']:
            group['request_date'] = req.request_date
        group['payment_slip_url'] = req.payment_slip_url
    return grouped

@admin_bp.route('/requests/open')
@login_required
@admin_required
def manage_open_requests():
    requests = AssessmentRequest.query.filter_by(status='pending').order_by(AssessmentRequest.request_date.desc()).all()
    grouped_requests = group_requests(requests)
    return render_template('admin/manage_requests.html', requests=grouped_requests, title="Open Assessment Requests", active_tab='open')

@admin_bp.route('/requests/archived')
@login_required
@admin_required
def manage_archived_requests():
    requests = AssessmentRequest.query.filter(AssessmentRequest.status != 'pending').order_by(AssessmentRequest.request_date.desc()).all()
    grouped_requests = group_requests(requests)
    return render_template('admin/manage_requests.html', requests=grouped_requests, title="Archived Assessment Requests", active_tab='archived')

@admin_bp.route('/requests/approve_group', methods=['POST'])
@login_required
@admin_required
def approve_request_group():
    group_id = request.form.get('group_id')
    admin_notes = request.form.get('admin_notes')
    requests_to_update = AssessmentRequest.query.filter_by(payment_slip_url=group_id, status='pending').all()
    
    if not requests_to_update:
        flash('No pending requests found for this group.', 'warning')
        return redirect(url_for('admin.manage_open_requests'))

    for req in requests_to_update:
        req.status = 'approved'
        req.admin_notes = admin_notes
    
    db.session.commit()
    flash(f'Request group approved successfully.', 'success')
    return redirect(url_for('admin.manage_open_requests'))

@admin_bp.route('/requests/reject_group', methods=['POST'])
@login_required
@admin_required
def reject_request_group():
    group_id = request.form.get('group_id')
    admin_notes = request.form.get('admin_notes')

    if not admin_notes:
        flash('A reason is required to reject requests.', 'danger')
        return redirect(url_for('admin.manage_open_requests'))

    requests_to_update = AssessmentRequest.query.filter_by(payment_slip_url=group_id, status='pending').all()

    if not requests_to_update:
        flash('No pending requests found for this group.', 'warning')
        return redirect(url_for('admin.manage_open_requests'))

    for req in requests_to_update:
        req.status = 'rejected'
        req.admin_notes = admin_notes
        
    db.session.commit()
    flash(f'Request group rejected.', 'danger')
    return redirect(url_for('admin.manage_open_requests'))

@admin_bp.route('/users')
@login_required
@admin_required
def manage_users():
    users = User.query.all()
    assessments = Assessment.query.all()
    return render_template('admin/manage_users.html', users=users, assessments=assessments)

@admin_bp.route('/users/<int:user_id>/suspend', methods=['POST'])
@login_required
@admin_required
def suspend_user(user_id):
    user = User.query.get_or_404(user_id)
    if user.role == 'admin':
        flash('Cannot suspend an admin user.', 'danger')
    else:
        user.is_active = False
        db.session.commit()
        flash(f'User {user.username} suspended.', 'success')
    return redirect(url_for('admin.manage_users'))

@admin_bp.route('/users/<int:user_id>/activate', methods=['POST'])
@login_required
@admin_required
def activate_user(user_id):
    user = User.query.get_or_404(user_id)
    user.is_active = True
    db.session.commit()
    flash(f'User {user.username} activated.', 'success')
    return redirect(url_for('admin.manage_users'))

@admin_bp.route('/users/<int:user_id>/delete', methods=['POST'])
@login_required
@admin_required
def delete_user(user_id):
    user = User.query.get_or_404(user_id)
    if user.role == 'admin':
        flash('Cannot delete an admin user.', 'danger')
    else:
        db.session.delete(user)
        db.session.commit()
        flash(f'User {user.username} deleted.', 'success')
    return redirect(url_for('admin.manage_users'))

@admin_bp.route('/users/<int:user_id>/change_password', methods=['POST'])
@login_required
@admin_required
def change_user_password(user_id):
    user = User.query.get_or_404(user_id)
    if user.id == current_user.id:
        flash('Cannot change your own password here.', 'warning')
        return redirect(url_for('admin.manage_users'))
    new_password = request.form.get('new_password', '').strip()
    if not new_password or len(new_password) < 8:
        flash('Password must be at least 8 characters.', 'danger')
        return redirect(url_for('admin.manage_users'))
    from app import bcrypt
    user.password = bcrypt.generate_password_hash(new_password).decode('utf-8')
    db.session.commit()
    flash(f'Password updated for {user.username}.', 'success')
    return redirect(url_for('admin.manage_users'))

@admin_bp.route('/users/<int:user_id>/grant_credits', methods=['POST'])
@login_required
@admin_required
def grant_credits(user_id):
    user = User.query.get_or_404(user_id)
    if user.role != 'consultant':
        flash('Credits can only be granted to consultant accounts.', 'danger')
        return redirect(url_for('admin.manage_users'))
    assessment_id = request.form.get('assessment_id')
    quantity = request.form.get('quantity')
    try:
        assessment = Assessment.query.get_or_404(int(assessment_id))
        qty = int(quantity)
        if qty <= 0:
            raise ValueError()
    except Exception:
        flash('Invalid assessment or quantity.', 'danger')
        return redirect(url_for('admin.manage_users'))
    from app.models import AssessmentRequest
    req = AssessmentRequest(
        consultant_id=user.id,
        assessment_id=assessment.id,
        requested_quantity=qty,
        total_cost=0.0,
        status='approved',
        assigned_count=0,
        admin_notes='Granted by admin'
    )
    db.session.add(req)
    db.session.commit()
    flash(f'Granted {qty} credits for "{assessment.name}" to {user.username}.', 'success')
    return redirect(url_for('admin.manage_users'))
@admin_bp.route('/clients')
@admin_bp.route('/seed_demo_accounts', methods=['POST'])
@login_required
@admin_required
def seed_demo_accounts():
    from app import bcrypt
    demos = [
        ('demo1', 'demo1@vrwellness.in', 'Demo1@123'),
        ('demo2', 'demo2@vrwellness.in', 'Demo2@123'),
        ('demo3', 'demo3@vrwellness.in', 'Demo3@123'),
    ]
    created = []
    for uname, email, pw in demos:
        user = User.query.filter_by(username=uname).first()
        if not user:
            hashed_pw = bcrypt.generate_password_hash(pw).decode('utf-8')
            user = User(username=uname, email=email, password=hashed_pw, role='consultant', is_active=True)
            db.session.add(user)
            db.session.commit()
            created.append(uname)
    # Grant 2 credits for each paid assessment
    from app.models import AssessmentRequest
    paid_assessments = Assessment.query.filter_by(is_free=False).all()
    demo_consultants = User.query.filter(User.username.in_([d[0] for d in demos])).all()
    for consultant in demo_consultants:
        for a in paid_assessments:
            existing_req = AssessmentRequest.query.filter_by(consultant_id=consultant.id, assessment_id=a.id, status='approved').first()
            if not existing_req:
                req = AssessmentRequest(
                    consultant_id=consultant.id,
                    assessment_id=a.id,
                    requested_quantity=2,
                    total_cost=0.0,
                    status='approved',
                    assigned_count=0,
                    admin_notes='Seeded demo credits (admin)'
                )
                db.session.add(req)
    db.session.commit()
    if created:
        flash(f'Created demo accounts: {", ".join(created)}', 'success')
    else:
        flash('Demo accounts already exist; ensured credits are in place.', 'info')
    return redirect(url_for('admin.manage_users'))
@admin_bp.route('/clients')
@login_required
@admin_required
def manage_clients():
    clients = Client.query.all()
    assessments = Assessment.query.all()
    return render_template('admin/clients.html', clients=clients, assessments=assessments)

@admin_bp.route('/clients/create', methods=['GET', 'POST'])
@login_required
@admin_required
def create_client():
    form = CreateClientForm()
    consultants = User.query.filter_by(role='consultant', is_active=True).all()
    form.referred_by.choices = [(c.id, c.username) for c in consultants]
    # No default placeholder; consultant is required

    if form.validate_on_submit():
        referred_by_id = form.referred_by.data
        consultant = User.query.get(referred_by_id)
        new_client = Client(
            name=form.name.data,
            date_of_birth=form.date_of_birth.data,
            email=form.email.data or f"{form.center_case_id.data}@noemail.local",
            phone_number=form.phone_number.data,
            gender=form.gender.data,
            referred_by_id=referred_by_id,
            is_active=form.is_active.data,
            center_case_id=form.center_case_id.data
        )
        db.session.add(new_client)
        db.session.commit()
        flash('Client created successfully!', 'success')
        return redirect(url_for('admin.manage_clients'))
    return render_template('admin/create_client.html', form=form)

@admin_bp.route('/clients/<int:client_id>/edit', methods=['GET', 'POST'])
@login_required
@admin_required
def edit_client(client_id):
    client = Client.query.get_or_404(client_id)
    form = EditClientForm(obj=client)
    consultants = User.query.filter_by(role='consultant', is_active=True).all()
    form.referred_by.choices = [(c.id, c.username) for c in consultants]
    form.referred_by.choices.insert(0, (0, '-- Select Consultant --'))

    if form.validate_on_submit():
        referred_by_id = form.referred_by.data if form.referred_by.data != 0 else None
        client.name = form.name.data
        client.date_of_birth = form.date_of_birth.data
        client.email = form.email.data or f"{form.center_case_id.data}@noemail.local"
        client.phone_number = form.phone_number.data
        client.gender = form.gender.data
        client.referred_by_id = referred_by_id
        client.is_active = form.is_active.data
        client.center_case_id = form.center_case_id.data
        db.session.commit()
        flash('Client updated successfully!', 'success')
        return redirect(url_for('admin.manage_clients'))
    elif request.method == 'GET':
        form.referred_by.data = client.referred_by_id if client.referred_by_id else 0
        form.is_active.data = client.is_active

    return render_template('admin/edit_client.html', form=form, client=client)

@admin_bp.route('/clients/<int:client_id>/suspend', methods=['POST'])
@login_required
@admin_required
def suspend_client(client_id):
    client = Client.query.get_or_404(client_id)
    client.is_active = False
    db.session.commit()
    flash(f'Client {client.name} suspended.', 'success')
    return redirect(url_for('admin.manage_clients'))

@admin_bp.route('/clients/<int:client_id>/activate', methods=['POST'])
@login_required
@admin_required
def activate_client(client_id):
    client = Client.query.get_or_404(client_id)
    client.is_active = True
    db.session.commit()
    flash(f'Client {client.name} activated.', 'success')
    return redirect(url_for('admin.manage_clients'))

@admin_bp.route('/clients/<int:client_id>/delete', methods=['POST'])
@login_required
@admin_required
def delete_client(client_id):
    client = Client.query.get_or_404(client_id)
    db.session.delete(client)
    db.session.commit()
    flash('Client deleted successfully!', 'success')
    return redirect(url_for('admin.manage_clients'))

@admin_bp.route('/assign_users_to_consultants')
@login_required
@admin_required
def assign_users_to_consultants():
    users = User.query.filter_by(role='user', is_active=True).all()
    consultants = User.query.filter_by(role='consultant', is_active=True).all()
    return render_template('admin/assign_users_to_consultants.html', users=users, consultants=consultants)

@admin_bp.route('/assign_user_to_consultant', methods=['POST'])
@login_required
@admin_required
def assign_user_to_consultant():
    user_id = request.form.get('user_id')
    consultant_id = request.form.get('consultant_id')
    
    if not user_id or not consultant_id:
        flash('Please select both a user and a consultant.', 'danger')
        return redirect(url_for('admin.assign_users_to_consultants'))
    
    user = User.query.get_or_404(user_id)
    consultant = User.query.get_or_404(consultant_id)
    
    if consultant.role != 'consultant':
        flash('Selected user is not a consultant.', 'danger')
        return redirect(url_for('admin.assign_users_to_consultants'))
    
    user.assigned_consultant_id = consultant_id
    
    # Check if a Client record already exists for this user
    existing_client = Client.query.filter_by(email=user.email).first()
    if existing_client:
        existing_client.referred_by_id = consultant_id
    else:
        # Create a new Client record
        new_client = Client(
            name=user.username,
            email=user.email,
            date_of_birth=user.date_of_birth,
            phone_number=user.mobile_number,
            gender=user.gender,
            referred_by_id=consultant_id
        )
        db.session.add(new_client)
    
    db.session.commit()
    
    flash(f'User {user.username} has been assigned to consultant {consultant.username}.', 'success')
    return redirect(url_for('admin.assign_users_to_consultants'))

@admin_bp.route('/unassign_user_from_consultant/<int:user_id>', methods=['POST'])
@login_required
@admin_required
def unassign_user_from_consultant(user_id):
    user = User.query.get_or_404(user_id)
    user.assigned_consultant_id = None
    
    # Also unassign the corresponding Client record if it exists
    existing_client = Client.query.filter_by(email=user.email).first()
    if existing_client:
        existing_client.referred_by_id = None
        
    db.session.commit()
    
    flash(f'User {user.username} has been unassigned from their consultant.', 'success')
    return redirect(url_for('admin.assign_users_to_consultants'))

@admin_bp.route('/assign_multiple_assessments_to_client/<int:client_id>', methods=['POST'])
@login_required
@admin_required
def assign_multiple_assessments_to_client(client_id):
    client = Client.query.get_or_404(client_id)

    recipient_email = client.email
    if not recipient_email:
        recipient_email = f"client_{client.id}_placeholder@local.test"
        flash('Client has no email. A placeholder email was used for the invitation link.', 'info')

    assessment_ids = request.form.getlist('assessment_ids')
    
    if not assessment_ids:
        flash('Please select at least one assessment.', 'warning')
        return redirect(url_for('admin.manage_clients'))
    
    generated_links = []
    
    for assessment_id in assessment_ids:
        assessment = Assessment.query.get_or_404(assessment_id)
        
        invitation = AssessmentInvitation(recipient_email=recipient_email, assessment_id=assessment.id)
        db.session.add(invitation)
        db.session.commit()
        
        token = invitation.get_assessment_token()
        link = url_for('main.start_invited_assessment', token=token, _external=True)
        generated_links.append({'assessment_name': assessment.name, 'link': link})
        
        
    
    return render_template('admin/generated_links_display.html', client=client, links=generated_links)

@admin_bp.route('/generated-links')
@login_required
@admin_required
def view_generated_links():
    invitations = AssessmentInvitation.query.filter_by(status='pending').order_by(AssessmentInvitation.created_at.desc()).all()
    return render_template('shared/view_links.html', invitations=invitations, title="All Generated Links")

@admin_bp.route('/client_assessment_report/<int:client_assessment_id>')
@login_required
@admin_required
def view_client_assessment_report(client_assessment_id):
    client_assessment = ClientAssessment.query.get_or_404(client_assessment_id)
    return render_template('admin/client_assessment_report.html', client_assessment=client_assessment)
@admin_bp.route('/assessment_credits')
@login_required
@admin_required
def assessment_credits():
    consultants = User.query.filter_by(role='consultant').all()
    credits = []
    for c in consultants:
        approved = AssessmentRequest.query.filter_by(consultant_id=c.id, status='approved').all()
        by_assessment = defaultdict(lambda: {'approved': 0, 'assigned': 0})
        for req in approved:
            by_assessment[req.assessment_id]['approved'] += req.requested_quantity
            by_assessment[req.assessment_id]['assigned'] += req.assigned_count
        for assessment_id, s in by_assessment.items():
            a = Assessment.query.get(assessment_id)
            if a:
                remaining = max(s['approved'] - s['assigned'], 0)
                credits.append({
                    'consultant': c,
                    'assessment': a,
                    'approved': s['approved'],
                    'assigned': s['assigned'],
                    'remaining': remaining,
                    'is_free': a.is_free
                })
        # Also include free assessments as unlimited entries
        for a in Assessment.query.filter_by(is_free=True).all():
            credits.append({
                'consultant': c,
                'assessment': a,
                'approved': None,
                'assigned': None,
                'remaining': None,
                'is_free': True
            })
    # Sort for readability
    credits.sort(key=lambda x: (x['consultant'].username.lower(), not x['is_free'], x['assessment'].name.lower()))
    return render_template('admin/assessment_credits.html', credits=credits)
