"""
Formulário de extração de dados — revisão LLMs médicas / Edge AI.
Tipos: O=texto, M=texto longo, B=booleano, I=inteiro, S=seleção múltipla.
"""

from django.db import models

from reviews.models import DataExtractionField, DataExtractionLookup

# (descrição, tipo, opções ou None)
EXTRACTION_FORM: list[tuple[str, str, list[str] | None]] = [
    ("Descrição", "M", None),
    ("Title", "O", None),
    ("Authors", "O", None),
    ("Year", "I", None),
    ("Source", "O", None),
    ("Peer Reviewed", "B", None),
    ("Texto completo disponível", "B", None),
    (
        "Application Domain",
        "S",
        [
            "Biosignal Analysis",
            "CPS/IoT",
            "Clinical Decision Support Biomedical",
            "General Edge AI",
            "General Healthcare",
            "Healthcare",
            "IoT in Healthcare",
            "Medical Imaging",
            "Medical Natural Language Processing",
            "Other",
            "Smart Hospital",
            "Smart Office",
            "Telemedicine",
        ],
    ),
    ("Medical Context", "B", None),
    ("Dataset/Senário", "O", None),
    (
        "Model Type",
        "S",
        [
            "Based on CNN",
            "Decoder-only LLM Model",
            "Encoder-Decoder Transformer",
            "Hybrid Architecture",
            "Mixture of Experts (MoE)",
            "Multimodal LLM Model",
            "NNN/LSTM",
            "Other",
            "Quantum Neural Network",
            "Quantum-Inspired Neural Network",
            "State-Space Model (SSM) / Mamba",
            "Transformer",
            "Vision Transformer (ViT)",
            "Vision-Language Model (VLM)",
            "Wavelet Neural Network",
        ],
    ),
    ("LLM-based", "B", None),
    ("Transformer-based", "B", None),
    ("Wavelet-based Approach", "B", None),
    ("Quantum-Inspired Approach", "B", None),
    ("Edge AI Context", "B", None),
    ("Offline Execution", "B", None),
    (
        "Deployment Architecture",
        "S",
        [
            "Centralized Cloud",
            "Cloud-edge Hybrid",
            "Distributed Edge",
            "Edge-only",
            "Federated",
            "Not Reported",
            "On-device",
        ],
    ),
    (
        "Edge Device Type",
        "S",
        [
            "Cloud-assisted Edge",
            "Edge Server",
            "Embedded Device",
            "IoT Gateway",
            "Jetson Nano",
            "Jetson Orin",
            "Jetson Xavier",
            "Laptop-class Edge",
            "Not Reported",
            "Other",
            "Raspberry Pi",
            "Smartphone",
            "Wearable",
        ],
    ),
    ("Hardware Platform", "O", None),
    (
        "GPU/CPU/NPU Usage",
        "S",
        [
            "CPU",
            "Edge Accelerator",
            "FPGA",
            "GPU",
            "NPU",
            "Not Reported",
            "Other",
            "TPU",
        ],
    ),
    (
        "Compression Technique",
        "S",
        [
            "Entropy Encoding",
            "Feature Fusion / PCA",
            "Knowledge Compression",
            "Low-Rank",
            "Model Compression",
            "Model Merging",
            "None",
            "PEFT/LoRA",
            "Prompt Optimization",
            "Quantization",
            "Sparse Representation",
            "Weight Sharing",
            "Ternary / Sub-byte Quantization",
        ],
    ),
    (
        "Quantization Technique",
        "S",
        [
            "Dynamic Quantization",
            "FP16",
            "INT4",
            "INT8",
            "Mixed Precision",
            "None",
            "Post-training Quantization",
            "Quantization-aware Training",
            "Sub-byte Quantization (W2, W3)",
            "Ternary / Sub-byte Quantization",
        ],
    ),
    (
        "Pruning Technique",
        "S",
        [
            "Asymmetric Pruning",
            "Layer Pruning",
            "Magnitude-based",
            "None",
            "Structured Pruning",
            "Token Pruning",
            "Unstructured Pruning",
        ],
    ),
    (
        "Distillation Technique",
        "S",
        [
            "None",
            "Progressive Distillation",
            "Self-Distillation",
            "Teacher-Student",
        ],
    ),
    (
        "Energy Efficiency Metric",
        "S",
        [
            "Battery Consumption",
            "CPU Utilization",
            "Compression Rate",
            "Energy Consumption",
            "FLOPs",
            "GPU Utilization",
            "Latency",
            "Memory Footprint",
            "None",
            "Power Usage",
            "RAM Usage",
            "Thermal Efficiency",
            "Throughput",
            "Token Efficiency",
        ],
    ),
    ("Latency Reduction", "O", None),
    ("Memory Reduction", "O", None),
    ("Energy Consumption (Watts)", "O", None),
    ("Model Size (Parameters/Billion)", "O", None),
    ("Original Model Name", "O", None),
    (
        "Evaluation Type",
        "S",
        [
            "Ablation Study",
            "Benchmark",
            "Case Study",
            "Comparative Study",
            "Experimental",
            "Prototype",
            "Review",
            "Simulation",
        ],
    ),
    ("Main Contribution", "M", None),
    ("Identified Limitations", "M", None),
    ("Research Gap", "M", None),
    (
        "Efficiency Strategy",
        "S",
        [
            "Architectural Optimization / Lightweight Design",
            "Collaborative Inference",
            "Compression",
            "Distillation",
            "Edge Computing",
            "Low-rank",
            "Model Merging",
            "Model Sharding",
            "None",
            "Parameter-Efficient Fine-Tuning (PEFT/LoRA)",
            "Prompt Optimization",
            "Pruning",
            "Quantization",
            "RAG Optimization",
            "Topic Modeling",
        ],
    ),
]

