from django.shortcuts import render, redirect, get_object_or_404
from django.http import HttpResponse, JsonResponse
from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib.auth.models import User
from django.contrib import messages
from django.contrib.auth import authenticate, login, logout
from django.core.files.storage import FileSystemStorage
from django.db.models import Q, Count
from django.views.decorators.csrf import csrf_exempt
from scipy.stats import chi2_contingency, chi2,norm,chisquare
from plotly.graph_objects import Bar, Figure
from plotly.subplots import make_subplots
import plotly.graph_objs as go
import pandas as pd
import numpy as np
import json
from urllib.parse import unquote

from .forms import PatientFilterForm
from .models import Dataset, Case, Control, ListedDatasets, UniversalPresets, Group
from .filter import Filter, Group

import math

from django.db import transaction
from django.core.files.storage import FileSystemStorage
from django.contrib import messages
from django.shortcuts import redirect, render
import pandas as pd

from django.shortcuts import get_object_or_404, render
from .models import Dataset, Group
from django.contrib.auth.decorators import login_required

from django.shortcuts import render, get_object_or_404
from urllib.parse import unquote
import json

from django.shortcuts import get_object_or_404, render
import plotly.graph_objs as go
from django.db.models import Count

from django.shortcuts import render, get_object_or_404
from django.db.models import Count, Q
from scipy.stats import chi2_contingency
from .models import Dataset, Case
import pandas as pd

from django.shortcuts import render, get_object_or_404
from django.db.models import Count, Q
from scipy.stats import chi2_contingency
from .models import Dataset, Case

from django.shortcuts import render, get_object_or_404
from django.db.models import Q
import json
from urllib.parse import unquote
import logging
from django.db.models import F, FloatField, ExpressionWrapper
from urllib.parse import quote

# Configure logging
logger = logging.getLogger(__name__)

def clean_cell(value):
    if pd.isna(value) or value == 'N/A':
        return None
    return value

def numberToString(value):
    if isinstance(value, float) and math.isnan(value):
        return None  # Handle NaN specifically
    if isinstance(value, (int, float)):
        return int(value)  # Convert float/int to integer
    try:
        return int(float(value))  # Convert string to float, then to int
    except ValueError:
        return None  # Handle invalid cases

def patient_information(request):
    # Get the first ListedDatasets entry and its first dataset
    listed_dataset = ListedDatasets.objects.prefetch_related("datasets").first()
    dataset_unique_id = None

    if listed_dataset and listed_dataset.datasets.exists():
        dataset_unique_id = listed_dataset.datasets.first().unique_id

    return render(request, "patient_information.html", {
        "dataset_unique_id": dataset_unique_id,
    })

def contact_us(request):
    """
    Render the Contact Us page with static details for Dr. Jesmin and the department.
    """
    return render(request, "contact_us.html")

def register(request):
    if request.method == "POST":
        first_name = request.POST.get("first_name")
        last_name = request.POST.get("last_name")
        email = request.POST.get("email").strip().lower()  # Normalize email to lowercase
        password = request.POST.get("password")
        confirm_password = request.POST.get("confirm_password")

        # Validation
        if password != confirm_password:
            messages.error(request, "Passwords do not match!")
            return redirect("register")
        
        # Check if the email is already registered
        if User.objects.filter(email=email).exists():
            messages.error(request, "Email is already registered!")
            return redirect("register")

        # Create a new user
        user = User.objects.create_user(
            username=email,  # Using email as username
            email=email,
            password=password,
            first_name=first_name,
            last_name=last_name,
        )
        messages.success(request, "Registration successful! Please log in.")
        return redirect("login")  # Redirect to a login page or any other page

    return render(request, "register.html")

def login_view(request):
    if request.method == "POST":
        email = request.POST.get("email").strip().lower()  # Normalize email to lowercase
        password = request.POST.get("password")

        # Authenticate the user
        user = authenticate(request, username=email, password=password)
        if user is not None:
            login(request, user)
            messages.success(request, "Logged in successfully!")
            return redirect("index")  # Redirect to the homepage or dashboard
        else:
            messages.error(request, "Invalid email or password!")
            return redirect("login")

    return render(request, "login.html")

def logout_view(request):
    logout(request)
    messages.success(request, "You have been logged out successfully!")
    return redirect("login")

@login_required
def upload_dataset(request):
    if request.method == 'POST':
        dataset_title = request.POST['dataset_title']
        dataset_description = request.POST['dataset_description']
        case_file = request.FILES.get('case_file')
        control_file = request.FILES.get('control_file')

        # Validate file extensions
        if not (case_file and case_file.name.endswith(".xlsx")):
            messages.error(request, "Invalid case file! Please upload a valid .xlsx file.")
            return redirect("upload_dataset")
        if not (control_file and control_file.name.endswith(".xlsx")):
            messages.error(request, "Invalid control file! Please upload a valid .xlsx file.")
            return redirect("upload_dataset")
        
        if Dataset.objects.filter(title=dataset_title).exists():
            messages.error(request, f"A dataset with the title '{dataset_title}' already exists. Please choose a different title.")
            return redirect("upload_dataset")

        # Start a database transaction
        try:
            with transaction.atomic():
                # Create Dataset
                dataset = Dataset.objects.create(
                    title=dataset_title,
                    description=dataset_description,
                    user=request.user
                )

                # Save files temporarily for processing
                fs = FileSystemStorage()
                case_file_path = fs.save(case_file.name, case_file)
                control_file_path = fs.save(control_file.name, control_file)

                # Process case file
                case_data = pd.read_excel(fs.path(case_file_path))
                cases = [
                    Case(
                        dataset=dataset,
                        sl_no=row['Sl No'],
                        id_no=row['ID No'],
                        age_years=row['Age (Years)'],
                        occupation=row['Occupation'],
                        height_m=row['Height (m)'],
                        weight_kg=row['Weight (Kg)'],
                        bmi=row['BMI (Kg/m^2)'],
                        waist_circum_cm=row['Waist Circum. (cm)'],
                        hip_circum_cm=row['Hip Circum. (cm)'],
                        whr=row['WHR'],
                        bp_sys=row['BP-Sys (mm Hg)'],
                        bp_dia=row['BP-Dia (mm Hg)'],
                        rbs=row['RBS (mmol/dl)'],
                        hba1c=row['HbA1c (%)'],
                        tg=row['TG (mg/dl)'],
                        hdl=row['HDL (mg/dl)'],
                        ldl=row['LDL (mg/dl)'],
                        tc=row['TC (mg/dl)'],
                        tg_hdl=row['TG/ HDL'],
                        ldl_hdl=row['LDL/ HDL'],
                        tc_hdl=row['TC/ HDL'],
                        creatinine=row['Creatinine (mg/dl)'],
                        education=row['Education'],
                        income=row['Income'],
                        carbohydrate=row['Carbohydrate'],
                        protein=row['Protein'],
                        fats_source=row['Fats Source'],
                        vegetable=row['Vegetable'],
                        fruits=row['Fruits'],
                        vitamins=row['Vitamins'],
                        minerals=row['Minerals'],
                        water_g_per_day=row['Water (G/D)'],
                        tea=row['Tea'],
                        coffee=row['Coffee'],
                        betal_nuts=row['Betal nuts'],
                        jarda=row['Jarda'],
                        processed_food=row['Processed food'],
                        fast_food=row['Fast food'],
                        soft_drinks=row['Soft drinks'],
                        walking_hours=row['Walking (h/d)'],
                        standing_hours=row['Standing (h/d)'],
                        desk_job=row['Desk job'],
                        shift_d_n=row['Shift (D/N)'],
                        regular_exercise=row['Regular exercise'],
                        feeling_restless=row['Felling restless'],
                        fatigue=row['Fatigue'],
                        shortness_of_breath=row['Shortness of breath'],
                        sleeping_hours=row['Sleeping (h/d)'],
                        sound_sleep=row['Sound sleep'],
                        family_history_with_cancer=row['Family history with cancer'],
                        menarche_age=clean_cell(row['Menerche age (Yrs)']),
                        menstrual_status=row['Menstrual status'],
                        menopause_age=clean_cell(row['Menopause age (Yrs)']),
                        parity=clean_cell(row['Parity (No)']),
                        age_at_first_birth=clean_cell(row['Age at 1st birth (Yrs)']),
                        lactation_situation=row['Lactation sitation'],
                        breastfeeding_duration=row['Breast feeding duration'],
                        use_contraceptive=row['Use contraceptive'],
                        thyroidism_problem=row['Thyroidism problem'],
                        gastric=row['Gastric'],
                        antibiotic_intake=row['Antibiotic intake'],
                        diabetes=row['Diabetes'],
                        age_at_diagnosis=clean_cell(row['Age at Diagnosis']),
                        er=row['ER'],
                        pr=row['PR'],
                        her2=row['HER-2'],
                        tnbc=row['TNBC'],
                        breast_cancer_type=row['Breast Cancer type'],
                        radio_therapy=row['Radio therapy'],
                        chemotherapy=row['Chemotherapy'],
                        hormone_therapy=row['Hormone therapy'],
                        surgery=row['Surgery'],
                        cancer_stage=numberToString(row['B. Cancer Stage']),
                        oral_medication=row['Oral medication'],
                        histologic_type=row['Histologic type'],
                        fnac=row['FNAC'],
                        pathology=row['Pathology'],
                        histopathology=row['Histopathology'],
                        mammography=row['Mamography'],
                        physical=row['Physical'],
                    )
                    for _, row in case_data.iterrows()
                ]
                Case.objects.bulk_create(cases)

                # Process control file
                control_data = pd.read_excel(fs.path(control_file_path))
                controls = [
                    Control(
                        dataset=dataset,
                        sl_no=row['Sl No'],
                        id_no=row['ID No'],
                        age_years=row['Age (Years)'],
                        occupation=row['Occupation'],
                        height_m=row['Height (m)'],
                        weight_kg=row['Weight (Kg)'],
                        bmi=row['BMI (Kg/m^2)'],
                        waist_circum_cm=row['Waist Circum. (cm)'],
                        hip_circum_cm=row['Hip Circum. (cm)'],
                        whr=row['WHR'],
                        bp_sys=row['BP-Sys (mm Hg)'],
                        bp_dia=row['BP-Dia (mm Hg)'],
                        rbs=row['RBS (mmol/dl)'],
                        hba1c=row['HbA1c (%)'],
                        tg=row['TG (mg/dl)'],
                        hdl=row['HDL (mg/dl)'],
                        ldl=row['LDL (mg/dl)'],
                        tc=row['TC (mg/dl)'],
                        tg_hdl=row['TG/ HDL'],
                        ldl_hdl=row['LDL/ HDL'],
                        tc_hdl=row['TC/ HDL'],
                        creatinine=row['Creatinine (mg/dl)'],
                        education=row['Education'],
                        income=row['Income'],
                        carbohydrate=row['Carbohydrate'],
                        protein=row['Protein'],
                        fats_source=row['Fats Source'],
                        vegetable=row['Vegetable'],
                        fruits=row['Fruits'],
                        vitamins=row['Vitamins'],
                        minerals=row['Minerals'],
                        water_g_per_day=row['Water (G/D)'],
                        tea=row['Tea'],
                        coffee=row['Coffee'],
                        betal_nuts=row['Betal nuts'],
                        jarda=row['Jarda'],
                        processed_food=row['Processed food'],
                        fast_food=row['Fast food'],
                        soft_drinks=row['Soft drinks'],
                        walking_hours=row['Walking (h/d)'],
                        standing_hours=row['Standing (h/d)'],
                        desk_job=row['Desk job'],
                        shift_d_n=row['Shift (D/N)'],
                        regular_exercise=row['Regular exercise'],
                        feeling_restless=row['Felling restless'],
                        fatigue=row['Fatigue'],
                        shortness_of_breath=row['Shortness of breath'],
                        sleeping_hours=row['Sleeping (h/d)'],
                        sound_sleep=row['Sound sleep'],
                        family_history_with_cancer=row['Family history with cancer'],
                        menarche_age=clean_cell(row['Menerche age (Yrs)']),
                        menstrual_status=row['Menstrual status'],
                        menopause_age=clean_cell(row['Menopause age (Yrs)']),
                        parity=clean_cell(row['Parity (No)']),
                        age_at_first_birth=clean_cell(row['Age at 1st birth (Yrs)']),
                        lactation_situation=row['Lactation sitation'],
                        breastfeeding_duration=row['Breast feeding duration'],
                        use_contraceptive=row['Use contraceptive'],
                        thyroidism_problem=row['Thyroidism problem'],
                        gastric=row['Gastric'],
                        antibiotic_intake=row['Antibiotic intake'],
                        diabetes=row['Diabetes'],
                    )
                    for _, row in control_data.iterrows()
                ]
                Control.objects.bulk_create(controls)

                # Delete temporary files
                fs.delete(case_file_path)
                fs.delete(control_file_path)

                # Redirect to success page
                return redirect("upload_success", unique_id=dataset.unique_id)

        except Exception as e:
            # Log the error (optional)
            messages.error(request, f"Error uploading dataset: {e}")
            return redirect("upload_dataset")

    return render(request, 'upload_dataset.html')

