from flask import Blueprint, render_template, flash, redirect, url_for, request, make_response, current_app, jsonify
from flask_login import login_required, current_user
from cpm_assessment.models import Assessment, AssessmentAssignment, Client, Question, Choice, User, Response, BlankAnswer
from cpm_assessment.models.assessment_invitation import AssessmentInvitation
from cpm_assessment import db
from cpm_assessment.utils import send_email
from docx import Document
from docx.shared import Inches
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml.ns import nsdecls
from docx.oxml import parse_xml
import io
import os
from werkzeug.utils import secure_filename
import traceback
import tempfile
import base64
from datetime import datetime, timezone, date
from collections import defaultdict
from fpdf import FPDF

# Set a writable directory for matplotlib's config before importing pyplot
os.environ['MPLCONFIGDIR'] = tempfile.gettempdir()
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt


main = Blueprint('main', __name__)

@main.route('/test_mail')
def test_mail():
    try:
        send_email('manasassessment@gmail.com', 'Test Email', '<p>This is a test email from the Flask application.</p>')
        flash('Test email sent successfully!', 'success')
    except Exception as e:
        flash(f'Failed to send test email: {e}', 'danger')
    return redirect(url_for('main.index'))

@main.route('/upload_audio', methods=['POST'])
def upload_audio():
    token = request.form.get('token')
    question_id = request.form.get('question_id')
    assignment = AssessmentAssignment.query.filter_by(unique_token=token).first()
    if not assignment or assignment.is_completed:
        return jsonify({'error': 'invalid_assignment'}), 400
    file = request.files.get('audio')
    if not file:
        return jsonify({'error': 'no_file'}), 400
    safe = secure_filename(file.filename or 'audio.webm')
    ts = datetime.utcnow().strftime('%Y%m%d%H%M%S')
    stored = f"a_{assignment.id}_{question_id}_{ts}_{safe}"
    folder = current_app.config['AUDIO_UPLOAD_FOLDER']
    os.makedirs(folder, exist_ok=True)
    path = os.path.join(folder, stored)
    file.save(path)
    rel = f"audio_recordings/{stored}"
    return jsonify({'url': url_for('static', filename=rel), 'relative': rel})
# VSMS Scoring Tables
VSMS_SCORING_TABLES = {
    "COMMUNICATION": [91.25, 200.75, 339.45, 620.5, 711.75, 1149.75, 1908.95, 2244.75, 3120.75, 3514.95, 3759.5, 4088, 4226.7, 5456.75, 5602.75],
    "SELF-HELP GENERAL": [91.25, 109.5, 109.5, 127.75, 164.25, 200.75, 237.25, 310.25, 474.5, 521.95, 722.7, 1040.25, 1397.95, 2657.2],
    "SELF-HELP EATING": [200.75, 328.5, 401.5, 511, 558.45, 602.25, 675.25, 857.75, 886.95, 2200.95, 2938.25, 3295.95],
    "SELF-HELP DRESSING": [412.45, 748.25, 949, 1040.25, 1222.75, 1397.95, 1697.25, 1752, 2273.95, 2463.75, 3230.25, 3230.25, 4518.7],
    "SELF-DIRECTION": [2127.95, 3423.7, 4179.25, 4745, 5887.45, 6033.45, 6340.05, 6745.2, 6825.5, 7183.2, 7493.45, 7847.5, 9125, 9125],
    "OCCUPATION": [156.95, 401.5, 438, 503.7, 740.95, 1051.2, 1295.75, 1872.45, 1872.45, 3102.5, 3113.45, 3978.5, 4106.25, 5347.25, 6763.45, 9125, 9125, 9125, 9125, 9125, 9125, 9125],
    "LOCOMOTION": [229.95, 375.95, 594.95, 638.75, 1178.95, 1715.5, 2127.95, 3441.95, 5785.25, 6588.25],
    "SOCIALIZATION": [109.5, 255.5, 547.5, 1197.2, 1368.75, 1872.45, 2054.95, 3022.2, 3022.2, 4489.5, 5146.5, 7847.5, 9125, 9125, 9125, 9125, 9125]
}

# T-score mapping for LEVEL 2 Anxiety Adult
ANXIETY_T_SCORES = {
    7: 36.3,
    8: 42.1,
    9: 44.7,
    10: 46.7,
    11: 48.4,
    12: 49.9,
    13: 51.3,
    14: 52.6,
    15: 53.8,
    16: 55.1,
    17: 56.3,
    18: 57.6,
    19: 58.8,
    20: 60.0,
    21: 61.3,
    22: 62.6,
    23: 63.8,
    24: 65.1,
    25: 66.4,
    26: 67.7,
    27: 68.9,
    28: 70.2,
    29: 71.5,
    30: 72.9,
    31: 74.2,
    32: 75.8,
    33: 77.4,
    34: 79.5,
    35: 82.7
}

def get_anxiety_t_score(raw_score):
    # Find the closest T-score for the given raw score
    # If raw_score is less than min key, use min T-score
    # If raw_score is greater than max key, use max T-score
    if raw_score <= min(ANXIETY_T_SCORES.keys()):
        return ANXIETY_T_SCORES[min(ANXIETY_T_SCORES.keys())]
    if raw_score >= max(ANXIETY_T_SCORES.keys()):
        return ANXIETY_T_SCORES[max(ANXIETY_T_SCORES.keys())]

    # Interpolate for scores not directly in the map, or find exact match
    if raw_score in ANXIETY_T_SCORES:
        return ANXIETY_T_SCORES[raw_score]
    else:
        # Find the two closest raw scores in the map
        lower_raw = max(k for k in ANXIETY_T_SCORES if k < raw_score)
        upper_raw = min(k for k in ANXIETY_T_SCORES if k > raw_score)
        
        lower_t = ANXIETY_T_SCORES[lower_raw]
        upper_t = ANXIETY_T_SCORES[upper_raw]

        # Linear interpolation
        t_score = lower_t + (raw_score - lower_raw) * (upper_t - lower_t) / (upper_raw - lower_raw)
        return round(t_score, 1)

def get_anxiety_interpretation(t_score):
    if t_score < 55:
        return "None to slight"
    elif 55 <= t_score <= 59.9:
        return "Mild"
    elif 60 <= t_score <= 69.9:
        return "Moderate"
    else:
        return "Severe"

def get_mania_interpretation(raw_score):
    if raw_score >= 6:
        return "A score of 6 or higher indicates a high probability of a manic or hypomanic condition and may indicate a need for treatment and/or further diagnostic workup."
    else:
        return "A score of 5 or lower is less likely to be associated with significant symptoms of mania."

def get_gaming_interpretation(raw_score):
    if raw_score < 50:
        return "Not addiction, hobby."
    elif 50 <= raw_score <= 79:
        return "Scores in the 50-79 range indicate moderately excessive levels of gameplay."
    else:
        return "Severe addiction."

def get_internet_addiction_interpretation(raw_score):
    if 0 <= raw_score <= 30:
        return "Normal usage"
    elif 31 <= raw_score <= 49:
        return "Mild addiction"
    elif 50 <= raw_score <= 79:
        return "Moderate addiction"
    elif 80 <= raw_score <= 100:
        return "Severe addiction"
    return None

def get_gad7_interpretation(raw_score):
    if 0 <= raw_score <= 4:
        return "Minimal anxiety"
    elif 5 <= raw_score <= 9:
        return "Mild anxiety"
    elif 10 <= raw_score <= 14:
        return "Moderate anxiety"
    elif 15 <= raw_score <= 21:
        return "Severe anxiety"
    return None

def get_ham_a_interpretation(raw_score):
    if raw_score <= 17:
        return "Mild severity"
    elif 18 <= raw_score <= 24:
        return "Mild to moderate severity"
    elif 25 <= raw_score <= 30:
        return "Moderate to severe"
    else:
        return "Severe"

def get_cars_interpretation(raw_score, age):
    if age is None:
        return "Age not provided, cannot determine interpretation."
        
    age_in_years = age
    
    if age_in_years is None:
        return f"Could not parse age: {age}"
        
    if age_in_years < 13:
        if 15 <= raw_score <= 29.5:
            return "Minimal-to-No Symptoms of Autism Spectrum Disorder"
        elif 30 <= raw_score <= 36.5:
            return "Mild-to-Moderate Symptoms of Autism Spectrum Disorder"
        elif raw_score >= 37:
            return "Severe Symptoms of Autism Spectrum Disorder"
    else:
        if 15 <= raw_score <= 27.5:
            return "Minimal-to-No Symptoms of Autism Spectrum Disorder"
        elif 28 <= raw_score <= 34.5:
            return "Mild-to-Moderate Symptoms of Autism Spectrum Disorder"
        elif raw_score >= 35:
            return "Severe Symptoms of Autism Spectrum Disorder"
    return "Score is outside the typical range for interpretation."

