from flask import Blueprint, render_template, request, redirect, url_for, flash, current_app
from flask_login import login_required, current_user
from functools import wraps
from cpm_assessment.models import Client, Assessment, AssessmentInvitation, User, AssessmentRequest, AssessmentAssignment
from cpm_assessment import db, allowed_file
from cpm_assessment.utils import send_email
import os
from datetime import datetime
from werkzeug.utils import secure_filename
from sqlalchemy import or_

consultant_bp = Blueprint('consultant', __name__)

def consultant_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if not current_user.is_authenticated or current_user.role not in ['consultant', '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

@consultant_bp.route('/dashboard')
@login_required
@consultant_required
def dashboard():
    free_assessments = Assessment.query.filter_by(is_free=True).all()
    approved_requests = AssessmentRequest.query.filter_by(consultant_id=current_user.id, status='approved').all()
    summary = {}
    for req in approved_requests:
        s = summary.setdefault(req.assessment_id, {'approved': 0, 'assigned': 0})
        s['approved'] += req.requested_quantity
        s['assigned'] += req.assigned_count
    credit_summary = []
    # Include paid assessments with any approved quantity
    for assessment_id, s in summary.items():
        remaining = max(s['approved'] - s['assigned'], 0)
        assessment = Assessment.query.get(assessment_id)
        if assessment:
            credit_summary.append({
                'assessment': assessment,
                'approved': s['approved'],
                'assigned': s['assigned'],
                'remaining': remaining,
                'is_free': assessment.is_free
            })
    # Add free assessments as unlimited entries
    for a in free_assessments:
        credit_summary.append({
            'assessment': a,
            'approved': None,
            'assigned': None,
            'remaining': None,
            'is_free': True
        })
    # Sort: free first, then by name
    credit_summary.sort(key=lambda x: (not x['is_free'], x['assessment'].name.lower()))
    return render_template('consultant/dashboard.html', credit_summary=credit_summary)

@consultant_bp.route('/reports')
@login_required
@consultant_required
def reports():
    client_ids = [client.id for client in current_user.clients]
    # Also get reports for assigned users
    assigned_user_ids = [user.id for user in User.query.filter_by(assigned_consultant_id=current_user.id).all()]
    
    assignments = AssessmentAssignment.query.filter(
        or_(
            AssessmentAssignment.client_id.in_(client_ids),
            AssessmentAssignment.user_id.in_(assigned_user_ids),
            AssessmentAssignment.user_id == current_user.id  # Include consultant's own assessments
        ),
        AssessmentAssignment.is_completed == True
    ).order_by(AssessmentAssignment.completed_date.desc()).all()
    return render_template('consultant/reports.html', assignments=assignments)

@consultant_bp.route('/my_clients')
@login_required
@consultant_required
def my_clients():
    clients = Client.query.filter_by(referred_by_id=current_user.id).all()
    assigned_users = User.query.filter_by(assigned_consultant_id=current_user.id).all()
    approved_requests = AssessmentRequest.query.filter_by(consultant_id=current_user.id, status='approved').all()
    eligible_paid_ids = {r.assessment_id for r in approved_requests if r.assigned_count < r.requested_quantity}
    assessments = Assessment.query.filter(or_(Assessment.is_free == True, Assessment.id.in_(eligible_paid_ids))).all()
    return render_template('consultant/my_clients.html', clients=clients, assigned_users=assigned_users, assessments=assessments)

@consultant_bp.route('/add_client', methods=['GET', 'POST'])
@login_required
@consultant_required
def add_client():
    from app.utils import consultant_prefix, is_alphanumeric
    if request.method == 'POST':
        name = request.form.get('name')
        email = request.form.get('email')
        date_of_birth_str = request.form.get('date_of_birth')
        phone_number = request.form.get('phone_number')
        gender = request.form.get('gender')
        skip_details_collection = 'skip_details_collection' in request.form
        case_id_suffix = request.form.get('case_id_suffix', '').strip()
        prefix = consultant_prefix(current_user)

        date_of_birth = None
        if date_of_birth_str:
            try:
                date_of_birth = datetime.strptime(date_of_birth_str, '%Y-%m-%d').date()
            except ValueError:
                flash('Invalid date format. Please use YYYY-MM-DD.', 'danger')
                return render_template('consultant/add_client.html', prefix=prefix)

        if not case_id_suffix:
            flash('Client ID suffix is required.', 'danger')
            return render_template('consultant/add_client.html', prefix=prefix)
        # Allow letters, numbers, hyphen, underscore, dot in suffix
        import re
        if not re.fullmatch(r'[A-Za-z0-9._-]+', case_id_suffix):
            flash('Client ID suffix may contain letters, numbers, and symbols - _ .', 'danger')
            return render_template('consultant/add_client.html', prefix=prefix)
        center_case_id = prefix + case_id_suffix

        # Uniqueness check
        exists = Client.query.filter_by(center_case_id=center_case_id).first()
        if exists:
            flash('Client ID already exists. Please use a unique value.', 'danger')
            return render_template('consultant/add_client.html', prefix=prefix)

        new_client = Client(
            name=name,
            email=email or f"{center_case_id}@noemail.local",
            date_of_birth=date_of_birth,
            phone_number=phone_number,
            gender=gender,
            referred_by_id=current_user.id,
            skip_details_collection=skip_details_collection,
            center_case_id=center_case_id
        )
        db.session.add(new_client)
        db.session.commit()
        
        # Send notification to admin
        try:
            html = render_template('email/new_client_notification.html', 
                                   client_name=new_client.name, 
                                   client_email=new_client.email, 
                                   consultant_name=current_user.username)
            send_email(['admin@vrwellness.in', 'swarunimh@gmail.com'], f"New Client Added: {new_client.name}", html)
        except Exception as e:
            print(f"Failed to send admin notification for new client: {e}")

        flash('Client added successfully!', 'success')
        return redirect(url_for('consultant.my_clients'))
    # GET
    return render_template('consultant/add_client.html', prefix=consultant_prefix(current_user))

@consultant_bp.route('/edit_client/<int:client_id>', methods=['GET', 'POST'])
@login_required
@consultant_required
def edit_client(client_id):
    client = Client.query.get_or_404(client_id)
    if current_user.role != 'admin' and client.referred_by_id != current_user.id:
        flash('You do not have permission to edit this client.', 'danger')
        return redirect(url_for('consultant.my_clients'))

    if request.method == 'POST':
        name = request.form.get('name')
        # email = request.form.get('email') # Email cannot be edited
        date_of_birth_str = request.form.get('date_of_birth')
        # phone_number = request.form.get('phone_number') # Phone number cannot be edited
        gender = request.form.get('gender')
        skip_details_collection = 'skip_details_collection' in request.form

        date_of_birth = None
        if date_of_birth_str:
            try:
                date_of_birth = datetime.strptime(date_of_birth_str, '%Y-%m-%d').date()
            except ValueError:
                flash('Invalid date format. Please use YYYY-MM-DD.', 'danger')
                return render_template('consultant/edit_client.html', client=client)

        client.name = name
        # client.email = email
        client.date_of_birth = date_of_birth
        # client.phone_number = phone_number
        client.gender = gender
        client.skip_details_collection = skip_details_collection
        db.session.commit()
        flash('Client updated successfully!', 'success')
        return redirect(url_for('consultant.my_clients'))

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

@consultant_bp.route('/edit_assigned_user/<int:user_id>', methods=['GET', 'POST'])
@login_required
@consultant_required
def edit_assigned_user(user_id):
    user = User.query.get_or_404(user_id)
    if user.assigned_consultant_id != current_user.id:
        flash('You do not have permission to edit this user.', 'danger')
        return redirect(url_for('consultant.my_clients'))

    if request.method == 'POST':
        date_of_birth_str = request.form.get('date_of_birth')
        gender = request.form.get('gender')
        
        date_of_birth = None
        if date_of_birth_str:
            try:
                date_of_birth = datetime.strptime(date_of_birth_str, '%Y-%m-%d').date()
            except ValueError:
                flash('Invalid date format. Please use YYYY-MM-DD.', 'danger')
                return render_template('consultant/edit_assigned_user.html', user=user)

        user.date_of_birth = date_of_birth
        user.gender = gender
        db.session.commit()
        flash('User details updated successfully!', 'success')
        return redirect(url_for('consultant.my_clients'))

    return render_template('consultant/edit_assigned_user.html', user=user)

@consultant_bp.route('/assign_assessment_to_client/<int:client_id>', methods=['POST'])
@login_required
@consultant_required
def assign_assessment_to_client(client_id):
    client = Client.query.get_or_404(client_id)
    if client.referred_by_id != current_user.id:
        flash('You do not have permission to assign assessments to this client.', 'danger')
        return redirect(url_for('consultant.my_clients'))

    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_id = request.form.get('assessment_id')
    if not assessment_id:
        flash('Please select an assessment.', 'warning')
        return redirect(url_for('consultant.my_clients'))

    assessment = Assessment.query.get_or_404(assessment_id)
    if not assessment.is_free:
        req = AssessmentRequest.query\
            .filter_by(consultant_id=current_user.id, assessment_id=assessment.id, status='approved')\
            .filter(AssessmentRequest.assigned_count < AssessmentRequest.requested_quantity)\
            .order_by(AssessmentRequest.request_date.asc())\
            .first()
        if not req:
            flash('You do not have subscription credits for this assessment. Please submit a request or wait for approval.', 'danger')
            return redirect(url_for('consultant.my_clients'))

    # Create an invitation
    invitation = AssessmentInvitation(recipient_email=recipient_email, assessment_id=assessment.id)
    db.session.add(invitation)
    if not assessment.is_free:
        req.assigned_count += 1
    db.session.commit()
    
    # Generate the token and the full URL
    token = invitation.get_assessment_token()
    link = url_for('main.start_invited_assessment', token=token, _external=True)
    
    # Display the generated link to the consultant
    return render_template('consultant/generated_link_display.html', client=client, assessment=assessment, link=link)

@consultant_bp.route('/assign_multiple_assessments/<int:client_id>', methods=['POST'])
@login_required
@consultant_required
def assign_multiple_assessments(client_id):
    client = Client.query.get_or_404(client_id)
    if current_user.role != 'admin' and client.referred_by_id != current_user.id:
        flash('You do not have permission to assign assessments to this client.', 'danger')
        return redirect(url_for('consultant.my_clients'))

    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('consultant.my_clients'))

    generated_links = []
    for assessment_id in assessment_ids:
        assessment = Assessment.query.get_or_404(assessment_id)
        
        req = None
        if not assessment.is_free:
            req = AssessmentRequest.query\
                .filter_by(consultant_id=current_user.id, assessment_id=assessment.id, status='approved')\
                .filter(AssessmentRequest.assigned_count < AssessmentRequest.requested_quantity)\
                .order_by(AssessmentRequest.request_date.asc())\
                .first()
            if not req:
                flash(f'No subscription credits available for "{assessment.name}". Skipping.', 'warning')
                continue

        invitation = AssessmentInvitation(recipient_email=recipient_email, assessment_id=assessment.id)
        db.session.add(invitation)
        if not assessment.is_free and req:
            req.assigned_count += 1
            
        # Flush to generate the ID for the invitation without committing the transaction yet
        db.session.flush()

        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})
        
    # Commit all changes at once to minimize disk I/O
    try:
        db.session.commit()
    except Exception as e:
        db.session.rollback()
        flash(f'Error assigning assessments: {str(e)}', 'danger')
        return redirect(url_for('consultant.my_clients'))

    action = request.form.get('action')
    if action == 'send_email':
        if 'placeholder' in recipient_email and '@local.test' in recipient_email:
            flash('Cannot send email: Client has no email address (placeholder used).', 'warning')
        else:
            try:
                html = render_template('email/client_multiple_assessments_invite.html', client=client, links=generated_links)
                send_email(recipient_email, 'Assessment Invitation', html)
                flash(f'Email sent successfully to {recipient_email}!', 'success')
            except Exception as e:
                flash(f'Error sending email: {str(e)}', 'danger')

    return render_template('consultant/generated_link_display.html', client=client, links=generated_links)