def upload_success(request, unique_id):
    dataset = get_object_or_404(Dataset, unique_id=unique_id)
    analysis_url = request.build_absolute_uri(f"/dataset/{unique_id}/")

    context = {
        "dataset": dataset,
        "analysis_url": analysis_url,
    }
    return render(request, "upload_success.html", context)

def dataset_options(request, unique_id):
    dataset = get_object_or_404(Dataset, unique_id=unique_id)

    sections = [
        {"name": "Demographic Data", "url_name": "demographic_data"},
        {"name": "Anthropometric Data", "url_name": "anthropometric_data"},
        {"name": "Biochemical Parameters Data", "url_name": "biochemical_data"},
        {"name": "Clinical Data", "url_name": "clinical_data"},
        {"name": "Obstetric Data", "url_name": "obstetric_data"},
        {"name": "Treatment Strategies Data", "url_name": "treatment_data"},
        {"name": "Genetic SNPs Variation Data", "url_name": "genetic_snps_data"},
    ]

    tools = [
        {"title": "Data Visualization", "url": f"/presets/{unique_id}/", "description": "Visualize patterns and insights in your dataset."},
        {"title": "OR Calculation", "url": f"/or-calculation/{unique_id}/", "description": "Analyze odds ratios and relationships."},
        {"title": "Chi-Square Test (2x2)", "url": f"/chi-square-2x2/{unique_id}/", "description": "Perform categorical data analysis."},
        {"title": "More Tools Coming Soon", "url": "#", "description": "Stay tuned for additional features."},
    ]

    context = {
        "dataset": dataset,
        "cases_count": Case.objects.filter(dataset=dataset).count(),
        "controls_count": Control.objects.filter(dataset=dataset).count(),
        "sections": sections,
        "tools": tools,
    }

    return render(request, "dataset_options.html", context)


def getUniversalPresets():
    UNIVERSAL_PRESETS = [
        {
            "groupTitle":"Hypertension",
            "rootGroups":[{"title": "Hypertension - Yes", "filters": {"logic": "AND", "filters": [{"type": "condition", "field": "bp_sys", "condition_type": "gte", "value": "130"}, {"type": "condition", "field": "bp_dia", "condition_type": "gte", "value": "80"}]}}, {"title": "Hypertension - No", "filters": {"logic": "OR", "filters": [{"type": "condition", "field": "bp_sys", "condition_type": "lt", "value": "130"}, {"type": "condition", "field": "bp_dia", "condition_type": "lt", "value": "80"}]}}]
        },
        {
            "groupTitle":"BMI",
            "rootGroups":[{"title": "NW", "filters": {"logic": "AND", "filters": [{"type": "condition", "field": "bmi", "condition_type": "gt", "value": "18.5"}, {"type": "condition", "field": "bmi", "condition_type": "lt", "value": "25.0"}]}}, {"title": "OW", "filters": {"logic": "AND", "filters": [{"type": "condition", "field": "bmi", "condition_type": "gte", "value": "25"}, {"type": "condition", "field": "bmi", "condition_type": "lt", "value": "30"}]}}, {"title": "OB", "filters": {"logic": "AND", "filters": [{"type": "condition", "field": "bmi", "condition_type": "gte", "value": "30"}]}}, {"title": "NL+OW", "filters": {"logic": "AND", "filters": [{"type": "condition", "field": "bmi", "condition_type": "gte", "value": "18.5"}, {"type": "condition", "field": "bmi", "condition_type": "lt", "value": "30"}]}}]
        },
        {
            "groupTitle":"Hyperlipidemia",
            "rootGroups":[{"title": "Hyperlipidemia - Yes", "filters": {"logic": "AND", "filters": [{"type": "condition", "field": "tg_hdl", "condition_type": "gt", "value": "2.0"}]}}]
        },
        {
            "groupTitle":"Overall (*)",
            "rootGroups":[{"title": "*", "filters": {"logic": "AND", "filters": [{"type": "condition", "field": "bmi", "condition_type": "gt", "value": "0"}]}}]
        }
    ]
    return UNIVERSAL_PRESETS
def presets(request, unique_id):
    # Ensure the dataset exists
    dataset = get_object_or_404(Dataset, unique_id=unique_id)

    # Fetch universal presets with kid=None
    # presets = UniversalPresets.objects.filter(kid__isnull=True).first()
    # preset_groups = presets.groups.all() if presets else Group.objects.none()

    preset_groups = getUniversalPresets()

    for group in preset_groups:
        group["group_logic_uriencoded"] = quote(json.dumps(group))

    print(preset_groups)

    # Check if user is logged in
    if request.user.is_authenticated:
        # Fetch presets created by the logged-in user and associated with the dataset
        user_presets = Group.objects.filter(creator=request.user).order_by('-created_at')
    else:
        user_presets = None  # No custom presets for anonymous users

    return render(request, "presets.html", {
        "unique_id": unique_id,
        "dataset": dataset,
        "preset_groups": preset_groups,
        "user_presets": user_presets,
        # "dataset_title": dataset.title,  # Include dataset title for display
        "is_logged_in": request.user.is_authenticated,  # Pass login status
        "cases_count": Case.objects.filter(dataset=dataset).count(),
        "controls_count": Control.objects.filter(dataset=dataset).count(),
    })

@login_required
def research_tools(request):
    context = {
        "upload_dataset_url": "/upload-dataset/",
        "visualize_data_url": "/breast-cancer-resources/",
        "tools": [
            {
                "title": "More Tools Coming Soon",
                "description": "We are continuously adding new features and tools to enhance your research capabilities.",
            },
            {
                "title": "Stay Tuned!",
                "description": "Exciting new updates and tools will be available here shortly.",
            },
        ],
    }
    return render(request, "research_tools.html", context)

def breast_cancer_resources(request):
    dataset_groups = ListedDatasets.objects.prefetch_related("datasets").all()
    annotated_groups = []
    for group in dataset_groups:
        datasets_with_counts = []
        for dataset in group.datasets.all():
            # Add cases and controls count to each dataset
            dataset.cases_count = Case.objects.filter(dataset=dataset).count()
            dataset.controls_count = Control.objects.filter(dataset=dataset).count()
            datasets_with_counts.append(dataset)
        group.annotated_datasets = datasets_with_counts
        annotated_groups.append(group)

    # Get user-uploaded datasets
    user_datasets = []
    if request.user.is_authenticated:
        user_datasets = Dataset.objects.filter(user=request.user)
        for dataset in user_datasets:
            dataset.cases_count = Case.objects.filter(dataset=dataset).count()
            dataset.controls_count = Control.objects.filter(dataset=dataset).count()

    return render(request, "breast_cancer_resources.html", {
        "dataset_groups": annotated_groups,
        "user_datasets": user_datasets,
    })

@login_required
def delete_dataset(request, dataset_id):
    dataset = get_object_or_404(Dataset, id=dataset_id, user=request.user)
    if request.method == "POST":
        dataset.delete()
        messages.success(request, "Dataset deleted successfully!")
        return redirect("breast_cancer_resources")
    return render(request, "confirm_delete.html", {"dataset": dataset})

def chi2x2_calculation(case1, case2, control1, control2, alpha=0.05):
    # Calculate Odds Ratio (OR)
    or_value = (case1 * control2) / (case2 * control1)

    # Standard error of log(OR)
    se_log_or = np.sqrt(1/case1 + 1/case2 + 1/control1 + 1/control2)

    # Z-value for the given alpha
    z_alpha = norm.ppf(1 - alpha / 2)

    # Confidence intervals (CI) for log(OR)
    log_or = np.log(or_value)
    ci_lower = np.exp(log_or - z_alpha * se_log_or)
    ci_upper = np.exp(log_or + z_alpha * se_log_or)

    # Z-score for Wald test
    z_score = log_or / se_log_or

    # Two-tailed p-value
    p_value = 2 * (1 - norm.cdf(abs(z_score)))

    return or_value, ci_lower, ci_upper, p_value

def or_calculation_table(request, unique_id):
    """
    Calculates Odds Ratios and related statistics for each criterion defined in CHART_CONFIG.
    """
    dataset = get_object_or_404(Dataset, unique_id=unique_id)
    cases = Case.objects.filter(dataset=dataset)
    controls = Control.objects.filter(dataset=dataset)

    table_data_by_category = {}

    for chart_title, config in CHART_CONFIG.items():
        # Skip fields marked as 'case_only' if applicable
        if config.get("case_only", False):
            continue

        label = config["title"]
        key = config["key"]
        category = config.get("category", "Others")  # Default category if not defined
        labels = config["labels"]
        filters = config["filters"]

        # Ensure there are exactly two labels for OR calculation
        if len(labels) != 2:
            continue

        # Retrieve counts using the filter lambdas
        try:
            case_counts = filters(cases)
            control_counts = filters(controls)
        except Exception as e:
            continue

        if len(case_counts) != 2 or len(control_counts) != 2:
            continue

        case1, case2 = case_counts
        control1, control2 = control_counts

        # Perform Chi-square test
        try:
            or_value, ci_lower, ci_upper, p_value = chi2x2_calculation(case1, control1, case2, control2)
        except Exception as e:
            or_value, ci_lower, ci_upper, p_value = None, None, None, None

        row_data = {
            "criteria": label,
            "case_1": case1,
            "control_1": control1,
            "case_2": case2,
            "control_2": control2,
            "odds_ratio": or_value,
            "lower_limit": ci_lower,
            "upper_limit": ci_upper,
            "p_value": p_value
        }

        # Group by category
        if category not in table_data_by_category:
            table_data_by_category[category] = []
        table_data_by_category[category].append(row_data)

    context = {
        "dataset": dataset,
        "cases": cases.count(),
        "controls": controls.count(),
        "table_data_by_category": table_data_by_category,
    }

    return render(request, "or_calculation_table.html", context)