def get_vadrs_interpretation(responses):
    interpretations = []
    
    domain_rules = {
        "Inattentive": {"threshold": 6, "max_questions": 9},
        "Hyperactive/Impulsive": {"threshold": 6, "max_questions": 9},
        "Oppositional-Deficient Disorder": {"threshold": 4, "max_questions": 8},
        "Conduct Disorder": {"threshold": 3, "max_questions": 14},
        "Anxiety/Depression": {"threshold": 3, "max_questions": 7},
        "Performance": {"max_questions": 8}
    }

    responses_by_domain = defaultdict(list)
    for r in responses:
        if r.question and r.question.domain:
            for domain_key in domain_rules.keys():
                if domain_key.lower() in r.question.domain.lower():
                    responses_by_domain[domain_key].append(r)
                    break
    
    domain_scores = {domain: 0 for domain in domain_rules.keys()}
    domain_counts = {domain: 0 for domain in domain_rules.keys() if domain != "Performance"}

    for domain, resps in responses_by_domain.items():
        domain_scores[domain] = sum(r.choice.weightage for r in resps if r.choice)
        if domain != "Performance":
            domain_counts[domain] = sum(1 for r in resps if r.choice and r.choice.weightage >= 2)

    performance_responses = responses_by_domain.get("Performance", [])
    performance_score_met = any(r.choice and r.choice.weightage >= 4 for r in performance_responses)

    inattentive_met = domain_counts.get("Inattentive", 0) >= domain_rules["Inattentive"]["threshold"] and performance_score_met
    hyperactive_met = domain_counts.get("Hyperactive/Impulsive", 0) >= domain_rules["Hyperactive/Impulsive"]["threshold"] and performance_score_met

    if inattentive_met:
        interpretations.append("Predominantly Inattentive subtype")
    if hyperactive_met:
        interpretations.append("Predominantly Hyperactive/Impulsive subtype")
    if inattentive_met and hyperactive_met:
        interpretations.append("ADHD Combined Inattention/Hyperactivity")

    if domain_counts.get("Oppositional-Deficient Disorder", 0) >= domain_rules["Oppositional-Deficient Disorder"]["threshold"] and performance_score_met:
        interpretations.append("Oppositional-Deficient Disorder Screen")
    if domain_counts.get("Conduct Disorder", 0) >= domain_rules["Conduct Disorder"]["threshold"] and performance_score_met:
        interpretations.append("Conduct Disorder Screen")
    if domain_counts.get("Anxiety/Depression", 0) >= domain_rules["Anxiety/Depression"]["threshold"] and performance_score_met:
        interpretations.append("Anxiety/Depression Screen")

    report_scores = {
        "Inattentive": domain_scores.get("Inattentive", 0),
        "Hyperactive/Impulsive": domain_scores.get("Hyperactive/Impulsive", 0),
        "Combined Score": domain_scores.get("Inattentive", 0) + domain_scores.get("Hyperactive/Impulsive", 0),
        "Oppositional-Deficient Disorder": domain_scores.get("Oppositional-Deficient Disorder", 0),
        "Conduct Disorder": domain_scores.get("Conduct Disorder", 0),
        "Anxiety/Depression": domain_scores.get("Anxiety/Depression", 0),
        "Performance": domain_scores.get("Performance", 0)
    }

    max_scores = {
        "Inattentive": domain_rules["Inattentive"]["max_questions"] * 3,
        "Hyperactive/Impulsive": domain_rules["Hyperactive/Impulsive"]["max_questions"] * 3,
        "Combined Score": (domain_rules["Inattentive"]["max_questions"] + domain_rules["Hyperactive/Impulsive"]["max_questions"]) * 3,
        "Oppositional-Deficient Disorder": domain_rules["Oppositional-Deficient Disorder"]["max_questions"] * 3,
        "Conduct Disorder": domain_rules["Conduct Disorder"]["max_questions"] * 3,
        "Anxiety/Depression": domain_rules["Anxiety/Depression"]["max_questions"] * 3,
        "Performance": domain_rules["Performance"]["max_questions"] * 5
    }

    return interpretations if interpretations else ["No subtype criteria met."], report_scores, max_scores

def is_authorized_to_view_report(assignment):
    if current_user.role == 'admin':
        return True
    if current_user.role == 'consultant':
        if assignment.client and assignment.client.referred_by_id == current_user.id:
            return True
        if assignment.user_id and assignment.user_id == current_user.id:
            return True
    return False

@main.route('/')
def index():
    assessments = Assessment.query.all()
    return render_template('index.html', assessments=assessments)

@main.route('/start_assessment/<int:assessment_id>')
@login_required
def start_user_assessment(assessment_id):
    assessment = Assessment.query.get_or_404(assessment_id)
    assessment_name = assessment.name.strip().lower()
    
    # If the assessment is 'VSMS', it must be taken via an invitation link.
    if 'vsms' in assessment_name or 'social maturity' in assessment_name:
        flash('You can not use this assessment without an invitation link.', 'danger')
        return redirect(url_for('main.index'))

    else:
        # For all other assessments, use the existing user-based logic
        existing_assignment = AssessmentAssignment.query.filter_by(user_id=current_user.id, assessment_id=assessment.id, is_completed=False).first()
        if existing_assignment:
            flash(f'You already have an active assignment for {assessment.name}. Continuing that assessment.', 'info')
            return redirect(url_for('main.take_assessment', token=existing_assignment.unique_token))

        # Create a new assignment for the logged-in user
        new_assignment = AssessmentAssignment(user_id=current_user.id, assessment_id=assessment.id)
        db.session.add(new_assignment)
        db.session.commit()
        flash(f'Assessment {assessment.name} started.', 'success')
        return redirect(url_for('main.take_assessment', token=new_assignment.unique_token))

@main.route('/assessment/invite/<string:token>', methods=['GET', 'POST'])
def start_invited_assessment(token):
    invitation = AssessmentInvitation.verify_assessment_token(token)

    # 1. Check if invitation is valid and not already fully completed.
    if invitation is None or invitation.status == 'completed':
        flash('This assessment invitation link is invalid, has expired, or has already been used.', 'danger')
        return redirect(url_for('main.index'))

    # 2. Find if an assessment has already been started with this link.
    existing_assignment = AssessmentAssignment.query.filter_by(invitation_id=invitation.id, is_completed=False).first()

    if existing_assignment:
        # If an assignment is already in progress, just redirect to it.
        flash(f'Welcome back! Continuing your {existing_assignment.assessment.name} assessment.', 'info')
        return redirect(url_for('main.take_assessment', token=existing_assignment.unique_token))

    # 3. If we're here, no assignment exists yet. The link must be 'pending' to create one.
    if invitation.status != 'pending':
        # This case handles if status is 'in-progress' but no assignment was found (data inconsistency)
        # or any other unexpected status.
        flash('This assessment invitation link cannot be used to start a new assessment.', 'danger')
        return redirect(url_for('main.index'))

    # 4. Handle GET request: show the confirmation page to start a new assessment.
    if request.method == 'GET':
        assessment = Assessment.query.get(invitation.assessment_id)
        return render_template('confirm_assessment_start.html', assessment=assessment, token=token)

    # 5. Handle POST request: create the assignment and mark the invitation as 'in-progress'.
    if request.method == 'POST':
        client = None
        # Check for placeholder email pattern (for clients without email)
        import re
        match = re.match(r'client_(\d+)_placeholder@local\.test', invitation.recipient_email)
        if match:
            client_id = int(match.group(1))
            client = Client.query.get(client_id)

        if not client:
            # Find or create a client record for the recipient
            client = Client.query.filter_by(email=invitation.recipient_email).first()

        if not client:
            client = Client(email=invitation.recipient_email, name="New Client")
            db.session.add(client)
            db.session.flush()

        # Create the new assignment
        new_assignment = AssessmentAssignment(
            client_id=client.id,
            assessment_id=invitation.assessment_id,
            invitation_id=invitation.id
        )
        db.session.add(new_assignment)
        
        # Mark the invitation as in-progress to prevent creating another assignment
        invitation.status = 'in-progress'
        
        db.session.commit()

        flash(f'Welcome! You may now begin the {new_assignment.assessment.name} assessment.', 'success')
        return redirect(url_for('main.take_assessment', token=new_assignment.unique_token))