@consultant_bp.route('/assign_assessments_to_user/<int:user_id>', methods=['POST'])
@login_required
@consultant_required
def assign_assessments_to_user(user_id):
    user = User.query.get_or_404(user_id)
    if user.assigned_consultant_id != current_user.id:
        flash('You do not have permission to assign assessments to this user.', 'danger')
        return redirect(url_for('consultant.my_clients'))

    assessment_ids = request.form.getlist('assessment_ids')
    if not assessment_ids:
        flash('Please select at least one assessment.', 'warning')
        return redirect(url_for('consultant.my_clients'))

    generated_links = []
    for assessment_id in assessment_ids:
        assessment = Assessment.query.get_or_404(assessment_id)
        if not assessment.is_free:
            req = AssessmentRequest.query\
                .filter_by(consultant_id=current_user.id, assessment_id=assessment.id, status='approved')\
                .filter(AssessmentRequest.assigned_count < AssessmentRequest.requested_quantity)\
                .order_by(AssessmentRequest.request_date.asc())\
                .first()
            if not req:
                flash(f'No subscription credits available for "{assessment.name}". Skipping.', 'warning')
                continue
        invitation = AssessmentInvitation(recipient_email=user.email, assessment_id=assessment.id)
        db.session.add(invitation)
        if not assessment.is_free:
            req.assigned_count += 1
        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})
        
        

    # Pass a dict with name to avoid template errors if User model property isn't picked up
    client_obj = {'name': user.username}
    return render_template('consultant/generated_link_display.html', client=client_obj, links=generated_links)