def chi2x2_calculation_table(request, unique_id):
    """
    Generates a Chi-square calculation table for each criterion defined in CHART_CONFIG.
    Groups the data into separate tables based on category.
    """
    dataset = get_object_or_404(Dataset, unique_id=unique_id)
    cases = Case.objects.filter(dataset=dataset)
    controls = Control.objects.filter(dataset=dataset)

    # Dictionary to hold the table data grouped by category
    categorized_data = {}

    for chart_title, config in CHART_CONFIG.items():
        # Skip fields marked as 'case_only' if applicable
        if config.get("case_only", False):
            continue

        label = config["title"]
        key = config["key"]
        labels = config["labels"]
        filters = config["filters"]

        # Ensure there are exactly two labels for Chi-square calculation
        if len(labels) != 2:
            continue

        # Retrieve counts using the filter lambdas
        try:
            case_counts = filters(cases)
            control_counts = filters(controls)
        except Exception as e:
            continue

        # Ensure we have two counts
        if len(case_counts) != 2 or len(control_counts) != 2:
            continue

        # Extract counts
        case1, case2 = case_counts
        control1, control2 = control_counts

        # Perform Chi-square test
        try:
            _,_,_,p = chi2x2_calculation(case1, control1, case2, control2)
        except Exception as e:
            p = None

        # Group data by category (assuming `key` represents the category)
        category = config.get("category", "General")
        if category not in categorized_data:
            categorized_data[category] = []

        categorized_data[category].append({
            "criteria": label,
            "case_1": case1,
            "control_1": control1,
            "case_2": case2,
            "control_2": control2,
            "p_value": round(p, 4) if p is not None else "N/A",
        })

    context = {
        "dataset": dataset,
        "cases": cases.count(),
        "controls": controls.count(),
        "categorized_data": categorized_data,
    }

    return render(request, "chi2x2_calculation_table.html", context)


def create_filter_lambda(field, condition1, condition2, ignore_nan=False):
    """
    Generates a filter function for CHART_CONFIG entries.
    
    Args:
        field (str): The model field to filter on.
        condition1 (dict): The first condition as a dictionary.
        condition2 (dict): The second condition as a dictionary.
        ignore_nan (bool): Whether to exclude NaN (NULL) values.
    
    Returns:
        function: A lambda function that applies the filters to a queryset.
    """
    def filters(cases):
        if ignore_nan:
            # Exclude records where the field is NULL
            count1 = cases.filter(**condition1, **{f"{field}__isnull": False}).count()
            count2 = cases.filter(**condition2, **{f"{field}__isnull": False}).count()
        else:
            count1 = cases.filter(**condition1).count()
            count2 = cases.filter(**condition2).count()
        return [count1, count2]
    
    return filters