@main.route('/take_assessment/<string:token>', methods=['GET', 'POST'])
def take_assessment(token):
    assignment = AssessmentAssignment.query.filter_by(unique_token=token).first_or_404()
    assessment = assignment.assessment

    # Determine if it's a client or a direct user taking the assessment
    if assignment.client_id:
        taker = assignment.client
        is_client_assignment = True
    elif assignment.user_id:
        taker = assignment.user
        is_client_assignment = False
    else:
        flash('Invalid assessment assignment.', 'danger')
        return redirect(url_for('main.index'))

    # After submission, expire the link for subsequent visits
    if assignment.is_completed:
        return render_template('link_used.html', link_type='assessment')

    # If it's a client assignment and details need to be collected/confirmed
    if is_client_assignment and not taker.skip_details_collection:
        if request.method == 'POST':
            name = request.form.get('name')
            dob_str = request.form.get('date_of_birth')
            gender = request.form.get('gender')
            center_case_id = request.form.get('center_case_id')
            phone_number = request.form.get('phone_number')

            if not name or name in ["New Client", "Invited Client"]:
                flash('Please provide a valid name for the client.', 'danger')
                return render_template('client_details.html', taker=taker, assessment=assessment, token=token, is_client_assignment=is_client_assignment)

            if not dob_str or not gender or not center_case_id:
                flash('Name, Date of Birth, Gender, and Center Case ID are required fields.', 'danger')
                return render_template('client_details.html', taker=taker, assessment=assessment, token=token, is_client_assignment=is_client_assignment)

            taker.name = name
            taker.email = request.form.get('email', taker.email)
            try:
                taker.date_of_birth = datetime.strptime(dob_str, '%Y-%m-%d').date()
            except (ValueError, TypeError):
                flash('Invalid date format for Date of Birth. Please use YYYY-MM-DD.', 'danger')
                return render_template('client_details.html', taker=taker, assessment=assessment, token=token, is_client_assignment=is_client_assignment)
            taker.phone_number = phone_number
            taker.gender = gender
            # Validate center_case_id prefix if consultant assigned
            from app.utils import consultant_prefix, is_alphanumeric
            if not is_alphanumeric(center_case_id):
                flash('Center Case ID must be alphanumeric.', 'danger')
                return render_template('client_details.html', taker=taker, assessment=assessment, token=token, is_client_assignment=is_client_assignment)
            if taker.referred_by_id:
                consultant = User.query.get(taker.referred_by_id)
                prefix = consultant_prefix(consultant)
                if center_case_id[:len(prefix)].upper() != prefix:
                    flash(f'Center Case ID must start with the consultant prefix "{prefix}".', 'danger')
                    return render_template('client_details.html', taker=taker, assessment=assessment, token=token, is_client_assignment=is_client_assignment)
            taker.center_case_id = center_case_id
            taker.skip_details_collection = True
            db.session.commit()
            flash('Client details updated.', 'success')
            return redirect(url_for('main.take_assessment', token=token))
        
        # For GET request, show details form if not skipped
        return render_template('client_details.html', taker=taker, assessment=assessment, token=token, is_client_assignment=is_client_assignment)

    # If details are skipped or already confirmed, proceed to questions
    questions = Question.query.filter_by(assessment_id=assessment.id).order_by(Question.domain).all()
    name_lower = assessment.name.strip().lower()
    if 'cbcl' in name_lower:
        for q in questions:
            seen = set()
            unique = []
            for c in q.choices:
                key = ' '.join(c.text.strip().lower().split())
                if key not in seen:
                    seen.add(key)
                    unique.append(c)
            q.choices_unique = unique
    else:
        for q in questions:
            q.choices_unique = list(q.choices)

    # Group questions by domain to display in tabs
    questions_by_domain = {}
    for q in questions:
        # Use 'General' as a fallback for questions without a specified domain
        domain = q.domain if q.domain else "General"
        if domain not in questions_by_domain:
            questions_by_domain[domain] = []
        questions_by_domain[domain].append(q)
    
    # Convert to list of tuples for template loop
    questions_by_domain_list = sorted(questions_by_domain.items())

    return render_template('take_assessment.html', assignment=assignment, taker=taker, assessment=assessment, questions_by_domain=questions_by_domain_list, is_client_assignment=is_client_assignment)

def get_isaa_summary(domain_scores):
    """
    Generates a concise narrative summary for the ISAA assessment based on domain scores.
    """
    parts = []
    for domain, score in domain_scores.items():
        if score <= 1:
            level = "requiring support"
        elif score == 2:
            level = "requiring substantial support"
        else:
            level = "requiring very substantial support"
        parts.append(f"{domain} ({level}, score {score})")
    if not parts:
        return "No domain scores were available to summarize."
    return (
        "Based on the ISAA results, the individual presents varied support needs across domains: "
        + "; ".join(parts)
        + ". These findings can guide targeted interventions and monitoring over time."
    )