@consultant_bp.route('/view_links')
@login_required
@consultant_required
def view_generated_links():
    """Displays active assessment invitations generated by the current consultant."""
    clients = Client.query.filter_by(referred_by_id=current_user.id).all()
    assigned_users = User.query.filter_by(assigned_consultant_id=current_user.id).all()
    
    email_to_name_map = {client.email: client.name for client in clients}
    email_to_case_id_map = {client.email: getattr(client, 'center_case_id', None) for client in clients}
    for user in assigned_users:
        email_to_name_map[user.email] = user.username
        email_to_case_id_map[user.email] = None
        
    all_recipient_emails = list(email_to_name_map.keys())
    
    invitations = AssessmentInvitation.query.filter(
        AssessmentInvitation.recipient_email.in_(all_recipient_emails),
        AssessmentInvitation.status == 'pending'
    ).order_by(AssessmentInvitation.created_at.desc()).all()
    
    for inv in invitations:
        inv.client_name = email_to_name_map.get(inv.recipient_email, inv.recipient_email)
        inv.client_case_id = email_to_case_id_map.get(inv.recipient_email)
        
    return render_template('consultant/view_links.html', invitations=invitations, title="My Generated Links", active_tab='active')

@consultant_bp.route('/view_archived_links')
@login_required
@consultant_required
def view_archived_links():
    """Displays used or expired assessment invitations generated by the current consultant."""
    clients = Client.query.filter_by(referred_by_id=current_user.id).all()
    assigned_users = User.query.filter_by(assigned_consultant_id=current_user.id).all()
    
    email_to_name_map = {client.email: client.name for client in clients}
    email_to_case_id_map = {client.email: getattr(client, 'center_case_id', None) for client in clients}
    for user in assigned_users:
        email_to_name_map[user.email] = user.username
        email_to_case_id_map[user.email] = None
        
    all_recipient_emails = list(email_to_name_map.keys())
    
    invitations = AssessmentInvitation.query.filter(
        AssessmentInvitation.recipient_email.in_(all_recipient_emails),
        AssessmentInvitation.status != 'pending'
    ).order_by(AssessmentInvitation.created_at.desc()).all()
    
    for inv in invitations:
        inv.client_name = email_to_name_map.get(inv.recipient_email, inv.recipient_email)
        inv.client_case_id = email_to_case_id_map.get(inv.recipient_email)
        
    return render_template('consultant/view_links.html', invitations=invitations, title="Archived Links", active_tab='archived')