CHART_CONFIG = {
    # Demographic Data
    "Age": {
        "key": "age_years",
        "title": "Age (≥ 45 vs. < 45) Years",
        "category": "Demographic data",
        "labels": [">=45", "<45"],
        "ignore_nan": True,  # Exclude records where age_years is NULL
        "filters": create_filter_lambda(
            field="age_years",
            condition1={"age_years__gte": 45},
            condition2={"age_years__lt": 45},
            ignore_nan=True
        ),
    },
    "Socio-economic Status": {
        "key": "income",
        "title": "Socio-economic Status (Medium vs. Low)",
        "category": "Demographic data",
        "labels": ["Medium", "Low"],
        "ignore_nan": False,  # Include records where income is NULL
        "filters": create_filter_lambda(
            field="income",
            condition1={"income": "Medium"},
            condition2={"income": "Low"},
            ignore_nan=False
        ),
    },
    "Education": {
        "key": "education",
        "title": "Education Level (Yes vs. No)",
        "category": "Demographic data",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="education",
            condition1={"education": "Yes"},
            condition2={"education": "No"},
            ignore_nan=False
        ),
    },
    "Regular Exercise": {
        "key": "regular_exercise",
        "title": "Regular Exercise (Yes vs. No)",
        "category": "Demographic data",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="regular_exercise",
            condition1={"regular_exercise": "Yes"},
            condition2={"regular_exercise": "No"},
            ignore_nan=False
        ),
    },
    "Walking Time": {
        "key": "walking_hours",
        "title": "Walking Time (> 3 vs. ≤ 3) H/Day",
        "category": "Demographic data",
        "labels": [">3 H/Day", "≤3 H/Day"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="walking_hours",
            condition1={"walking_hours__gt": 3},
            condition2={"walking_hours__lte": 3},
            ignore_nan=False
        ),
    },
    "Sleeping Time": {
        "key": "sleeping_hours",
        "title": "Sleeping Hours (≥ 6 vs. < 6) H/Day",
        "category": "Demographic data",
        "labels": [">=6 H/Day", "<6 H/Day"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="sleeping_hours",
            condition1={"sleeping_hours__gte": 6},
            condition2={"sleeping_hours__lt": 6},
            ignore_nan=False
        ),
    },
    "Tea Intake": {
        "key": "tea",
        "title": "Tea Intake (Yes vs. No)",
        "category": "Demographic data",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="tea",
            condition1={"tea": "Yes"},
            condition2={"tea": "No"},
            ignore_nan=False
        ),
    },
    "Coffee Intake": {
        "key": "coffee",
        "title": "Coffee Intake (Yes vs. No)",
        "category": "Demographic data",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="coffee",
            condition1={"coffee": "Yes"},
            condition2={"coffee": "No"},
            ignore_nan=False
        ),
    },
    "Family History with Cancer": {
        "key": "family_history_with_cancer",
        "title": "Family History with Cancer (Yes vs. No)",
        "category": "Demographic data",
        "labels": ["Yes", "No"],
        "ignore_nan": True,  # Treat NaN as "No" by excluding
        "filters": create_filter_lambda(
            field="family_history_with_cancer",
            condition1={"family_history_with_cancer": "Yes"},
            condition2={"family_history_with_cancer": "No"},
            ignore_nan=True
        ),
    },
    "Sound Sleep Occurrence": {
        "key": "sound_sleep",
        "title": "Sound Sleep Occurrence (Yes vs. No)",
        "category": "Demographic data",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="sound_sleep",
            condition1={"sound_sleep": "Yes"},
            condition2={"sound_sleep": "No"},
            ignore_nan=False
        ),
    },
    "Lifestyle": {
        "key": "desk_job",
        "title": "Lifestyle (Active in Job vs. Sedentary)",
        "category": "Demographic data",
        "labels": ["Active in Job", "Sedentary"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="desk_job",
            condition1={"desk_job": "No"},
            condition2={"desk_job": "Yes"},
            ignore_nan=False
        ),
    },
    
    # Anthropometric Data
    "Height": {
        "key": "height_m",
        "title": "Height (<1.5 m vs. ≥1.5 m)",
        "category": "Anthropometric data",
        "labels": ["≥1.5 m", "<1.5 m"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="height_m",
            condition1={"height_m__gte": 1.5},
            condition2={"height_m__lt": 1.5},
            ignore_nan=False
        ),
    },
    "BMI": {
        "key": "bmi",
        "title": "BMI (>=30 vs. <30)",
        "category": "Anthropometric data",
        "labels": [">=30", "<30"],
        "ignore_nan": True,  # Exclude records where BMI is NULL
        "filters": create_filter_lambda(
            field="bmi",
            condition1={"bmi__gte": 30},
            condition2={"bmi__lt": 30},
            ignore_nan=True
        ),
    },
    "Weight": {
        "key": "weight_kg",
        "title": "Weight (≥59 Kg vs. <59 Kg)",
        "category": "Anthropometric data",
        "labels": ["≥59 Kg", "<59 Kg"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="weight_kg",
            condition1={"weight_kg__gte": 59},
            condition2={"weight_kg__lt": 59},
            ignore_nan=False
        ),
    },
    "Waist Circumference": {
        "key": "waist_circum_cm",
        "title": "Waist Circumference (≥80 cm vs. <80 cm)",
        "category": "Anthropometric data",
        "labels": ["≥80 cm", "<80 cm"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="waist_circum_cm",
            condition1={"waist_circum_cm__gte": 80},
            condition2={"waist_circum_cm__lt": 80},
            ignore_nan=False
        ),
    },
    "Waist-Hip Ratio": {
        "key": "whr",
        "title": "Waist-Hip Ratio (≥0.8 vs. <0.8)",
        "category": "Anthropometric data",
        "labels": ["≥0.8", "<0.8"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="whr",
            condition1={"whr__gte": 0.8},
            condition2={"whr__lt": 0.8},
            ignore_nan=False
        ),
    },
    
    # Biochemical Parameters Data
    "BP SYS": {
        "key": "bp_sys",
        "title": "Blood Pressure SYS (>120 mmHg vs. ≤120 mmHg)",
        "category": "Biochemical parameters data",
        "labels": [">120 mmHg", "<=120 mmHg"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="bp_sys",
            condition1={"bp_sys__gt": 120},
            condition2={"bp_sys__lte": 120},
            ignore_nan=False
        ),
    },
    "BP DIA": {
        "key": "bp_dia",
        "title": "Blood Pressure DIA (>80 mmHg vs. ≤80 mmHg)",
        "category": "Biochemical parameters data",
        "labels": [">80 mmHg", "<=80 mmHg"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="bp_dia",
            condition1={"bp_dia__gt": 80},
            condition2={"bp_dia__lte": 80},
            ignore_nan=False
        ),
    },
    "RBS": {
        "key": "rbs",
        "title": "Random Blood Sugar (>=7.8 mmol/dl vs. <7.8 mmol/dl)",
        "category": "Biochemical parameters data",
        "labels": [">=7.8 mmol/dl", "<7.8 mmol/dl"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="rbs",
            condition1={"rbs__gte": 7.8},
            condition2={"rbs__lt": 7.8},
            ignore_nan=False
        ),
    },
    "HBA1C": {
        "key": "hba1c",
        "title": "HbA1c (>6.3% vs. ≤6.3%)",
        "category": "Biochemical parameters data",
        "labels": [">6.3%", "<=6.3%"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="hba1c",
            condition1={"hba1c__gt": 6.3},
            condition2={"hba1c__lte": 6.3},
            ignore_nan=False
        ),
    },
    "Blood Creatinine Level": {
        "key": "creatinine",
        "title": "Blood Creatinine Level (<0.6 & >1.2 mg/dl vs. 0.6-1.2 mg/dl)",
        "category": "Biochemical parameters data",
        "labels": ["<0.6 & >1.2 mg/dl", "0.6-1.2 mg/dl"],
        "ignore_nan": False,
        "filters": lambda cases: [
            cases.filter(creatinine__lt=0.6).count() + cases.filter(creatinine__gt=1.2).count(),
            cases.filter(creatinine__gte=0.6, creatinine__lte=1.2).count(),
        ],
    },
    "HDL": {
        "key": "hdl",
        "title": "HDL (≥30 mg/dl vs. <30 mg/dl)",
        "category": "Biochemical parameters data",
        "labels": ["≥30 mg/dl", "<30 mg/dl"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="hdl",
            condition1={"hdl__gte": 30},
            condition2={"hdl__lt": 30},
            ignore_nan=False
        ),
    },
    "LDL": {
        "key": "ldl",
        "title": "LDL (≥130 mg/dl vs. <130 mg/dl)",
        "category": "Biochemical parameters data",
        "labels": ["≥130 mg/dl", "<130 mg/dl"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="ldl",
            condition1={"ldl__gte": 130},
            condition2={"ldl__lt": 130},
            ignore_nan=False
        ),
    },
    "LDL / HDL Ratio": {
        "key": "ldl_hdl",
        "title": "LDL / HDL Ratio (≥2.5 vs. <2.5)",
        "category": "Biochemical parameters data",
        "labels": ["≥2.5", "<2.5"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="ldl_hdl",
            condition1={"ldl_hdl__gte": 2.5},
            condition2={"ldl_hdl__lt": 2.5},
            ignore_nan=False
        ),
    },

    # Clinical Data
    "Subtype": {
        "key": "breast_cancer_type",
        "title": "Subtype (ER+ vs. ER-)",
        "category": "Clinical Data",
        "labels": ["ER+", "ER-"],
        "ignore_nan": False,  # Assuming subtype is always provided
        "filters": lambda cases: [
            cases.filter(er="Positive").count(),
            cases.filter(er="Negative").count(),
        ],
        "case_only": True,  # This field is only available in the Case model
    },
    "Age at Diagnosis": {
        "key": "age_at_diagnosis",
        "title": "Age at Diagnosis (<40 vs. >=40)",
        "category": "Clinical Data",
        "labels": ["<40", ">=40"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="age_at_diagnosis",
            condition1={"age_at_diagnosis__lt": 40},
            condition2={"age_at_diagnosis__gte": 40},
            ignore_nan=False
        ),
    },
    "Cancer Stage": {
        "key": "cancer_stage",
        "title": "Cancer Stage (II vs. III)",
        "category": "Clinical Data",
        "labels": ["II", "III"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="cancer_stage",
            condition1={"cancer_stage": "2"},
            condition2={"cancer_stage": "3"},
            ignore_nan=False
        ),
    },
    "Cancer Type": {
        "key": "breast_cancer_type",
        "title": "Cancer Type",
        "category": "Clinical Data",
        "labels": ["TNBC", "Ductal cell carcinoma"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="breast_cancer_type",
            condition1={"breast_cancer_type__icontains": "TNBC"},
            condition2={"breast_cancer_type__icontains": "Ductal Cell Carcinoma"},
            ignore_nan=False
        ),
    },
    "Physical": {
        "key": "physical",
        "title": "Physical (Yes vs. No)",
        "category": "Clinical Data",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="physical",
            condition1={"physical": "Yes"},
            condition2={"physical": "No"},
            ignore_nan=False
        ),
    },
    "Mammography": {
        "key": "mammography",
        "title": "Mammography (Yes vs. No)",
        "category": "Clinical Data",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="mammography",
            condition1={"mammography": "Yes"},
            condition2={"mammography": "No"},
            ignore_nan=False
        ),
    },
    "FNAC": {
        "key": "fnac",
        "title": "FNAC (Yes vs. No)",
        "category": "Clinical Data",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="fnac",
            condition1={"fnac": "Yes"},
            condition2={"fnac": "No"},
            ignore_nan=False
        ),
    },
    "Pathology": {
        "key": "pathology",
        "title": "Pathology (Yes vs. No)",
        "category": "Clinical Data",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="pathology",
            condition1={"pathology": "Yes"},
            condition2={"pathology": "No"},
            ignore_nan=False
        ),
    },
    "Histopathology": {
        "key": "histopathology",
        "title": "Histopathology (Yes vs. No)",
        "category": "Clinical Data",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="histopathology",
            condition1={"histopathology": "Yes"},
            condition2={"histopathology": "No"},
            ignore_nan=False
        ),
    },
    
    # Obstetric Data
    "Menarche Age": {
        "key": "menarche_age",
        "title": "Menarche Age (≥12 vs. <12 Years)",
        "category": "Obstetric data",
        "labels": ["≥12", "<12"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="menarche_age",
            condition1={"menarche_age__gte": 12},
            condition2={"menarche_age__lt": 12},
            ignore_nan=False
        ),
    },
    "Menopause Age": {
        "key": "menopause_age",
        "title": "Menopause Age (≥45 vs. <45 Years)",
        "category": "Obstetric data",
        "labels": ["≥45", "<45"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="menopause_age",
            condition1={"menopause_age__gte": 45},
            condition2={"menopause_age__lt": 45},
            ignore_nan=False
        ),
    },
    "First Child-Bearing Age": {
        "key": "age_at_first_birth",
        "title": "First Child-Bearing Age (≥18 vs. <18 Years)",
        "category": "Obstetric data",
        "labels": ["≥18", "<18"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="age_at_first_birth",
            condition1={"age_at_first_birth__gte": 18},
            condition2={"age_at_first_birth__lt": 18},
            ignore_nan=False
        ),
    },
    "Parity": {
        "key": "parity",
        "title": "Parity (>2 vs. ≤2)",
        "category": "Obstetric data",
        "labels": [">2", "≤2"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="parity",
            condition1={"parity__gt": 2},
            condition2={"parity__lte": 2},
            ignore_nan=False
        ),
    },
    "Breastfeeding Duration": {
        "key": "breastfeeding_duration",
        "title": "Breastfeeding Duration (>4 vs. ≤4 Years)",
        "category": "Obstetric data",
        "labels": [">4", "≤4"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="breastfeeding_duration",
            condition1={"breastfeeding_duration__gt": 4},
            condition2={"breastfeeding_duration__lte": 4},
            ignore_nan=False
        ),
    },
    "Contraceptive Use": {
        "key": "use_contraceptive",
        "title": "Contraceptive Use (Yes vs. No)",
        "category": "Obstetric data",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="use_contraceptive",
            condition1={"use_contraceptive": "Yes"},
            condition2={"use_contraceptive": "No"},
            ignore_nan=False
        ),
    },
    
    # Treatment Data
    "Radiotherapy": {
        "key": "radio_therapy",
        "title": "Radiotherapy (Yes vs. No)",
        "category": "Treatment Data",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="radio_therapy",
            condition1={"radio_therapy": "Yes"},
            condition2={"radio_therapy": "No"},
            ignore_nan=False
        ),
    },
    "Chemotherapy": {
        "key": "chemotherapy",
        "title": "Chemotherapy (Yes vs. No)",
        "category": "Treatment Data",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="chemotherapy",
            condition1={"chemotherapy": "Yes"},
            condition2={"chemotherapy": "No"},
            ignore_nan=False
        ),
    },
    "Surgery": {
        "key": "surgery",
        "title": "Surgery (Yes vs. No)",
        "category": "Treatment Data",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="surgery",
            condition1={"surgery": "Yes"},
            condition2={"surgery": "No"},
            ignore_nan=False
        ),
    },
    "Hormone Therapy": {
        "key": "hormone_therapy",
        "title": "Hormone Therapy (Yes vs. No)",
        "category": "Treatment Data",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="hormone_therapy",
            condition1={"hormone_therapy": "Yes"},
            condition2={"hormone_therapy": "No"},
            ignore_nan=False
        ),
    },
    "Oral Medication": {
        "key": "oral_medication",
        "title": "Oral Medication (Yes vs. No)",
        "category": "Treatment Data",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="oral_medication",
            condition1={"oral_medication": "Yes"},
            condition2={"oral_medication": "No"},
            ignore_nan=False
        ),
    },

    # Special Data
    "NL vs OW": {
        "key": "bmi",
        "title": "NL vs OW",
        "category": "NL vs OW vs OB",
        "labels": ["NL", "OW"],
        "ignore_nan": False,  # Include records where bmi is NULL
        "filters": create_filter_lambda(
            field="bmi",
            condition1={"bmi__gte": 18.5, "bmi__lt": 25},  # Normal weight (NL)
            condition2={"bmi__gte": 25, "bmi__lt": 30},    # Overweight (OW)
            ignore_nan=False
        ),
    },
    "NL vs OB": {
        "key": "bmi",
        "title": "NL vs OB",
        "category": "NL vs OW vs OB",
        "labels": ["NL", "OB"],
        "ignore_nan": False,  # Include records where bmi is NULL
        "filters": create_filter_lambda(
            field="bmi",
            condition1={"bmi__gte": 18.5, "bmi__lt": 25},  # Normal weight (NL)
            condition2={"bmi__gte": 30},                    # Obese (OB)
            ignore_nan=False
        ),
    },
    "OW vs OB":{
        "key": "bmi",
        "title": "OW vs OB",
        "category": "NL vs OW vs OB",
        "labels": ["OW", "OB"],
        "ignore_nan": False,  # Include records where bmi is NULL
        "filters": create_filter_lambda(
            field="bmi",
            condition1={"bmi__gte": 25, "bmi__lt": 30},
            condition2={"bmi__gte": 30},
            ignore_nan=False
        ),
    },
    "NL+OW vs OB": {
        "key": "bmi",
        "title": "NL+OW vs OB",
        "category": "NL vs OW vs OB",
        "labels": ["NL+OW", "OB"],
        "ignore_nan": False,  # Include records where bmi is NULL
        "filters": create_filter_lambda(
            field="bmi",
            condition1 = {"bmi__gte": 18.5, "bmi__lt": 30},  # NL+OW: BMI between 18.5 and 30
            condition2 = {"bmi__gte": 30},                  # OB: BMI >= 30
            ignore_nan=False
        ),
    },

    # Extra Data
    "antibiotic_intake": {
        "key": "antibiotic_intake",
        "title": "Antibiotic Intake (Yes vs. No)",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="antibiotic_intake",
            condition1={"antibiotic_intake": "Yes"},
            condition2={"antibiotic_intake": "No"},
            ignore_nan=False
        ),
    },
    "betal_nuts": {
        "key": "betal_nuts",
        "title": "Betel Nut Intake (Yes vs. No)",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="betal_nuts",
            condition1={"betal_nuts": "Yes"},
            condition2={"betal_nuts": "No"},
            ignore_nan=False
        ),
    },
    "carbohydrate": {
        "key": "carbohydrate",
        "title": "Carbohydrates (> 2 vs. ≤ 2) Times/Day",
        "labels": [">2 Times/Day", "≤2 Times/Day"],
        "ignore_nan": True,
        "filters": create_filter_lambda(
            field="carbohydrate",
            condition1={"carbohydrate__gt": 2.0},
            condition2={"carbohydrate__lte": 2.0},
            ignore_nan=True
        ),
    },
    "gastric": {
        "key": "gastric",
        "title": "Gastric Problem (Yes vs. No)",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="gastric",
            condition1={"gastric": "Yes"},
            condition2={"gastric": "No"},
            ignore_nan=False
        ),
    },
    "jarda": {
        "key": "jarda",
        "title": "Jorda Intake (Yes vs. No)",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="jarda",
            condition1={"jarda": "Yes"},
            condition2={"jarda": "No"},
            ignore_nan=False
        ),
    },
    "menstrual_status": {
        "key": "menstrual_status",
        "title": "Menstrual Status (No vs. Yes)",
        "labels": ["No", "Yes"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="menstrual_status",
            condition1={"menstrual_status": "No"},
            condition2={"menstrual_status": "Yes"},
            ignore_nan=False
        ),
    },
    "processed_fastfood_softdrinks": {
        "key": "processed_fastfood_softdrinks",
        "title": "Processed/Fastfood/Soft Drinks (Yes vs. No)",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": lambda cases: [
            cases.filter(Q(processed_food="Yes") | Q(fast_food="Yes") | Q(soft_drinks="Yes")).count(),
            cases.filter(processed_food="No", fast_food="No", soft_drinks="No").count(),
        ],
    },
    "protein": {
        "key": "protein",
        "title": "Proteins (≤ 2 vs. > 2) Times/Day",
        "labels": ["≤2 Times/Day", ">2 Times/Day"],
        "ignore_nan": True,
        "filters": create_filter_lambda(
            field="protein",
            condition1={"protein__lte": 2.0},
            condition2={"protein__gt": 2.0},
            ignore_nan=True
        ),
    },
    "restless_fatigue_shortness_breath": {
        "key": "restless_fatigue_shortness_breath",
        "title": "Restless/Fatigue/Shortness of Breath (Yes vs. No)",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": lambda cases: [
            cases.filter(Q(feeling_restless="Yes") | Q(fatigue="Yes") | Q(shortness_of_breath="Yes")).count(),
            cases.filter(feeling_restless="No", fatigue="No", shortness_of_breath="No").count(),
        ],
    },
    "standing_hours": {
        "key": "standing_hours",
        "title": "Standing (> 3 vs. ≤ 3) Hours/Day",
        "labels": [">3 Hours/Day", "≤3 Hours/Day"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="standing_hours",
            condition1={"standing_hours__gt": 3},
            condition2={"standing_hours__lte": 3},
            ignore_nan=False
        ),
    },
    "tc": {
        "key": "tc",
        "title": "TC (≥200 vs. <200) mg/dl",
        "labels": [">=200 mg/dl", "<200 mg/dl"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="tc",
            condition1={"tc__gte": 200},
            condition2={"tc__lt": 200},
            ignore_nan=False
        ),
    },
    "tc_hdl": {
        "key": "tc_hdl",
        "title": "TC / HDL Ratio (≥3 vs. <3)",
        "labels": [">=3", "<3"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="tc_hdl",
            condition1={"tc_hdl__gte": 3},
            condition2={"tc_hdl__lt": 3},
            ignore_nan=False
        ),
    },
    "tea_coffee_intake": {
        "key": "tea_coffee_intake",
        "title": "Tea/Coffee Intake (No vs. Yes)",
        "labels": ["No", "Yes"],
        "ignore_nan": False,
        "filters": lambda cases: [
            cases.filter(Q(tea="No") & Q(coffee="No")).count(),
            cases.filter(Q(tea="Yes") | Q(coffee="Yes")).count(),
        ],
    },
    "tg": {
        "key": "tg",
        "title": "TG (≥150 vs. <150) mg/dl",
        "labels": [">=150 mg/dl", "<150 mg/dl"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="tg",
            condition1={"tg__gte": 150},
            condition2={"tg__lt": 150},
            ignore_nan=False
        ),
    },
    "tg_hdl": {
        "key": "tg_hdl",
        "title": "TG / HDL Ratio (≥2 vs. <2)",
        "labels": [">=2", "<2"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="tg_hdl",
            condition1={"tg_hdl__gte": 2},
            condition2={"tg_hdl__lt": 2},
            ignore_nan=False
        ),
    },
    "thyroidism_problem": {
        "key": "thyroidism_problem",
        "title": "Thyroidism (Yes vs. No)",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="thyroidism_problem",
            condition1={"thyroidism_problem": "Yes"},
            condition2={"thyroidism_problem": "No"},
            ignore_nan=False
        ),
    },
    "vegetable_fruits": {
        "key": "vegetable_fruits",
        "title": "Vegetable/Fruits (≤2 vs. >2) Times/Day",
        "labels": ["≤2 Times/Day", ">2 Times/Day"],
        "ignore_nan": False,
        "filters": lambda cases: [
            cases.annotate(total=F('vegetable') + F('fruits')).filter(total__lte=2).count(),
            cases.annotate(total=F('vegetable') + F('fruits')).filter(total__gt=2).count(),
        ],
    },
    "vitamin_mineral_intake": {
        "key": "vitamin_mineral_intake",
        "title": "Vitamin/Mineral Intake (Yes vs. No)",
        "labels": ["Yes", "No"],
        "ignore_nan": False,
        "filters": lambda cases: [
            cases.filter(Q(vitamins="Yes") | Q(minerals="Yes")).count(),
            cases.filter(Q(vitamins="No") & Q(minerals="No")).count(),
        ],
    },
    "water_g_per_day": {
        "key": "water_g_per_day",
        "title": "Water Intake (<8 vs. ≥8) Glass/Day",
        "labels": ["<8 Glasses/Day", "≥8 Glasses/Day"],
        "ignore_nan": False,
        "filters": create_filter_lambda(
            field="water_g_per_day",
            condition1={"water_g_per_day__lt": 8},
            condition2={"water_g_per_day__gte": 8},
            ignore_nan=False
        ),
    },
}