def calculate_scores(assignment):
    """
    Calculates the total raw score, domain_scores, t_score, and interpretation for an assessment assignment.
    """
    total_raw_score = 0
    domain_scores = {}
    max_domain_scores = {}
    t_score = None
    interpretation = None
    summary = None
    assessment_name = assignment.assessment.name.strip().lower()

    # Calculate age for CARS if applicable
    age = None
    calc_date = assignment.completed_date.date() if assignment.completed_date else datetime.now().date()

    if hasattr(assignment.client, 'date_of_birth') and assignment.client.date_of_birth:
        age = calc_date.year - assignment.client.date_of_birth.year - ((calc_date.month, calc_date.day) < (assignment.client.date_of_birth.month, assignment.client.date_of_birth.day))
    elif hasattr(assignment.user, 'date_of_birth') and assignment.user.date_of_birth:
        age = calc_date.year - assignment.user.date_of_birth.year - ((calc_date.month, calc_date.day) < (assignment.user.date_of_birth.month, assignment.user.date_of_birth.day))


    if 'ciss' in assessment_name:
        for question in assignment.assessment.questions:
            if question.domain:
                max_domain_scores[question.domain] = max_domain_scores.get(question.domain, 0) + 5
        for response in assignment.responses:
            question = response.question
            choice = response.choice
            if question and choice:
                total_raw_score += choice.weightage
                if question.domain:
                    domain_scores[question.domain] = domain_scores.get(question.domain, 0) + choice.weightage

    elif 'vadrs' in assessment_name:
        interpretation, domain_scores, max_domain_scores = get_vadrs_interpretation(assignment.responses)
        total_raw_score = sum(domain_scores.values())
    elif any(x in assessment_name for x in ['dst', 'ddst', 'dsst', 'developmental screening']):
        for response in assignment.responses:
            question = response.question
            choice = response.choice
            # Assuming 'Yes' means the milestone is met and the choice's weightage is the score in days.
            if question and choice and choice.text.strip().lower() == 'yes':
                score_val = choice.weightage
                domain = question.domain or "General"
                domain_scores[domain] = domain_scores.get(domain, 0) + score_val
        
        # total_raw_score for DST is the sum of domain scores (Developmental Age in days)
        if domain_scores:
            total_raw_score = sum(domain_scores.values())
        else:
            total_raw_score = 0
    elif 'vsms' in assessment_name or 'social maturity' in assessment_name:
        taker = assignment.client or assignment.user
        age_in_months = 0
        if hasattr(taker, 'date_of_birth') and taker.date_of_birth:
            today = datetime.now().date()
            age_in_months = (today.year - taker.date_of_birth.year) * 12 + (today.month - taker.date_of_birth.month)
            if today.day < taker.date_of_birth.day:
                age_in_months -= 1

        # Get all unique domains from the assessment questions
        all_domains = sorted(list(set(q.domain for q in assignment.assessment.questions if q.domain)))

        # Determine active domains based on age
        active_domains = all_domains
        if age_in_months < 70: # 5 years 10 months = 70 months
            active_domains = [d for d in all_domains if d.strip().upper() != 'SELF-DIRECTION']

        # Initialize yes counts for all active domains to 0
        yes_counts = {domain: 0 for domain in active_domains}

        # Count "Yes" answers for each domain
        for response in assignment.responses:
            question = response.question
            choice = response.choice

            if question and choice and question.domain in active_domains:
                if choice.text.strip().lower() == 'yes':
                    yes_counts[question.domain] += 1

        # Create a mapping of normalized table keys to their original form
        normalized_table_keys = {key.upper().replace("-", "").replace(" ", ""): key for key in VSMS_SCORING_TABLES.keys()}

        # Look up the score from the tables based on the count of "Yes" answers
        for domain in active_domains:
            count = yes_counts.get(domain, 0)
            
            # Normalize the database domain name for lookup
            normalized_domain = domain.upper().replace("-", "").replace(" ", "").split('(')[0]
            if normalized_domain == "SOCIALISATION": # Handle spelling
                normalized_domain = "SOCIALIZATION"

            # Find the original key from the normalized map
            original_key = normalized_table_keys.get(normalized_domain)

            if count > 0 and original_key and count <= len(VSMS_SCORING_TABLES[original_key]):
                domain_scores[domain] = VSMS_SCORING_TABLES[original_key][count - 1]
            else:
                domain_scores[domain] = 0
        
        total_raw_score = sum(domain_scores.values())
        t_score = None
        interpretation = None

    elif 'isaa' in assessment_name:
        for question in assignment.assessment.questions:
            if question.domain:
                max_domain_scores[question.domain] = max_domain_scores.get(question.domain, 0) + 5
        for response in assignment.responses:
            question = response.question
            choice = response.choice
            if question and choice:
                total_raw_score += choice.weightage
                if question.domain:
                    domain_scores[question.domain] = domain_scores.get(question.domain, 0) + choice.weightage
        if 'isaa1' not in assessment_name:
            summary = get_isaa_summary(domain_scores)

    elif 'cars' in assessment_name:
        for response in assignment.responses:
            question = response.question
            choice = response.choice
            if question and choice:
                total_raw_score += choice.weightage
        interpretation = get_cars_interpretation(total_raw_score, age)
        # Do not populate domain_scores for CARS
        domain_scores = {}
        max_domain_scores = {}
    elif 'cbcl' in assessment_name:
        for response in assignment.responses:
            question = response.question
            choice = response.choice
            if question and choice:
                total_raw_score += choice.weightage
                if question.domain:
                    domain_scores[question.domain] = domain_scores.get(question.domain, 0) + choice.weightage
        # No t-score or general interpretation here; handled as composites later
        max_domain_scores = {}

    else: # General assessments, including GAD-7, Internet Addiction, Level 2 Anxiety Adult
        for response in assignment.responses:
            question = response.question
            choice = response.choice

            if question and choice:
                total_raw_score += choice.weightage
        
        if assessment_name == "level 2 anxiety adult":
            t_score = get_anxiety_t_score(total_raw_score)
            interpretation = get_anxiety_interpretation(t_score)
            domain_scores = {} # Ensure no domain scores for this assessment
            max_domain_scores = {}
        elif assessment_name == "altman self rating mania scale":
            interpretation = get_mania_interpretation(total_raw_score)
        elif assessment_name == "excessive gaming screening tool":
            interpretation = get_gaming_interpretation(total_raw_score)
        elif assessment_name == "internet addiction assessment":
            interpretation = get_internet_addiction_interpretation(total_raw_score)
            domain_scores = {} # Ensure no domain scores for this assessment
            max_domain_scores = {}
        elif assessment_name == "gad 7":
            interpretation = get_gad7_interpretation(total_raw_score)
            domain_scores = {} # Ensure no domain scores for this assessment
            max_domain_scores = {}
        elif 'ham a' in assessment_name:
            interpretation = get_ham_a_interpretation(total_raw_score)

    return total_raw_score, domain_scores, max_domain_scores, t_score, interpretation, summary