@consultant_bp.route('/profile', methods=['GET', 'POST'])
@login_required
@consultant_required
def profile():
    if request.method == 'POST':
        current_user.registration_number = request.form.get('registration_number')
        current_user.contact_details = request.form.get('contact_details')
        
        if 'logo' in request.files:
            file = request.files['logo']
            if file.filename != '':
                # Securely save the file
                filename = f"logo_{current_user.id}_{file.filename}"
                filepath = os.path.join(current_app.root_path, 'static/uploads', filename)
                file.save(filepath)
                current_user.logo_filename = filename
        
        db.session.commit()
        flash('Your profile has been updated.', 'success')
        return redirect(url_for('consultant.profile'))
        
    return render_template('consultant/profile.html')

# ----------------------------
# Assessment Requests (Consultant)
# ----------------------------

@consultant_bp.route('/requests')
@login_required
@consultant_required
def manage_requests():
    """Redirect to pending (open) requests view."""
    return redirect(url_for('consultant.manage_open_requests'))

@consultant_bp.route('/requests/open')
@login_required
@consultant_required
def manage_open_requests():
    """List pending assessment requests by the current consultant."""
    requests = AssessmentRequest.query.filter_by(consultant_id=current_user.id, status='pending')\
        .order_by(AssessmentRequest.request_date.desc()).all()
    return render_template('consultant/manage_requests.html', requests=requests, title="Pending Requests", active_tab='open')