def get_combined_condition(filters):
    logic = filters["logic"]
    conditions = Q()

    for filter_obj in filters["filters"]:
        if filter_obj["type"] == "condition":
            field = filter_obj["field"]
            condition_type = filter_obj["condition_type"]
            value = filter_obj["value"]

            if condition_type == "gte":
                condition = Q(**{f"{field}__gte": value})
            elif condition_type == "lte":
                condition = Q(**{f"{field}__lte": value})
            elif condition_type == "gt":
                condition = Q(**{f"{field}__gt": value})
            elif condition_type == "lt":
                condition = Q(**{f"{field}__lt": value})
            elif condition_type == "exact":
                condition = Q(**{f"{field}__in": value})
            else:
                raise ValueError(f"Unsupported condition type: {condition_type}")
        elif filter_obj["type"] == "group":
            condition = get_combined_condition(filter_obj)
        else:
            raise ValueError(f"Unsupported filter type: {filter_obj['type']}")

        if logic == "AND":
            conditions &= condition
        elif logic == "OR":
            conditions |= condition
        else:
            raise ValueError(f"Unsupported logic: {logic}")

    return conditions

def generate_grouped_bar_chart(title, labels, cases_values, controls_values, cases_counts, controls_counts, p_value=None):
    chart = go.Figure()

    # Add bars for Cases
    chart.add_trace(go.Bar(
        x=labels,
        y=cases_values,
        width=0.35,  # Narrower bars
        name="Cases",
        marker=dict(color="#1f77b4"),
        text=[f"{value}%" for value in cases_values],
        textposition="outside",
        customdata=cases_counts,
        hovertemplate="<b>Cases - %{x}</b><br>Count: %{customdata}<br>Percentage: %{y}%<extra></extra>",
    ))

    # Add bars for Controls
    chart.add_trace(go.Bar(
        x=labels,
        y=controls_values,
        width=0.35,  # Narrower bars
        name="Controls",
        marker=dict(color="#ff7f0e"),
        text=[f"{value}%" for value in controls_values],
        textposition="outside",
        customdata=controls_counts,
        hovertemplate="<b>Controls - %{x}</b><br>Count: %{customdata}<br>Percentage: %{y}%<extra></extra>",
    ))

    try:
        # Calculate the range dynamically if values are available
        y_axis_range = [0, max(max(cases_values), max(controls_values)) + 20]
    except (ValueError, TypeError) as e:
        # Fallback range in case of empty or invalid values
        print(f"Error calculating range: {e}")
        y_axis_range = [0, 100]  # Default range

    # Update layout
    chart.update_layout(
        title=dict(
            text=f"P-value: {p_value:.4f}" if p_value else "",
            x=0.5,
            font=dict(size=10, color="#B71C1C"),
        ),
        yaxis=dict(
            # title="Percentage (%)",
            tickfont=dict(size=10),
            range=y_axis_range,
        ),
        xaxis=dict(tickfont=dict(size=10)),
        barmode="group",
        template="plotly_white",
        showlegend=True,
        legend=dict(
            orientation="h",
            yanchor="bottom",
            y=-0.5,
            xanchor="center",
            x=0.5
        ),
        margin=dict(l=0, r=30, t=80, b=10),
        # height=250,  # Reduced height
        width=180,  # Reduced width
        modebar=dict(orientation='v')
    )

    config = {
        'responsive': True,
        'displaylogo': False,
        'displayModeBar': True,
        'modeBarButtonsToRemove': [
            'lasso2d', 'select2d', 'toggleSpikelines', 
            'hoverCompareCartesian', 'hoverClosestCartesian',
            'pan2d', "zoom2d", "zoomIn2d", "zoomOut2d", "autoScale2d"
        ],
        'toImageButtonOptions': {
            'filename': title,
            'width': 600,
            'scale': 2,
        },
    }

    return chart.to_html(full_html=False, include_plotlyjs=False, include_mathjax=False, config=config)

def generate_bar_chart_main(title, labels, values, counts, colors, p_value=None):
    chart = Figure()
    chart.add_trace(
        Bar(
            x=labels,
            y=values,
            width=0.4,  # Adjusted for narrower bars
            text=[f"{value}%" for value in values],
            textposition="inside",
            marker=dict(color=colors),
            customdata=counts,
            hovertemplate='<b>%{x}</b><br>Count: %{customdata}<br>Percentage: %{y}%<extra></extra>',
        )
    )

    chart.update_layout(
        title={
            "text": title,
            "x": 0.5,
            "font": {"size": 14},  # Slightly smaller font
        },
        # yaxis_title="Percentage (%)",
        yaxis=dict(tickfont=dict(size=10)),
        xaxis=dict(tickfont=dict(size=10)),
        showlegend=False,
        margin=dict(t=30, b=20, l=20, r=30),
        width=200,  # Reduced width
        height=250,  # Reduced height
        modebar=dict(orientation='v')
    )
    config = {
        'responsive': True,
        'displaylogo': False,
        'displayModeBar': True,
        'modeBarButtonsToRemove': [
            'lasso2d', 'select2d', 'toggleSpikelines', 
            'hoverCompareCartesian', 'hoverClosestCartesian',
            'pan2d', "zoom2d", "zoomIn2d", "zoomOut2d"
        ],
        'toImageButtonOptions': {
            'filename': title,
            'width': 600,
            'scale': 2,
        },
    }
    return chart.to_html(full_html=False, include_plotlyjs=False,include_mathjax=False, config=config)