@main.route('/submit_assessment/<string:token>', methods=['POST'])
def submit_assessment(token):
    assignment = AssessmentAssignment.query.filter_by(unique_token=token).first_or_404()
    assessment = assignment.assessment
    assessment_name = assessment.name.strip().lower()

    # For VADRS/ADHD assessment, ensure all questions are answered
    if 'vadrs' in assessment_name or 'adhd' in assessment_name:
        num_questions = len(assessment.questions)
        answered_questions = [key for key in request.form if key.startswith('question_')]
        if len(answered_questions) < num_questions:
            flash('Please answer all questions before submitting.', 'danger')
            return redirect(url_for('main.take_assessment', token=token))

    # Delete existing responses for this assignment to handle re-submissions
    Response.query.filter_by(assessment_assignment_id=assignment.id).delete()
    BlankAnswer.query.filter_by(assessment_assignment_id=assignment.id).delete()
    db.session.commit()

    # For ISAA assessment, ensure at least 2 questions per domain are answered
    if 'isaa' in assessment_name:
        questions_by_domain = defaultdict(list)
        for q in assessment.questions:
            questions_by_domain[q.domain or 'General'].append(q.id)

        answered_questions_by_domain = defaultdict(int)
        for key in request.form:
            if key.startswith('question_'):
                question_id = int(key.split('_')[1])
                question = Question.query.get(question_id)
                if question:
                    answered_questions_by_domain[question.domain or 'General'] += 1
        
        for domain, questions in questions_by_domain.items():
            if answered_questions_by_domain.get(domain, 0) < 2:
                flash(f'Please answer at least 2 questions in the "{domain}" section before submitting.', 'danger')
                return redirect(url_for('main.take_assessment', token=token))

    # Process new responses
    for question in assessment.questions:
        if getattr(question, 'question_type', '') == 'blank':
            txt = request.form.get(f'blank_text_{question.id}', '').strip()
            audio_rel = request.form.get(f'blank_audio_{question.id}', '').strip()
            ba = BlankAnswer(
                assessment_assignment_id=assignment.id,
                question_id=question.id,
                text_answer=txt or None,
                audio_url=audio_rel or None,
                transcript=txt or None
            )
            db.session.add(ba)
        else:
            choice_id = request.form.get(f'question_{question.id}')
            if choice_id:
                choice = Choice.query.get(choice_id)
                if choice and choice.question_id == question.id:
                    new_response = Response(
                        client_id=assignment.client_id,
                        user_id=assignment.user_id,
                        assessment_id=assessment.id,
                        assessment_assignment_id=assignment.id,
                        question_id=question.id,
                        choice_id=choice.id
                    )
                    db.session.add(new_response)
    
    # Save therapist notes if present
    therapist_notes = request.form.get('therapist_notes')
    if therapist_notes:
        assignment.therapist_notes = therapist_notes

    assignment.is_completed = True
    assignment.completed_date = datetime.now(timezone.utc)
    # If this assignment came from an invitation, mark that invitation as completed now
    if assignment.invitation:
        assignment.invitation.status = 'completed'
        assignment.invitation.completed_at = datetime.now(timezone.utc)
    
    # Commit all changes (add new, update assignment)
    db.session.commit()

    # Re-fetch the assignment to ensure the 'responses' collection is fresh
    fresh_assignment = AssessmentAssignment.query.get(assignment.id)

    if fresh_assignment.client_id:
        taker = fresh_assignment.client
        is_client_assignment = True
    elif fresh_assignment.user_id:
        taker = fresh_assignment.user
        is_client_assignment = False
    else:
        flash('Invalid assessment assignment.', 'danger')
        return redirect(url_for('main.index'))

    # Send completion email
    if assignment.client and assignment.client.referred_by:
        consultant = assignment.client.referred_by
        html = render_template('email/assessment_completion_notification.html', 
                               recipient_name=consultant.name,
                               assessment_name=assessment.name,
                               taker_name=assignment.client.name,
                               completion_date=assignment.completed_date.strftime('%Y-%m-%d'))
        try:
            send_email(consultant.email, f"Assessment Completed: {assessment.name}", html)
        except Exception as e:
            print(f"Failed to send consultant completion notification: {e}")

    # Send copy to admin
    try:
        admin_html = render_template('email/assessment_completion_notification.html', 
                               recipient_name="Admin",
                               assessment_name=assessment.name,
                               taker_name=taker.name if taker else "Unknown",
                               completion_date=assignment.completed_date.strftime('%Y-%m-%d'))
        send_email('manasassessment@gmail.com', f"Assessment Completed: {assessment.name} (Copy)", admin_html)
    except Exception as e:
        print(f"Failed to send admin completion notification: {e}")

    flash('Assessment completed successfully!', 'success')
    
    # Compute age in months and days for template and display fields
    taker_age_in_months = None
    taker_age_in_days = None
    try:
        if getattr(taker, 'date_of_birth', None):
            calc_date = assignment.completed_date.date() if assignment.completed_date else datetime.now().date()
            
            # Calculate months
            months = (calc_date.year - taker.date_of_birth.year) * 12 + (calc_date.month - taker.date_of_birth.month)
            if calc_date.day < taker.date_of_birth.day:
                months -= 1
            taker_age_in_months = months
            
            # Calculate days
            delta = calc_date - taker.date_of_birth
            taker_age_in_days = delta.days
    except Exception:
        taker_age_in_months = None
        taker_age_in_days = None

    total_raw_score, domain_scores, max_domain_scores, t_score, interpretation, summary = calculate_scores(fresh_assignment)

    # Calculate DQ for DST/DDST/DSST if in days
    dst_dq = None
    name_lower = assessment.name.strip().lower()
    if any(x in name_lower for x in ['dst', 'ddst', 'dsst', 'developmental screening']) and taker_age_in_days:
        # total_raw_score for DST is already the Developmental Age in days
        dst_dq = (total_raw_score / taker_age_in_days) * 100
        
        # Classification for DST
        if dst_dq >= 130: interpretation = "Very Superior"
        elif dst_dq >= 120: interpretation = "Superior"
        elif dst_dq >= 110: interpretation = "High Average"
        elif dst_dq >= 90: interpretation = "Average"
        elif dst_dq >= 80: interpretation = "Low Average"
        elif dst_dq >= 70: interpretation = "Borderline"
        elif dst_dq >= 50: interpretation = "Mild Development Delay"
        elif dst_dq >= 35: interpretation = "Moderate Development Delay"
        elif dst_dq >= 20: interpretation = "Severe Development Delay"
        else: interpretation = "Profound Development Delay"
    
    cbcl_composites = None
    cbcl_interpretations = None
    cbcl_domain_interpretations = None
    if 'cbcl' in name_lower:
        def norm(s):
            return ' '.join(s.strip().lower().replace('/', ' ').replace('-', ' ').split())
        internalizing_keys = {
            'emotionally reactive',
            'anxious depressed',
            'somatic complaints',
            'somatic complains',
            'withdrawn'
        }
        externalizing_keys = {
            'attention problem',
            'aggressive behaviour',
            'aggressive behavior'
        }
        other_keys = {
            'other problems',
            'sleep problems'
        }
        domain_thresholds = {
            'emotionally reactive': [(0, 5, 'Normal'), (6, 8, 'Borderline Clinical'), (9, 18, 'Clinical')],
            'anxious depressed': [(0, 6, 'Normal'), (7, 8, 'Borderline Clinical'), (9, 16, 'Clinical')],
            'somatic complaints': [(0, 4, 'Normal'), (5, 6, 'Borderline Clinical'), (7, 22, 'Clinical')],
            'somatic complains': [(0, 4, 'Normal'), (5, 6, 'Borderline Clinical'), (7, 22, 'Clinical')],
            'withdrawn': [(0, 4, 'Normal'), (5, 5, 'Borderline Clinical'), (6, 16, 'Clinical')],
            'sleep problems': [(0, 7, 'Normal'), (8, 8, 'Borderline Clinical'), (9, 14, 'Clinical')],
            'attention problem': [(0, 5, 'Normal'), (6, 6, 'Borderline Clinical'), (7, 10, 'Clinical')],
            'aggressive behaviour': [(0, 20, 'Normal'), (21, 24, 'Borderline Clinical'), (25, 38, 'Clinical')],
            'aggressive behavior': [(0, 20, 'Normal'), (21, 24, 'Borderline Clinical'), (25, 38, 'Clinical')]
        }
        allowed_domains = {
            'emotionally reactive',
            'anxious depressed',
            'somatic complaints',
            'somatic complains',
            'withdrawn',
            'sleep problems',
            'attention problem',
            'aggressive behaviour',
            'aggressive behavior',
            'other problems'
        }
        domain_scores = {d: s for d, s in domain_scores.items() if norm(d) in allowed_domains}
        internalizing = 0
        externalizing = 0
        total_other = 0
        cbcl_domain_interpretations = {}
        for d, s in domain_scores.items():
            key = norm(d)
            if key in internalizing_keys:
                internalizing += s
            if key in externalizing_keys:
                externalizing += s
            if key in other_keys:
                total_other += s
            th = domain_thresholds.get(key)
            if th:
                interp = None
                for lo, hi, label in th:
                    if lo <= s <= hi:
                        interp = label
                        break
                cbcl_domain_interpretations[d] = interp or 'N/A'
        total_score_all = sum(domain_scores.values())
        def classify_internalizing(x):
            if x <= 13:
                return 'Normal'
            if 14 <= x <= 17:
                return 'Borderline Clinical'
            return 'Clinical'
        def classify_externalizing(x):
            if x <= 20:
                return 'Normal'
            if 21 <= x <= 24:
                return 'Borderline Clinical'
            return 'Clinical'
        def classify_total(x):
            if x <= 51:
                return 'Normal'
            if 52 <= x <= 60:
                return 'Borderline Clinical'
            return 'Clinical'
        cbcl_composites = {
            'internalizing': internalizing,
            'externalizing': externalizing,
            'total_other_problems': total_other,
            'total_score': total_score_all
        }
        cbcl_interpretations = {
            'internalizing': classify_internalizing(internalizing),
            'externalizing': classify_externalizing(externalizing),
            'total_other_problems': None,
            'total_score': classify_total(total_score_all)
        }
    
    return render_template('assessment_completed.html', 
                           assignment=fresh_assignment,
                           assessment=assessment,
                           taker=taker, 
                           is_client_assignment=is_client_assignment, 
                           domain_scores=domain_scores, 
                           max_domain_scores=max_domain_scores, 
                           total_raw_score=total_raw_score, 
                           t_score=t_score, 
                           interpretation=interpretation, 
                           summary=summary,
                           taker_age_in_months=taker_age_in_months,
                           taker_age_in_days=taker_age_in_days,
                           dst_dq=dst_dq,
                           is_free=assessment.is_free,
                           cbcl_composites=cbcl_composites,
                           cbcl_interpretations=cbcl_interpretations,
                           cbcl_domain_interpretations=cbcl_domain_interpretations)