FIELD_TYPE_LABELS = {
    "O": "Texto",
    "M": "Texto longo",
    "B": "Sim/Não",
    "I": "Inteiro",
    "F": "Decimal",
    "D": "Data",
    "S": "Seleção múltipla",
}


def parse_lookup_options_text(raw: str) -> list[str]:
    """Uma opção por linha ou separadas por vírgula."""
    values: list[str] = []
    for line in (raw or "").replace(",", "\n").splitlines():
        value = line.strip()
        if value and value not in values:
            values.append(value)
    return values


def create_extraction_field(
    review,
    description: str,
    field_type: str = "O",
    *,
    lookup_options: list[str] | None = None,
) -> tuple[DataExtractionField | None, str | None]:
    """
    Cria um campo no formulário da revisão.
    Retorna (campo, mensagem_de_erro).
    """
    desc = (description or "").strip()
    if not desc:
        return None, "Informe o nome do campo."
    ftype = (field_type or "O").strip().upper()
    if ftype not in DataExtractionField.FIELD_TYPES:
        return None, "Tipo de campo inválido."

    existing = DataExtractionField.objects.filter(
        review_id=review.id, description=desc
    ).first()
    if existing:
        return None, f'Já existe um campo chamado "{desc}".'

    max_order = (
        DataExtractionField.objects.filter(review_id=review.id)
        .aggregate(max_order=models.Max("order"))
        .get("max_order")
    )
    order = (max_order if max_order is not None else -1) + 1

    field = DataExtractionField(
        review_id=review.id,
        description=desc,
        field_type=ftype,
        order=order,
    )
    field.save()
    

    if ftype == "S" and lookup_options:
        for value in lookup_options:
            DataExtractionLookup.objects.create(field_id=field.id, value=value)

    
    return field, None


def add_extraction_lookup(
    review,
    field_id: int,
    value: str,
) -> tuple[DataExtractionLookup | None, str | None]:
    """Adiciona uma opção a um campo de seleção múltipla (tipo S)."""
    field = DataExtractionField.objects.filter(
        id=field_id, review_id=review.id
    ).first()
    if not field:
        return None, "Campo não encontrado."
    if field.field_type != "S":
        return None, "Este campo não é seleção múltipla."

    val = (value or "").strip()
    if not val:
        return None, "Informe o texto da opção."
    if len(val) > 255:
        return None, "A opção deve ter no máximo 255 caracteres."

    existing = DataExtractionLookup.objects.filter(field_id=field.id, value=val).first()
    if existing:
        return None, f'Opção "{val}" já existe em "{field.description}".'

    lookup = DataExtractionLookup(field_id=field.id, value=val)
    lookup.save()
    
    return lookup, None


def seed_extraction_form(review, *, replace: bool = True) -> int:
    """Aplica o formulário padrão da revisão. Retorna quantidade de campos."""
    if replace:
        for field in list(review.extraction_fields.all()):
            field.delete()
        
        

    for order, (description, field_type, options) in enumerate(EXTRACTION_FORM):
        field = DataExtractionField(
            review_id=review.id,
            description=description,
            field_type=field_type,
            order=order,
        )
        field.save()
        
        if field_type == "S" and options:
            for value in options:
                DataExtractionLookup.objects.create(field_id=field.id, value=value)
    
    return len(EXTRACTION_FORM)


def ensure_extraction_fields(review) -> int:
    """Adiciona campos do protocolo que ainda não existem (ex.: Descrição em revisões antigas)."""
    existing = {f.description for f in review.extraction_fields.all()}
    added = 0
    for order, (description, field_type, options) in enumerate(EXTRACTION_FORM):
        if description in existing:
            continue
        for f in review.extraction_fields.all():
            if f.order >= order:
                f.order += 1
        field = DataExtractionField(
            review_id=review.id,
            description=description,
            field_type=field_type,
            order=order,
        )
        field.save()
        
        if field_type == "S" and options:
            for value in options:
                DataExtractionLookup.objects.create(field_id=field.id, value=value)
        existing.add(description)
        added += 1
    if added:
        for f in DataExtractionField.objects.filter(review_id=review.id).order_by("order"):
            if f.order is None:
                f.order = 0
                f.save(update_fields=["order"])
    return added