def generate_chart_from_config(chart_title, group_title, filtered_cases, total_cases, filtered_controls=None, total_controls=None):
    if chart_title not in CHART_CONFIG:
        return None  # Return None if chart_title is not configured

    config = CHART_CONFIG[chart_title]

    # Handle cases
    try:
        case_counts = config["filters"](filtered_cases)
        case_percentages = [round((count / total_cases) * 100) if total_cases > 0 else 0 for count in case_counts]
    except Exception as e:
        # logger.warning(f"Error in case data for chart '{chart_title}': {e}")
        case_counts = [0, 0]
        case_percentages = [0, 0]

    # Handle controls
    control_counts = []
    control_percentages = []
    if filtered_controls and total_controls:
        try:
            control_counts = config["filters"](filtered_controls)
            control_percentages = [round((count / total_controls) * 100) if total_controls > 0 else 0 for count in control_counts]
        except Exception as e:
            # logger.warning(f"Error in control data for chart '{chart_title}': {e}")
            control_counts = [0, 0]
            control_percentages = [0, 0]

    # **Use original labels without interleaving**
    labels = config["labels"]
    cases_values = case_percentages
    controls_values = control_percentages
    cases_counts = case_counts
    controls_counts = control_counts

    try:
        # Perform chi-square test for comparison, if both cases and controls are available
        if case_counts and control_counts:
            _, _, _, p_value = chi2x2_calculation(case_counts[0], control_counts[0], case_counts[1], control_counts[1])
        else:
            p_value = None
    except Exception:
        p_value = None

    # print(f"title: {chart_title}, labels: {labels}, cases_values: {cases_values}, controls_values: {controls_values}, cases_counts: {cases_counts}, controls_counts: {controls_counts}")

    # **Call the new grouped bar chart generation function**
    return generate_grouped_bar_chart(
        title=chart_title,
        labels=labels,
        cases_values=cases_values,
        controls_values=controls_values,
        cases_counts=cases_counts,
        controls_counts=controls_counts,
        p_value=p_value,
    )

def data_visualization(request, unique_id):
    dataset = get_object_or_404(Dataset, unique_id=unique_id)

    groups_param = request.GET.get('groups')
    if groups_param:
        try:
            data = json.loads(unquote(groups_param))
            group_title = data.get("groupTitle", "Unnamed Group")
            root_groups = data.get("rootGroups", [])
        except json.JSONDecodeError:
            logger.error("Failed to decode 'groups' parameter.")
            group_title = "Unnamed Group"
            root_groups = []
    else:
        group_title = "Unnamed Group"
        root_groups = []

    chart_titles = CHART_CONFIG.keys()
    group_results = {}
    categorized_charts = {}

    total_cases = Case.objects.filter(dataset=dataset).count()
    total_controls = Control.objects.filter(dataset=dataset).count()

    for group in root_groups:
        title = group.get("title", "Unnamed Group")
        filters = group.get("filters", {})

        combined_condition = get_combined_condition(filters)
        try:
            filtered_cases = Case.objects.filter(dataset=dataset).filter(combined_condition)
        except:
            filtered_cases = None
        try:
            filtered_controls = Control.objects.filter(dataset=dataset).filter(combined_condition)
        except:
            filtered_controls = None

        try:
            cases_count = filtered_cases.count()
        except:
            cases_count = 0

        try:
            controls_count = filtered_controls.count()
        except:
            controls_count = 0

        cases_percent = round((cases_count / total_cases) * 100) if total_cases else 0
        controls_percent = round((controls_count / total_controls) * 100) if total_controls else 0

        try:
            _,_,_, p_value = chi2x2_calculation(cases_count, total_cases, controls_count, total_controls)
        except Exception as e:
            logger.error(f"Chi-square test failed for group '{title}': {e}")
            p_value = None

        bar_chart_html = generate_bar_chart_main(
            title=f"{title}",
            labels=["Cases", "Controls"],
            values=[cases_percent, controls_percent],
            counts=[cases_count, controls_count],
            colors=["#1f77b4", "#ff7f0e"],
        )

        for chart_title in chart_titles:
            chart_html = generate_chart_from_config(chart_title, title, filtered_cases, total_cases, filtered_controls, total_controls)
            if chart_html:
                category = CHART_CONFIG[chart_title].get("category", "Uncategorized")

                if category == "Uncategorized":
                    continue
                
                if category not in categorized_charts:
                    categorized_charts[category] = []
                chart_entry = next((item for item in categorized_charts[category] if item["title"] == chart_title), None)
                if not chart_entry:
                    chart_entry = {"title": chart_title, "charts": {}}
                    categorized_charts[category].append(chart_entry)
                chart_entry["charts"][title] = chart_html

        group_results[title] = {
            "label": title,
            "cases_count": cases_count,
            "total_cases": total_cases,
            "cases_percent": cases_percent,
            "controls_count": controls_count,
            "total_controls": total_controls,
            "controls_percent": controls_percent,
            "bar_chart_html": bar_chart_html,
        }

    context = {
        "dataset": dataset,
        "unique_id": unique_id,
        "group_title": group_title,
        "group_results": group_results,
        "categorized_charts": categorized_charts,
    }

    return render(request, "data_visualization.html", context)

@csrf_exempt
def add_groups(request, unique_id):
    def get_options(dataset_id,variable):
        cases = Case.objects.filter(dataset__unique_id=dataset_id).values_list(variable, flat=True).distinct()
        return [option for option in cases if option]
    
    dataset = get_object_or_404(Dataset, unique_id=unique_id)

    variables = [
        {"field": "age_years", "name": "Age (Years)", "type": "integer", "category": "Demographic data"},
        {"field": "occupation", "name": "Occupation", "type": "checkbox", "options": get_options(unique_id, "occupation"), "category": "Demographic data"},
        {"field": "education", "name": "Education", "type": "checkbox", "options": get_options(unique_id, "education"), "category": "Demographic data"},
        {"field": "income", "name": "Income", "type": "checkbox", "options": get_options(unique_id, "income"), "category": "Demographic data"},
        {"field": "family_history_with_cancer", "name": "Family history with cancer", "type": "checkbox", "options": get_options(unique_id, "family_history_with_cancer"), "category": "Demographic data"},
        {"field": "regular_exercise", "name": "Regular exercise", "type": "checkbox", "options": get_options(unique_id, "regular_exercise"), "category": "Demographic data"},
        {"field": "feeling_restless", "name": "Feeling restless", "type": "checkbox", "options": get_options(unique_id, "feeling_restless"), "category": "Demographic data"},
        {"field": "fatigue", "name": "Fatigue", "type": "checkbox", "options": get_options(unique_id, "fatigue"), "category": "Demographic data"},
        {"field": "shortness_of_breath", "name": "Shortness of breath", "type": "checkbox", "options": get_options(unique_id, "shortness_of_breath"), "category": "Demographic data"},
        {"field": "sleeping_hours", "name": "Sleeping (h/d)", "type": "float", "category": "Demographic data"},
        {"field": "sound_sleep", "name": "Sound sleep", "type": "checkbox", "options": get_options(unique_id, "sound_sleep"), "category": "Demographic data"},

        {"field": "height_m", "name": "Height (m)", "type": "float", "category": "Anthropometric data"},
        {"field": "weight_kg", "name": "Weight (Kg)", "type": "float", "category": "Anthropometric data"},
        {"field": "bmi", "name": "BMI (Kg/m^2)", "type": "float", "category": "Anthropometric data"},
        {"field": "waist_circum_cm", "name": "Waist Circum. (cm)", "type": "float", "category": "Anthropometric data"},
        {"field": "hip_circum_cm", "name": "Hip Circum. (cm)", "type": "float", "category": "Anthropometric data"},
        {"field": "whr", "name": "WHR", "type": "float", "category": "Anthropometric data"},
        
        {"field": "bp_sys", "name": "BP-Sys (mm Hg)", "type": "integer", "category": "Biochemical parameters data"},
        {"field": "bp_dia", "name": "BP-Dia (mm Hg)", "type": "integer", "category": "Biochemical parameters data"},
        {"field": "rbs", "name": "RBS (mmol/dl)", "type": "float", "category": "Biochemical parameters data"},
        {"field": "hba1c", "name": "HbA1c (%)", "type": "float", "category": "Biochemical parameters data"},
        {"field": "tg", "name": "TG (mg/dl)", "type": "float", "category": "Biochemical parameters data"},
        {"field": "hdl", "name": "HDL (mg/dl)", "type": "float", "category": "Biochemical parameters data"},
        {"field": "ldl", "name": "LDL (mg/dl)", "type": "float", "category": "Biochemical parameters data"},
        {"field": "tc", "name": "TC (mg/dl)", "type": "float", "category": "Biochemical parameters data"},
        {"field": "tg_hdl", "name": "TG/ HDL", "type": "float", "category": "Biochemical parameters data"},
        {"field": "ldl_hdl", "name": "LDL/ HDL", "type": "float", "category": "Biochemical parameters data"},
        {"field": "tc_hdl", "name": "TC/ HDL", "type": "float", "category": "Biochemical parameters data"},
        {"field": "creatinine", "name": "Creatinine (mg/dl)", "type": "float", "category": "Biochemical parameters data"},

        {"field": "menarche_age", "name": "Menerche age (Yrs)", "type": "integer", "category": "Obstetric data"},
        {"field": "menstrual_status", "name": "Menstrual status", "type": "checkbox", "options": get_options(unique_id, "menstrual_status"), "category": "Obstetric data"},
        {"field": "menopause_age", "name": "Menopause age (Yrs)", "type": "integer", "category": "Obstetric data"},
        {"field": "parity", "name": "Parity (No)", "type": "integer", "category": "Obstetric data"},
        {"field": "age_at_first_birth", "name": "Age at 1st birth (Yrs)", "type": "integer", "category": "Obstetric data"},
        {"field": "lactation_situation", "name": "Lactation situation", "type": "checkbox", "options": get_options(unique_id, "lactation_situation"), "category": "Obstetric data"},
        {"field": "breastfeeding_duration", "name": "Breastfeeding duration", "type": "float", "category": "Obstetric data"},
        {"field": "use_contraceptive", "name": "Use contraceptive", "type": "checkbox", "options": get_options(unique_id, "use_contraceptive"), "category": "Obstetric data"},
        {"field": "er", "name": "ER+/-", "type": "checkbox", "options": get_options(unique_id, "er"), "category": "Clinical Data"},
        {"field": "her2", "name": "HER2+/-", "type": "checkbox", "options": get_options(unique_id, "her2"), "category": "Clinical Data"},
        {"field": "age_at_diagnosis", "name": "Age at diagnosis (Yrs)", "type": "integer", "category": "Clinical Data"},
        {"field": "cancer_stage", "name": "Cancer stage", "type": "checkbox", "options": get_options(unique_id, "cancer_stage"), "category": "Clinical Data"},
        {"field": "physical", "name": "Physical", "type": "checkbox", "options": get_options(unique_id, "physical"), "category": "Clinical Data"},
        {"field": "mammography", "name": "Mammography", "type": "checkbox", "options": get_options(unique_id, "mammography"), "category": "Clinical Data"},
        {"field": "fnac", "name": "FNAC", "type": "checkbox", "options": get_options(unique_id, "fnac"), "category": "Clinical Data"},
        {"field": "pathology", "name": "Pathology", "type": "checkbox", "options": get_options(unique_id, "pathology"), "category": "Clinical Data"},
        {"field": "histopathology", "name": "Histopathology", "type": "checkbox", "options": get_options(unique_id, "histopathology"), "category": "Clinical Data"},
        {"field": "thyroidism_problem", "name": "Thyroidism problem", "type": "checkbox", "options": get_options(unique_id, "thyroidism_problem"), "category": "Clinical Data"},
        {"field": "gastric", "name": "Gastric", "type": "checkbox", "options": get_options(unique_id, "gastric"), "category": "Clinical Data"},
        {"field": "antibiotic_intake", "name": "Antibiotic intake", "type": "checkbox", "options": get_options(unique_id, "antibiotic_intake"), "category": "Clinical Data"},
        {"field": "diabetes", "name": "Diabetes", "type": "checkbox", "options": get_options(unique_id, "diabetes"), "category": "Clinical Data"},        
        
        {"field": "chemotherapy", "name": "Chemotherapy", "type": "checkbox", "options": get_options(unique_id, "chemotherapy"), "category": "Treatment Data"},
        {"field": "radio_therapy", "name": "Radiotherapy", "type": "checkbox", "options": get_options(unique_id, "radio_therapy"), "category": "Treatment Data"},
        {"field": "surgery", "name": "Surgery", "type": "checkbox", "options": get_options(unique_id, "surgery"), "category": "Treatment Data"},
        {"field": "hormone_therapy", "name": "Hormone therapy", "type": "checkbox", "options": get_options(unique_id, "hormone_therapy"), "category": "Treatment Data"},
        {"field": "oral_medication", "name": "Oral medication", "type": "checkbox", "options": get_options(unique_id, "oral_medication"), "category": "Treatment Data"},

        {"field": "carbohydrate", "name": "Carbohydrate", "type": "checkbox", "options": get_options(unique_id, "carbohydrate"), "category": "Extra Class"},
        {"field": "protein", "name": "Protein", "type": "checkbox", "options": get_options(unique_id, "protein"), "category": "Extra Class"},
        {"field": "fats_source", "name": "Fats Source", "type": "checkbox", "options": get_options(unique_id, "fats_source"), "category": "Extra Class"},
        {"field": "vegetable", "name": "Vegetable", "type": "checkbox", "options": get_options(unique_id, "vegetable"), "category": "Extra Class"},
        {"field": "fruits", "name": "Fruits", "type": "checkbox", "options": get_options(unique_id, "fruits"), "category": "Extra Class"},
        {"field": "vitamins", "name": "Vitamins", "type": "checkbox", "options": get_options(unique_id, "vitamins"), "category": "Extra Class"},
        {"field": "minerals", "name": "Minerals", "type": "checkbox", "options": get_options(unique_id, "minerals"), "category": "Extra Class"},
        {"field": "water_g_per_day", "name": "Water (G/D)", "type": "float", "category": "Extra Class"},
        {"field": "tea", "name": "Tea", "type": "checkbox", "options": get_options(unique_id, "tea"), "category": "Extra Class"},
        {"field": "coffee", "name": "Coffee", "type": "checkbox", "options": get_options(unique_id, "coffee"), "category": "Extra Class"},
        {"field": "betal_nuts", "name": "Betal nuts", "type": "checkbox", "options": get_options(unique_id, "betal_nuts"), "category": "Extra Class"},
        {"field": "jarda", "name": "Jarda", "type": "checkbox", "options": get_options(unique_id, "jarda"), "category": "Extra Class"},
        {"field": "processed_food", "name": "Processed food", "type": "checkbox", "options": get_options(unique_id, "processed_food"), "category": "Extra Class"},
        {"field": "fast_food", "name": "Fast food", "type": "checkbox", "options": get_options(unique_id, "fast_food"), "category": "Extra Class"},
        {"field": "soft_drinks", "name": "Soft drinks", "type": "checkbox", "options": get_options(unique_id, "soft_drinks"), "category": "Extra Class"},
        {"field": "walking_hours", "name": "Walking (h/d)", "type": "float", "category": "Extra Class"},
        {"field": "standing_hours", "name": "Standing (h/d)", "type": "float", "category": "Extra Class"},
        {"field": "desk_job", "name": "Desk job", "type": "checkbox", "options": get_options(unique_id, "desk_job"), "category": "Extra Class"},
        {"field": "shift_d_n", "name": "Shift (D/N)", "type": "checkbox", "options": get_options(unique_id, "shift_d_n"), "category": "Extra Class"},
    ]



    return render(request, "add_groups.html", {
        "unique_id": unique_id,
        "variables": json.dumps(variables),
    })