@main.route('/report/<int:assignment_id>')
@login_required
def view_report(assignment_id):
    """
    Displays the detailed report for a completed assessment.
    Admins can see any report. Consultants can only see reports for their own clients.
    """
    assignment = AssessmentAssignment.query.get_or_404(assignment_id)

    if not is_authorized_to_view_report(assignment):
        flash('You do not have permission to view this report.', 'danger')
        return redirect(url_for('consultant.dashboard') if current_user.role == 'consultant' else url_for('main.index'))

    if not assignment.is_completed:
        flash('This assessment has not been completed yet.', 'info')
        if current_user.role == 'admin':
            return redirect(url_for('admin.reports'))
        else:
            return redirect(url_for('consultant.reports'))

    assessment = assignment.assessment
    if 'ciss' in assessment.name.lower():
        assessment.name = 'Coping Inventory for Stressful Situations (CISS)'

    taker = assignment.client if assignment.client_id else assignment.user
    is_client_assignment = True if assignment.client_id else False
    # Compute age in months and days for template and display fields
    taker_age_in_months = None
    taker_age_in_days = None
    try:
        if getattr(taker, 'date_of_birth', None):
            # Use completed_date if available, else today
            if assignment.completed_date:
                calc_date = assignment.completed_date.date()
            else:
                calc_date = date.today()
            
            # Calculate months
            months = (calc_date.year - taker.date_of_birth.year) * 12 + (calc_date.month - taker.date_of_birth.month)
            if calc_date.day < taker.date_of_birth.day:
                months -= 1
            taker_age_in_months = months
            
            # Calculate days
            delta = calc_date - taker.date_of_birth
            taker_age_in_days = delta.days
    except Exception:
        taker_age_in_months = None
        taker_age_in_days = None
    taker_display_name = getattr(taker, 'name', None) or getattr(taker, 'username', None) or 'N/A'
    taker_display_gender = getattr(taker, 'gender', None) or 'N/A'
    
    if hasattr(taker, 'age') and taker.age is not None:
        # taker.age property usually uses today. We might want to calculate age at assessment.
        # But taker.age is a property on the model.
        # Let's recalculate for display if we want age at assessment.
        if getattr(taker, 'date_of_birth', None):
             calc_date = assignment.completed_date.date() if assignment.completed_date else date.today()
             taker_display_age = calc_date.year - taker.date_of_birth.year - ((calc_date.month, calc_date.day) < (taker.date_of_birth.month, taker.date_of_birth.day))
        else:
             taker_display_age = taker.age
    elif getattr(taker, 'date_of_birth', None):
        calc_date = assignment.completed_date.date() if assignment.completed_date else date.today()
        taker_display_age = calc_date.year - taker.date_of_birth.year - ((calc_date.month, calc_date.day) < (taker.date_of_birth.month, taker.date_of_birth.day))
    else:
        taker_display_age = 'N/A'
        
    total_raw_score, domain_scores, max_domain_scores, t_score, interpretation, summary = calculate_scores(assignment)

    # Calculate DQ for DST/DDST/DSST if in days
    dst_dq = None
    name_lower = assessment.name.strip().lower()
    if any(x in name_lower for x in ['dst', 'ddst', 'dsst', 'developmental screening']) and taker_age_in_days:
        # total_raw_score for DST is already the Developmental Age in days
        dst_dq = (total_raw_score / taker_age_in_days) * 100
        
        # Classification for DST
        if dst_dq >= 130: interpretation = "Very Superior"
        elif dst_dq >= 120: interpretation = "Superior"
        elif dst_dq >= 110: interpretation = "High Average"
        elif dst_dq >= 90: interpretation = "Average"
        elif dst_dq >= 80: interpretation = "Low Average"
        elif dst_dq >= 70: interpretation = "Borderline"
        elif dst_dq >= 50: interpretation = "Mild Development Delay"
        elif dst_dq >= 35: interpretation = "Moderate Development Delay"
        elif dst_dq >= 20: interpretation = "Severe Development Delay"
        else: interpretation = "Profound Development Delay"

    cbcl_composites = None
    cbcl_interpretations = None
    cbcl_domain_interpretations = None
    if 'cbcl' in name_lower:
        def norm(s):
            return ' '.join(s.strip().lower().replace('/', ' ').replace('-', ' ').split())
        internalizing_keys = {
            'emotionally reactive',
            'anxious depressed',
            'somatic complaints',
            'somatic complains',
            'withdrawn'
        }
        externalizing_keys = {
            'attention problem',
            'aggressive behaviour',
            'aggressive behavior'
        }
        other_keys = {
            'other problems',
            'sleep problems'
        }
        domain_thresholds = {
            'emotionally reactive': [(0, 5, 'Normal'), (6, 8, 'Borderline Clinical'), (9, 18, 'Clinical')],
            'anxious depressed': [(0, 6, 'Normal'), (7, 8, 'Borderline Clinical'), (9, 16, 'Clinical')],
            'somatic complaints': [(0, 4, 'Normal'), (5, 6, 'Borderline Clinical'), (7, 22, 'Clinical')],
            'somatic complains': [(0, 4, 'Normal'), (5, 6, 'Borderline Clinical'), (7, 22, 'Clinical')],
            'withdrawn': [(0, 4, 'Normal'), (5, 5, 'Borderline Clinical'), (6, 16, 'Clinical')],
            'sleep problems': [(0, 7, 'Normal'), (8, 8, 'Borderline Clinical'), (9, 14, 'Clinical')],
            'attention problem': [(0, 5, 'Normal'), (6, 6, 'Borderline Clinical'), (7, 10, 'Clinical')],
            'aggressive behaviour': [(0, 20, 'Normal'), (21, 24, 'Borderline Clinical'), (25, 38, 'Clinical')],
            'aggressive behavior': [(0, 20, 'Normal'), (21, 24, 'Borderline Clinical'), (25, 38, 'Clinical')]
        }
        allowed_domains = {
            'emotionally reactive',
            'anxious depressed',
            'somatic complaints',
            'somatic complains',
            'withdrawn',
            'sleep problems',
            'attention problem',
            'aggressive behaviour',
            'aggressive behavior',
            'other problems'
        }
        domain_scores = {d: s for d, s in domain_scores.items() if norm(d) in allowed_domains}
        internalizing = 0
        externalizing = 0
        total_other = 0
        cbcl_domain_interpretations = {}
        for d, s in domain_scores.items():
            key = norm(d)
            if key in internalizing_keys:
                internalizing += s
            if key in externalizing_keys:
                externalizing += s
            if key in other_keys:
                total_other += s
            th = domain_thresholds.get(key)
            if th:
                interp = None
                for lo, hi, label in th:
                    if lo <= s <= hi:
                        interp = label
                        break
                cbcl_domain_interpretations[d] = interp or 'N/A'
        total_score_all = sum(domain_scores.values())
        def classify_internalizing(x):
            if x <= 13:
                return 'Normal'
            if 14 <= x <= 17:
                return 'Borderline Clinical'
            return 'Clinical'
        def classify_externalizing(x):
            if x <= 20:
                return 'Normal'
            if 21 <= x <= 24:
                return 'Borderline Clinical'
            return 'Clinical'
        def classify_total(x):
            if x <= 51:
                return 'Normal'
            if 52 <= x <= 60:
                return 'Borderline Clinical'
            return 'Clinical'
        cbcl_composites = {
            'internalizing': internalizing,
            'externalizing': externalizing,
            'total_other_problems': total_other,
            'total_score': total_score_all
        }
        cbcl_interpretations = {
            'internalizing': classify_internalizing(internalizing),
            'externalizing': classify_externalizing(externalizing),
            'total_other_problems': None,
            'total_score': classify_total(total_score_all)
        }
    return render_template('assessment_completed.html', 
                           assignment=assignment, 
                           assessment=assessment, 
                           taker=taker, 
                           is_client_assignment=is_client_assignment, 
                           domain_scores=domain_scores, 
                           max_domain_scores=max_domain_scores, 
                           total_raw_score=total_raw_score, 
                           t_score=t_score, 
                           interpretation=interpretation, 
                           summary=summary,
                           taker_display_name=taker_display_name,
                           taker_display_gender=taker_display_gender,
                           taker_display_age=taker_display_age,
                           taker_age_in_months=taker_age_in_months,
                           taker_age_in_days=taker_age_in_days,
                           dst_dq=dst_dq,
                           is_free=assessment.is_free,
                           cbcl_composites=cbcl_composites,
                           cbcl_interpretations=cbcl_interpretations,
                           cbcl_domain_interpretations=cbcl_domain_interpretations)