@consultant_bp.route('/requests/archived')
@login_required
@consultant_required
def manage_archived_requests():
    """List archived (approved/rejected) assessment requests by the current consultant."""
    requests = AssessmentRequest.query.filter(AssessmentRequest.consultant_id == current_user.id, AssessmentRequest.status != 'pending')\
        .order_by(AssessmentRequest.request_date.desc()).all()
    return render_template('consultant/manage_requests.html', requests=requests, title="Archived Requests", active_tab='archived')

@consultant_bp.route('/requests/new', methods=['GET', 'POST'])
@login_required
@consultant_required
def new_request():
    """Create new assessment requests for paid assessments."""
    assessments = Assessment.query.filter_by(is_free=False).all()

    if request.method == 'POST':
        assessment_ids = request.form.getlist('assessment_ids')
        quantities = request.form.getlist('quantities')
        payment_slip = request.files.get('payment_slip')

        if not assessment_ids or not any(assessment_ids):
            flash('Please add at least one assessment.', 'warning')
            return render_template('consultant/new_request.html', assessments=assessments)

        # Validate and save payment slip
        if not payment_slip or payment_slip.filename == '':
            flash('Please upload a payment slip.', 'danger')
            return render_template('consultant/new_request.html', assessments=assessments)

        if not allowed_file(payment_slip.filename):
            flash('Invalid file type. Allowed: png, jpg, jpeg, pdf.', 'danger')
            return render_template('consultant/new_request.html', assessments=assessments)

        safe_name = secure_filename(payment_slip.filename)
        timestamp = datetime.utcnow().strftime('%Y%m%d%H%M%S')
        stored_name = f"slip_{current_user.id}_{timestamp}_{safe_name}"
        upload_folder = current_app.config.get('UPLOAD_FOLDER')
        os.makedirs(upload_folder, exist_ok=True)
        file_path = os.path.join(upload_folder, stored_name)
        payment_slip.save(file_path)

        total_transaction_cost = 0
        request_items = []
        
        for i, assessment_id in enumerate(assessment_ids):
            if not assessment_id:
                continue

            assessment = Assessment.query.get_or_404(assessment_id)
            if assessment.is_free:
                flash(f'Assessment "{assessment.name}" is free and cannot be requested.', 'warning')
                continue

            try:
                quantity = int(quantities[i])
                min_quantity = assessment.min_quantity or 1
                if quantity < min_quantity:
                    flash(f'Quantity for "{assessment.name}" is below the minimum of {min_quantity}.', 'danger')
                    continue
            except (ValueError, IndexError):
                flash(f'Invalid quantity for "{assessment.name}".', 'danger')
                continue

            price = float(assessment.price or 0.0)
            total_cost = price * quantity
            total_transaction_cost += total_cost
            
            request_items.append({
                'assessment_name': assessment.name,
                'quantity': quantity,
                'cost': total_cost
            })

            req = AssessmentRequest(
                consultant_id=current_user.id,
                assessment_id=assessment.id,
                requested_quantity=quantity,
                total_cost=total_cost,
                payment_slip_url=stored_name,
                status='pending'
            )
            db.session.add(req)

        if not db.session.new:
            flash('No valid assessment requests were created.', 'danger')
            return redirect(url_for('consultant.new_request'))

        db.session.commit()
        
        # Send notification to admin
        try:
            html = render_template('email/new_assessment_request_notification.html', 
                                   consultant_name=current_user.username, 
                                   consultant_email=current_user.email, 
                                   total_cost=total_transaction_cost)
            send_email(['admin@vrwellness.in', 'swarunimh@gmail.com'], f"New Assessment Request from {current_user.username}", html)
        except Exception as e:
            print(f"Failed to send admin notification: {e}")
            
        # Send confirmation to consultant
        try:
            html_consultant = render_template('email/assessment_request_confirmation.html',
                                              consultant_name=current_user.username,
                                              request_items=request_items,
                                              total_cost=total_transaction_cost)
            send_email(current_user.email, "Assessment Request Received", html_consultant)
        except Exception as e:
            print(f"Failed to send consultant confirmation: {e}")

        flash('Assessment requests submitted and pending admin review.', 'success')
        return redirect(url_for('consultant.manage_requests'))

    return render_template('consultant/new_request.html', assessments=assessments)