def save_preset(request):
    if request.method == "POST":
        try:
            print("Saving preset...")
            data = json.loads(request.body)
            name = data.get("name")
            group_logic = data.get("group_logic")
            uri_encoded = data.get("uri_encoded")
            save = data.get("save")
            update = data.get("update")

            print(save,update)

            if request.user.is_anonymous:
                return JsonResponse({"success": False, "error": "You are not logged in. Please log in to save/update a custom preset."}, status=400)
            
            # Check for empty name
            if not name or name.strip() == "":
                print("Group title cannot be empty.")
                return JsonResponse({"success": False, "error": "Group title cannot be empty."}, status=400)

            # Check if the user already has a preset with the same name
            if Group.objects.filter(creator=request.user, name=name).exists() and update == False:
                print(f"User already has a custom preset with the title '{name}'.")
                return JsonResponse({"success": False, "error": "You already have a custom preset with the same name. Please use the update operation."}, status=400)

            # Save the preset
            if save == True:
                Group.objects.create(
                    creator=request.user,
                    name=name,
                    group_logic=group_logic,
                    group_logic_uriencoded=uri_encoded,
                    description="Custom preset saved by user."
                )
            elif update == True:
                if Group.objects.filter(creator=request.user, name=name).exists() == False:
                    return JsonResponse({"success": False, "error": "You do not have a custom preset with the same name. Please use the save operation."}, status=400)
                else:
                    group = Group.objects.get(creator=request.user, name=name)
                    group.group_logic = group_logic
                    group.group_logic_uriencoded = uri_encoded
                    group.save()

            print("Preset saved successfully!")
            return JsonResponse({"success": True, "message": "Preset saved successfully!"})
        except Exception as e:
            print(f"Error saving preset: {str(e)}")
            return JsonResponse({"success": False, "error": str(e)}, status=500)

    return JsonResponse({"success": False, "error": "Invalid request method"}, status=405)

@csrf_exempt
@login_required
def delete_preset(request, preset_id):
    if request.method == "DELETE":
        try:
            preset = get_object_or_404(Group, id=preset_id, creator=request.user)
            preset.delete()
            return JsonResponse({"success": True, "message": "Preset deleted successfully!"})
        except Exception as e:
            return JsonResponse({"success": False, "error": str(e)}, status=500)
    return JsonResponse({"success": False, "error": "Invalid request method."}, status=405)

@user_passes_test(lambda u: u.is_superuser)
def list_universal_presets(request):
    # Get the first object in UniversalPresets (or create if it doesn't exist)
    universal_preset, created = UniversalPresets.objects.get_or_create()

    # Get all groups created by superusers
    groups = Group.objects.filter(creator__is_superuser=True)

    # Get selected groups in the current preset
    selected_groups = universal_preset.groups.all()

    if request.method == "POST":
        # Update the universal preset with selected groups
        selected_group_ids = request.POST.getlist('selected_groups')
        universal_preset.groups.set(Group.objects.filter(id__in=selected_group_ids))
        return redirect('list_universal_presets')  # Redirect to the same page

    return render(request, 'list_universal_presets.html', {
        'groups': groups,
        'selected_groups': selected_groups,
    })

def generate_category_charts(request, unique_id, category, template_name):
    # Fetch the dataset or return 404 if not found
    dataset = get_object_or_404(Dataset, unique_id=unique_id)

    charts = []

    # Filter CHART_CONFIG for the specified category
    category_charts = {
        name: config for name, config in CHART_CONFIG.items()
        if config.get("category") == category
    }

    # Total counts for Cases and Controls
    cases_queryset = Case.objects.filter(dataset=dataset)
    controls_queryset = Control.objects.filter(dataset=dataset)

    total_cases = cases_queryset.count()
    total_controls = controls_queryset.count()

    for name, config in category_charts.items():
        title = config.get("title", name)
        labels = config.get("labels", ["Condition 1", "Condition 2"])
        filters = config.get("filters", lambda qs: [0, 0])  # Default to [0, 0] if no filters provided

        # Apply filters to Cases and Controls using the provided lambda functions
        case_counts = filters(cases_queryset)
        control_counts = filters(controls_queryset)

        # Ensure that filters return exactly two counts
        if len(case_counts) != 2 or len(control_counts) != 2:
            print(f"Skipping {title} due to unexpected filter counts.")
            continue  # Skip this chart or handle appropriately

        # Extract counts
        case1_count, case2_count = case_counts
        control1_count, control2_count = control_counts

        # Calculate percentages
        case1_pct = (case1_count / total_cases * 100) if total_cases > 0 else 0
        control1_pct = (control1_count / total_controls * 100) if total_controls > 0 else 0
        case2_pct = (case2_count / total_cases * 100) if total_cases > 0 else 0
        control2_pct = (control2_count / total_controls * 100) if total_controls > 0 else 0

        # Prepare data for Cases and Controls
        cases_percentages = [case1_pct, case2_pct]
        controls_percentages = [control1_pct, control2_pct]
        cases_counts = [case1_count, case2_count]
        controls_counts = [control1_count, control2_count]

        # Determine the maximum y-value for dynamic y-axis scaling
        current_max = max(cases_percentages + controls_percentages)
        max_y = current_max + 10 if (current_max + 10) < 100 else 100  # Adjust as needed

        _,_,_,p_value = chi2x2_calculation(case1_count, case2_count, control1_count, control2_count)
        # Create the bar chart
        fig = go.Figure()

        # Trace for Cases
        fig.add_trace(go.Bar(
            x=labels,
            y=cases_percentages,
            name="Cases",
            marker=dict(
                color="#538BD5",  # Blue for Cases
                line=dict(color="black", width=1)
            ),
            hovertemplate=(
                "Condition: %{x}<br>"
                "Cases: %{y:.1f}%<br>"
                "Count: %{customdata}<extra></extra>"
            ),
            customdata=cases_counts,
            text=[f"{pct:.1f}%" for pct in cases_percentages],
            textposition='outside',
            insidetextanchor='middle',
            cliponaxis=False
        ))

        # Trace for Controls
        fig.add_trace(go.Bar(
            x=labels,
            y=controls_percentages,
            name="Controls",
            marker=dict(
                color="#E36C09",  # Orange for Controls
                line=dict(color="black", width=1)
            ),
            hovertemplate=(
                "Condition: %{x}<br>"
                "Controls: %{y:.1f}%<br>"
                "Count: %{customdata}<extra></extra>"
            ),
            customdata=controls_counts,
            text=[f"{pct:.1f}%" for pct in controls_percentages],
            textposition='outside',
            insidetextanchor='middle',
            cliponaxis=False  # Ensure text is clipped within the bar
        ))

        # Update layout
        fig.update_layout(
            title=dict(
                text=f"P-value: {p_value:.4f}" if p_value else "",
                x=0.5,
                font=dict(size=10, color="#B71C1C"),
            ),
            yaxis=dict(
                # title="Percentage (%)",
                range=[0, max_y],  # Adjusted to fit labels inside bars
                tickformat=".1f"   # One decimal place
            ),
            barmode="group",
            bargap=0.3,
            bargroupgap=0,
            template="plotly_white",
            showlegend=True,
            legend=dict(
                orientation="h",
                yanchor="top",
                y=-0.2,
                xanchor="center",
                x=0.5
            ),
            margin=dict(l=20, r=40, t=20, b=60),  # Adjust margins as needed
            # height=350,  # Increased height for better visibility
            modebar=dict(orientation='v')
        )

        # Plotly configuration
        config_plot = {
            'responsive': True,
            'displaylogo': False,
            'displayModeBar': True,  
            'modeBarButtonsToAdd': [],  
            'modeBarButtonsToRemove': [
                'lasso2d', 'select2d', 'toggleSpikelines', 
                'hoverCompareCartesian', 'hoverClosestCartesian', 
                'hoverClosest3d', 'toggleHover', 'zoom2d', 
                'pan2d', 'zoomIn2d', 'zoomOut2d', 'autoScale2d', 
                'zoom3d', 'pan3d', 'orbitRotation', 'tableRotation', 
                'zoomInGeo', 'zoomOutGeo', 'hoverClosestGeo'
            ],
            'toImageButtonOptions': {
                'filename': title, 
                'width': 260,  # Match the chart width
                'scale': 2
            },
        }

        # Convert chart to HTML
        chart_html = fig.to_html(
            full_html=False,
            config=config_plot,
            include_plotlyjs=False,  # Assume Plotly JS is included globally in your base template
            include_mathjax=False
        )

        # Append the chart to the charts list
        charts.append({
            "title": title,
            "chart_html": chart_html,
        })

    context = {
        "dataset": dataset,
        "charts": charts,
    }

    return render(request, template_name, context)