@main.route('/report/<int:assignment_id>/download_word', methods=['GET', 'POST'])
@login_required
def download_word_report(assignment_id):
    """
    Downloads the Word report for a completed assessment.
    """
    assignment = AssessmentAssignment.query.get_or_404(assignment_id)

    if not is_authorized_to_view_report(assignment):
        flash('You do not have permission to view this report.', 'danger')
        return redirect(url_for('consultant.dashboard') if current_user.role == 'consultant' else url_for('main.index'))

    try:
        if not assignment.is_completed:
            flash('Cannot download a report for an incomplete assessment.', 'warning')
            return redirect(url_for('main.view_report', assignment_id=assignment_id))

        taker = assignment.client or assignment.user
        total_raw_score, domain_scores, max_domain_scores, t_score, interpretation, summary = calculate_scores(assignment)
        
        assessment_name_lower = assignment.assessment.name.strip().lower()
        if 'ciss' in assessment_name_lower:
            display_assessment_name = 'Coping Inventory for Stressful Situations (CISS)'
        else:
            display_assessment_name = assignment.assessment.name

        document = Document()

        # Consultant Logo and Info
        consultant = None
        if assignment.client and assignment.client.referred_by:
            consultant = assignment.client.referred_by
        elif assignment.user and assignment.user.role == 'consultant':
            consultant = assignment.user
        
        if consultant and consultant.logo_filename:
            logo_path = os.path.join(current_app.root_path, 'static', 'uploads', consultant.logo_filename)
            if os.path.exists(logo_path):
                # Add logo aligned to the right
                paragraph = document.add_paragraph()
                paragraph.alignment = WD_ALIGN_PARAGRAPH.RIGHT
                run = paragraph.add_run()
                run.add_picture(logo_path, width=Inches(1.5))
        
        if consultant:
            if consultant.registration_number:
                p = document.add_paragraph(f'Registration No: {consultant.registration_number}')
                p.alignment = WD_ALIGN_PARAGRAPH.RIGHT
            if consultant.contact_details:
                p = document.add_paragraph(f'Contact: {consultant.contact_details}')
                p.alignment = WD_ALIGN_PARAGRAPH.RIGHT

        document.add_heading(f'Assessment Report: {display_assessment_name}', 0)

        # Client Details
        document.add_heading('Client Information', level=1)
        document.add_paragraph(f"Name: {taker.name or 'N/A'}")
        document.add_paragraph(f"Email: {taker.email or 'N/A'}")
        if hasattr(taker, 'date_of_birth'):
            document.add_paragraph(f"Age: {taker.age if taker.date_of_birth else 'N/A'}")
        if hasattr(taker, 'gender'):
            document.add_paragraph(f"Gender: {taker.gender or 'N/A'}")
        document.add_paragraph(f"Completion Date: {assignment.completed_date.strftime('%Y-%m-%d') if assignment.completed_date else 'N/A'}")

        # Overall Score Section for non-VSMS
        if 'vsms' not in assessment_name_lower and 'social maturity' not in assessment_name_lower:
            document.add_heading('Overall Score', level=1)
            if any(x in assessment_name_lower for x in ['dst', 'ddst', 'dsst', 'developmental screening']):
                document.add_paragraph(f"Total Score (days): {total_raw_score}")
            else:
                document.add_paragraph(f"Total Raw Score: {total_raw_score}")

            if t_score is not None:
                document.add_paragraph(f"T-Score: {t_score}")
            if interpretation is not None and 'gad 7' not in assessment_name_lower:
                document.add_paragraph(f"Interpretation: {interpretation}")
            if 'isaa' in assessment_name_lower:
                document.add_heading('ISAA Classification', level=1)
                if total_raw_score < 70:
                    cls = 'No Autism'
                elif total_raw_score <= 106:
                    cls = 'Mild Autism'
                elif total_raw_score <= 153:
                    cls = 'Moderate Autism'
                else:
                    cls = 'Severe Autism'
                document.add_paragraph(f"Classification: {cls}")
                t = document.add_table(rows=1, cols=5)
                h = t.rows[0].cells
                h[0].text = 'Classification'
                h[1].text = 'No Autism < 70'
                h[2].text = 'Mild Autism 70 to 106'
                h[3].text = 'Moderate Autism 107 to 153'
                h[4].text = 'Severe Autism > 153'

        # Domain Scores Table
        if domain_scores and 'gad 7' not in assessment_name_lower and 'cars' not in assessment_name_lower and 'level 2 anxiety adult' not in assessment_name_lower:
            document.add_heading('Domain-wise Results', level=1)
            
            is_vsms = 'vsms' in assessment_name_lower or 'social maturity' in assessment_name_lower
            
            cols = 2
            if not is_vsms:
                cols = 3
            if 'ciss' in assessment_name_lower or 'vadrs' in assessment_name_lower:
                cols = 2

            table = document.add_table(rows=1, cols=cols)
            hdr_cells = table.rows[0].cells
            hdr_cells[0].text = 'Domain'

            if is_vsms:
                hdr_cells[1].text = 'Equivalent Age (Months)'
            else:
                hdr_cells[1].text = 'Score'
                if 'ciss' not in assessment_name_lower and 'vadrs' not in assessment_name_lower:
                    hdr_cells[2].text = 'Equivalent Age (Months)'

            for i, (domain, score) in enumerate(domain_scores.items()):
                row_cells = table.add_row().cells
                row_cells[0].text = domain
                if is_vsms:
                    row_cells[1].text = f"{score / 30:.2f}"
                elif 'ciss' in assessment_name_lower or 'vadrs' in assessment_name_lower:
                    row_cells[1].text = str(score)
                    if 'vadrs' in assessment_name_lower and i == 1: # Highlight the row above "Combined Score"
                        for cell in table.rows[i].cells:
                            for paragraph in cell.paragraphs:
                                for run in paragraph.runs:
                                    run.font.bold = True
                            shading_elm = parse_xml(r'<w:shd {} w:fill="D3D3D3"/>'.format(nsdecls('w')))
                            cell._tc.get_or_add_tcPr().append(shading_elm)
                elif any(x in assessment_name_lower for x in ['dst', 'ddst', 'dsst', 'developmental screening']):
                    row_cells[1].text = f"{score} (days)"
                    row_cells[2].text = f"{score / 30:.2f}"
                else:
                    row_cells[1].text = str(score)
                    if 'ciss' not in assessment_name_lower:
                        row_cells[2].text = f"{score / 30:.2f}"


        if 'cbcl' in assessment_name_lower:
            def norm(s):
                return ' '.join(s.strip().lower().replace('/', ' ').replace('-', ' ').split())
            internalizing_keys = {
                'emotionally reactive',
                'anxious depressed',
                'somatic complaints',
                'somatic complains',
                'withdrawn'
            }
            externalizing_keys = {
                'attention problem',
                'aggressive behaviour',
                'aggressive behavior'
            }
            other_keys = {
                'other problems',
                'sleep problems'
            }
            internalizing = 0
            externalizing = 0
            total_other = 0
            for d, s in domain_scores.items():
                key = norm(d)
                if key in internalizing_keys:
                    internalizing += s
                if key in externalizing_keys:
                    externalizing += s
                if key in other_keys:
                    total_other += s
            total_score_all = sum(domain_scores.values())
            def classify_internalizing(x):
                if x <= 13:
                    return 'Normal'
                if 14 <= x <= 17:
                    return 'Borderline Clinical'
                return 'Clinical'
            def classify_externalizing(x):
                if x <= 20:
                    return 'Normal'
                if 21 <= x <= 24:
                    return 'Borderline Clinical'
                return 'Clinical'
            def classify_total(x):
                if x <= 51:
                    return 'Normal'
                if 52 <= x <= 60:
                    return 'Borderline Clinical'
                return 'Clinical'
            document.add_heading('CBCL Composite Scores', level=1)
            comp_table = document.add_table(rows=1, cols=3)
            ch = comp_table.rows[0].cells
            ch[0].text = 'Composite'
            ch[1].text = 'Score'
            ch[2].text = 'Interpretation'
            r = comp_table.add_row().cells
            r[0].text = 'Internalizing'
            r[1].text = str(internalizing)
            r[2].text = classify_internalizing(internalizing)
            r = comp_table.add_row().cells
            r[0].text = 'Externalizing'
            r[1].text = str(externalizing)
            r[2].text = classify_externalizing(externalizing)
            r = comp_table.add_row().cells
            r[0].text = 'Total Other Problems'
            r[1].text = str(total_other)
            r[2].text = '—'
            r = comp_table.add_row().cells
            r[0].text = 'Total Score'
            r[1].text = str(total_score_all)
            r[2].text = classify_total(total_score_all)

            document.add_heading('CBCL Domain Scores', level=1)
            dt = document.add_table(rows=1, cols=3)
            dh = dt.rows[0].cells
            dh[0].text = 'Domain'
            dh[1].text = 'Score'
            dh[2].text = 'Interpretation'
            def dnorm(s):
                return ' '.join(s.strip().lower().replace('/', ' ').replace('-', ' ').split())
            thresholds = {
                'emotionally reactive': [(0, 5, 'Normal'), (6, 8, 'Borderline Clinical'), (9, 18, 'Clinical')],
                'anxious depressed': [(0, 6, 'Normal'), (7, 8, 'Borderline Clinical'), (9, 16, 'Clinical')],
                'somatic complaints': [(0, 4, 'Normal'), (5, 6, 'Borderline Clinical'), (7, 22, 'Clinical')],
                'somatic complains': [(0, 4, 'Normal'), (5, 6, 'Borderline Clinical'), (7, 22, 'Clinical')],
                'withdrawn': [(0, 4, 'Normal'), (5, 5, 'Borderline Clinical'), (6, 16, 'Clinical')],
                'sleep problems': [(0, 7, 'Normal'), (8, 8, 'Borderline Clinical'), (9, 14, 'Clinical')],
                'attention problem': [(0, 5, 'Normal'), (6, 6, 'Borderline Clinical'), (7, 10, 'Clinical')],
                'aggressive behaviour': [(0, 20, 'Normal'), (21, 24, 'Borderline Clinical'), (25, 38, 'Clinical')],
                'aggressive behavior': [(0, 20, 'Normal'), (21, 24, 'Borderline Clinical'), (25, 38, 'Clinical')]
            }
            for d, s in domain_scores.items():
                rr = dt.add_row().cells
                rr[0].text = d
                rr[1].text = str(s)
                lab = 'N/A'
                th = thresholds.get(dnorm(d))
                if th:
                    for lo, hi, l in th:
                        if lo <= s <= hi:
                            lab = l
                            break
                rr[2].text = lab

        # Add Domain Performance Graph
        if domain_scores and 'ham a' not in assessment_name_lower and 'ciss' not in assessment_name_lower and 'cars' not in assessment_name_lower and 'level 2 anxiety adult' not in assessment_name_lower:
            document.add_page_break() # Force page break before graph
            document.add_heading('Domain Performance Graph', level=1)
            
            chart_image_data = request.form.get('chart_image')
            if chart_image_data:
                try:
                    header, encoded = chart_image_data.split(",", 1)
                    image_data = base64.b64decode(encoded)
                    image_stream = io.BytesIO(image_data)
                    try:
                        document.add_picture(image_stream, width=Inches(6.0))
                    except Exception:
                        tmp_stream = tempfile.NamedTemporaryFile(suffix='.png', delete=False)
                        tmp_stream.write(image_data)
                        tmp_stream.flush()
                        tmp_stream.close()
                        document.add_picture(tmp_stream.name, width=Inches(6.0))
                        try:
                            os.remove(tmp_stream.name)
                        except Exception:
                            pass
                except Exception as e:
                    document.add_paragraph('Chart image could not be processed.')
                    print(f"Error processing chart image: {e}")
            else:
                # Fallback to server-side generation if no client image provided (e.g. direct link access)
                # This is the old logic, kept as a backup but likely to fail if server env is bad
                try:
                    # Use a safer way to get domains and values
                    domains_for_chart = []
                    plot_values = []
                    
                    for domain, score in domain_scores.items():
                        domains_for_chart.append(domain.split(' (')[0])
                        if any(x in assessment_name_lower for x in ['vsms', 'social maturity', 'dst', 'ddst', 'dsst']):
                            plot_values.append(round(score / 30.0, 2))
                        else:
                            plot_values.append(score)

                    y_label = 'Equivalent Age (Months)' if any(x in assessment_name_lower for x in ['vsms', 'social maturity', 'dst', 'ddst', 'dsst']) else 'Score'

                    # Try setting style, fallback to default
                    try:
                        plt.style.use('seaborn-v0_8-whitegrid')
                    except:
                        plt.style.use('default')

                    fig, ax = plt.subplots(figsize=(8.27, 5.85))
                    ax.bar(domains_for_chart, plot_values, color='#4a90e2')
                    for i, val in enumerate(plot_values):
                        ax.text(i, val, f'{val:.2f}', ha='center', va='bottom', fontsize=8)
                    ax.set_xlabel('Domains')
                    ax.set_ylabel(y_label)
                    ax.set_title('Domain Performance Graph')
                    ax.tick_params(axis='x', labelsize=9, labelrotation=45)
                    ax.tick_params(axis='y', labelsize=9)
                    plt.setp(ax.get_xticklabels(), ha="right", rotation_mode="anchor")

                    fig.tight_layout(pad=2.0)

                    tmp_file = tempfile.NamedTemporaryFile(suffix='.png', delete=False)
                    fig.savefig(tmp_file.name, format='png', dpi=150)
                    document.add_picture(tmp_file.name, width=Inches(6.0))
                    try:
                        os.remove(tmp_file.name)
                    except Exception:
                        pass
                    plt.close(fig)
                    plt.close('all')
                except Exception as chart_error:
                    document.add_paragraph('Chart generation temporarily unavailable.')
                    print(f"Word chart generation error: {chart_error}")
                    traceback.print_exc()

        # Summary Section
        if summary:
            document.add_heading('Summary', level=1)
            document.add_paragraph(summary)
        elif hasattr(taker, 'date_of_birth') and taker.date_of_birth and domain_scores and 'ciss' not in assessment_name_lower and 'cars' not in assessment_name_lower and 'level 2 anxiety adult' not in assessment_name_lower and 'vadrs' not in assessment_name_lower and 'vanderbilt' not in assessment_name_lower:
            document.add_heading('Social Maturity Summary' if 'vsms' in assessment_name_lower or 'social maturity' in assessment_name_lower else 'Developmental Summary', level=1)
            
            if any(x in assessment_name_lower for x in ['dst', 'ddst', 'dsst', 'developmental screening']):
                # DST specific logic with days
                calc_date = assignment.completed_date.date() if assignment.completed_date else date.today()
                delta = calc_date - taker.date_of_birth
                ca_in_days = delta.days
                
                # We also need age in months for display
                months = (calc_date.year - taker.date_of_birth.year) * 12 + (calc_date.month - taker.date_of_birth.month)
                if calc_date.day < taker.date_of_birth.day:
                    months -= 1
                age_in_months = months

                da_in_days = total_raw_score
                dq = (da_in_days / ca_in_days) * 100 if ca_in_days > 0 else 0
                
                document.add_paragraph(f"Chronological Age (CA): {ca_in_days} days ({age_in_months} months)")
                document.add_paragraph(f"Developmental Age (DA): {da_in_days:.2f} days ({da_in_days/30:.2f} months)")
                document.add_paragraph(f"Developmental Quotient (DQ): {dq:.2f}")
                
                # Add Interpretation
                dst_interp = ""
                if dq >= 130: dst_interp = "Very Superior"
                elif dq >= 120: dst_interp = "Superior"
                elif dq >= 110: dst_interp = "High Average"
                elif dq >= 90: dst_interp = "Average"
                elif dq >= 80: dst_interp = "Low Average"
                elif dq >= 70: dst_interp = "Borderline"
                elif dq >= 50: dst_interp = "Mild Development Delay"
                elif dq >= 35: dst_interp = "Moderate Development Delay"
                elif dq >= 20: dst_interp = "Severe Development Delay"
                else: dst_interp = "Profound Development Delay"
                document.add_paragraph(f"Interpretation: {dst_interp}")

                # Add Classification Table
                if 'ddst' not in assessment_name_lower:
                    document.add_heading('DST DQ Classification Table', level=2)
                    tbl = document.add_table(rows=1, cols=2)
                    th = tbl.rows[0].cells
                    th[0].text = 'DQ Range'
                    th[1].text = 'Classification'
                    
                    ranges = [
                        ("> 130", "Very Superior"),
                        ("120 - 129", "Superior"),
                        ("110 - 119", "High Average"),
                        ("90 - 109", "Average"),
                        ("80 - 89", "Low Average"),
                        ("70 - 79", "Borderline"),
                        ("50 - 69", "Mild Development Delay"),
                        ("35 - 49", "Moderate Development Delay"),
                        ("20 - 34", "Severe Development Delay"),
                        ("< 20", "Profound Development Delay")
                    ]
                    for r_text, c_text in ranges:
                        row = tbl.add_row().cells
                        row[0].text = r_text
                        row[1].text = c_text
            else:
                total_months = sum(score / 30 for score in domain_scores.values())
                da_in_months = total_months / len(domain_scores)
                
                calc_date = assignment.completed_date.date() if assignment.completed_date else date.today()
                months = (calc_date.year - taker.date_of_birth.year) * 12 + (calc_date.month - taker.date_of_birth.month)
                if calc_date.day < taker.date_of_birth.day:
                    months -= 1
                ca_in_months = months

                quotient = (da_in_months / ca_in_months) * 100

                document.add_paragraph(f"Chronological Age (CA): {ca_in_months} months")
                if 'vsms' in assessment_name_lower or 'social maturity' in assessment_name_lower:
                    document.add_paragraph(f"Social Age (SA): {da_in_months:.2f} months")
                    document.add_paragraph(f"Social Quotient (SQ): {quotient:.2f}")
                else:
                    document.add_paragraph(f"Developmental Age (DA): {da_in_months:.2f} months")
                    document.add_paragraph(f"Developmental Quotient (DQ): {quotient:.2f}")

        # Therapist Notes Section
        if assignment.therapist_notes:
            document.add_heading('Therapist Notes', level=1)
            document.add_paragraph(assignment.therapist_notes)

        # Save document to a memory stream
        f = io.BytesIO()
        document.save(f)
        f.seek(0)

        response = make_response(f.read())
        response.headers['Content-Type'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        response.headers['Content-Disposition'] = f'attachment; filename="{display_assessment_name}_report_{taker.name.replace(" ", "_") if taker.name else "client"}.docx"'
        return response
            
    except Exception as e:
        print(f"Download report error: {e}")
        flash('Error generating report. Please try again.', 'error')
        return redirect(url_for('main.index'))