def generate_only_case_chart(cases, clinical_variables):
    charts = []
    total_cases = cases.count()

    for var in clinical_variables:
        # Extract labels and counts
        labels = []
        counts = []
        for category in var['categories']:
            count = cases.filter(category["filter"]).count()
            labels.append(category["label"])
            counts.append(count)

        # Calculate percentages
        percentages = [(count / total_cases * 100) if total_cases else 0 for count in counts]
        percentages = [round(p, 2) for p in percentages]

        # Perform Chi-square test if there are 2 or more categories
        if len(var['categories']) >= 2:
            obs = counts

            # Validate observed counts
            if sum(obs) > 0 and all(count >= 0 for count in obs):
                try:
                    # Perform Chi-square goodness-of-fit test
                    chi_stat, p_value = chisquare(f_obs=obs)
                    p_value = round(p_value, 3)
                except ValueError as ve:
                    print(f"ValueError for {var['label']}: {ve}")
                    p_value = None
                except Exception as e:
                    print(f"Error performing Chi-square test for {var['label']}: {e}")
                    p_value = None
            else:
                print(f"Invalid observed counts for {var['label']}: {obs}")
                p_value = None
        else:
            p_value = None

        # Create Plotly Bar Chart with Black Borders
        fig = go.Figure(data=[
            go.Bar(
                x=labels,  # Categories on X-axis
                y=percentages,  # Percentages on Y-axis
                width=0.4,  # Narrower bars
                customdata=counts,  # Pass counts for hover
                text=[f"{p}%" for p in percentages],  # Percentage text on bars
                textfont=dict(
                    size=14,  # Increased text size
                    color='black'  # Optional: Change text color if needed
                ),
                textposition='outside',
                marker=dict(
                    color='#538BD5',  # Bar fill color
                    line=dict(
                        color='black',  # Border color
                        width=1  # Border width in pixels
                    )
                ),
                hovertemplate='%{x}: %{y}% (%{customdata} counts)<extra></extra>',
                hoverlabel=dict(
                    bgcolor='white',  # Set hover background color to white
                    font=dict(
                        color='black'  # Set hover text color to black for readability
                    )
                )
            )
        ])

        # Customize layout for responsiveness
        fig.update_layout(
            title={
                # 'text': var['label'],
                'y': 0.9,
                'x': 0.5,
                'xanchor': 'center',
                'yanchor': 'top'
            },
            xaxis_title="",  # Remove X-axis title
            yaxis_title="Percentage (%)",  # Y-axis represents percentages
            template="plotly_white",
            margin=dict(l=20, r=20, t=60, b=60),  # Adjust margins as needed
            autosize=True  # Enable autosizing
        )

        # Define Plotly configuration
        config_plot = {
            'responsive': True,
            'displaylogo': False,
            'displayModeBar': True,
            'modeBarButtonsToAdd': [],
            'modeBarButtonsToRemove': [
                'lasso2d', 'select2d', 'toggleSpikelines', 
                'hoverCompareCartesian', 'hoverClosestCartesian', 
                'hoverClosest3d', 'toggleHover', 'zoom2d', 
                'pan2d', 'zoomIn2d', 'zoomOut2d', 'autoScale2d', 
                'zoom3d', 'pan3d', 'orbitRotation', 'tableRotation', 
                'zoomInGeo', 'zoomOutGeo', 'hoverClosestGeo'
            ],
            'toImageButtonOptions': {
                'filename': var['label'], 
                'width': 600,  # Match the chart width if fixed
                'scale': 2
            },
        }

        # Convert Plotly figure to HTML
        chart_html = fig.to_html(
            full_html=False,
            config=config_plot,
            include_plotlyjs=False,  # Plotly JS is included globally
            include_mathjax=False
        )

        # Append chart details to the charts list
        charts.append({
            "title": var['label'],  # Ensure 'title' key is used in the template
            "chart_html": chart_html,
            "p_value": p_value if p_value is not None else "N/A"
        })
    return charts

def demographic_data(request, unique_id):
    return generate_category_charts(
        request=request,
        unique_id=unique_id,
        category="Demographic data",
        template_name='sections/demographic_data.html'
    )

def anthropometric_data(request, unique_id):
    return generate_category_charts(
        request=request,
        unique_id=unique_id,
        category="Anthropometric data",
        template_name='sections/anthropometric_data.html'
    )

def biochemical_data(request, unique_id):
    return generate_category_charts(
        request=request,
        unique_id=unique_id,
        category="Biochemical parameters data",
        template_name='sections/biochemical_data.html'
    )

def obstetric_data(request, unique_id):
    return generate_category_charts(
        request=request,
        unique_id=unique_id,
        category="Obstetric data",
        template_name='sections/obstetric_data.html'
    )

def treatment_data(request, unique_id):
     # Get the dataset object using the unique_id
    dataset = get_object_or_404(Dataset, unique_id=unique_id)

    # Query the Case data related to this dataset
    cases = Case.objects.filter(dataset=dataset)

    # Prepare clinical data for the charts
    clinical_variables = [
        {
            "label": "Radiotherapy",
            "query": "radio_therapy",
            "categories": [
                {"label": "Yes", "filter": Q(radio_therapy="Yes")},
                {"label": "No", "filter": Q(radio_therapy="No")},
            ],
        },
        {
            "label": "Chemotherapy",
            "query": "chemotherapy",
            "categories": [
                {"label": "Yes", "filter": Q(chemotherapy="Yes")},
                {"label": "No", "filter": Q(chemotherapy="No")},
            ],
        },
        {
            "label": "Surgery",
            "query": "surgery",
            "categories": [
                {"label": "Yes", "filter": Q(surgery="Yes")},
                {"label": "No", "filter": Q(surgery="No")},
            ],
        },
        {
            "label": "Hormone therapy",
            "query": "hormone_therapy",
            "categories": [
                {"label": "Yes", "filter": Q(hormone_therapy="Yes")},
                {"label": "No", "filter": Q(hormone_therapy="No")},
            ]
        },
        {
            "label": "Oral medication",
            "query": "oral_medication",
            "categories": [
                {"label": "Yes", "filter": Q(oral_medication="Yes")},
                {"label": "No", "filter": Q(oral_medication="No")},
            ]
        },
    ]
    
    charts = generate_only_case_chart(cases,clinical_variables)

    context = {
        "dataset": dataset,
        "charts": charts
    }
    return render(request, 'sections/treatment_data.html', context)

def genetic_snps_data(request, unique_id):
    dataset = get_object_or_404(Dataset, unique_id=unique_id)
    return render(request, 'sections/genetic_snps_data.html', {'dataset': dataset})

def clinical_data(request, unique_id):
    # Get the dataset object using the unique_id
    dataset = get_object_or_404(Dataset, unique_id=unique_id)

    # Query the Case data related to this dataset
    cases = Case.objects.filter(dataset=dataset)

    # Prepare clinical data for the charts
    clinical_variables = [
        {
            "label": "Age at diagnosis",
            "query": "age_at_diagnosis",
            "categories": [
                {"label": "<40", "filter": Q(age_at_diagnosis__lt=40)},
                {"label": ">=40", "filter": Q(age_at_diagnosis__gte=40)},
            ],
        },
        {
            "label": "Cancer stage",
            "query": "cancer_stage",
            "categories": [
                {"label": "II", "filter": Q(cancer_stage="2")},
                {"label": "III", "filter": Q(cancer_stage="3")},
            ],
        },
        {
            "label": "Cancer type",
            "query": "breast_cancer_type",
            "categories": [
                {"label": "TNBC", "filter": Q(breast_cancer_type__icontains="TNBC")},
                {"label": "Invasive ductal cell carcinoma", "filter": Q(breast_cancer_type__icontains="Invasive ductal cell carcinoma") | Q(breast_cancer_type__icontains="IDCC")},
                {"label": "Ductal cell carcinoma", "filter": Q(breast_cancer_type="Ductal Cell Carcinoma") | Q(breast_cancer_type__icontains="DCC")},
            ],
        },
        {
            "label": "Physical",
            "query": "physical",
            "categories": [
                {"label": "Yes", "filter": Q(physical="Yes")},
                {"label": "No", "filter": Q(physical="No")},
            ],
        },
        {
            "label": "Mammography",
            "query": "mammography",
            "categories": [
                {"label": "Yes", "filter": Q(mammography="Yes")},
                {"label": "No", "filter": Q(mammography="No")},
            ],
        },
        {
            "label": "FNAC",
            "query": "fnac",
            "categories": [
                {"label": "Yes", "filter": Q(fnac="Yes")},
                {"label": "No", "filter": Q(fnac="No")},
            ],
        },
        {
            "label": "Pathology",
            "query": "pathology",
            "categories": [
                {"label": "Yes", "filter": Q(pathology="Yes")},
                {"label": "No", "filter": Q(pathology="No")},
            ],
        },
        {
            "label": "Histopathology",
            "query": "histopathology",
            "categories": [
                {"label": "Yes", "filter": Q(histopathology="Yes")},
                {"label": "No", "filter": Q(histopathology="No")},
            ]
        },
    ]
    
    charts = generate_only_case_chart(cases,clinical_variables)

    context = {
        "dataset": dataset,
        "charts": charts
    }

    return render(request, 'sections/clinical_data.html', context)