"""
Routing evaluation service for dynamic survey flow.

Evaluates QuestionRoutingRule conditions against user answers
to determine the next question, skip logic, or early termination.
"""

import json
import logging
from dataclasses import dataclass
from typing import Optional

logger = logging.getLogger(__name__)


@dataclass
class RoutingResult:
    action: str  # "next", "skip_to", "end_survey"
    next_question_id: Optional[str] = None
    is_complete: bool = False


def evaluate_condition(operator, rule_value, answer_value):
    """
    Evaluate a single routing rule condition.

    Args:
        operator: RoutingOperator value (e.g., "equals", "gt", "lt")
        rule_value: The value from the routing rule (JSON-serializable)
        answer_value: The user's actual answer (string or numeric)

    Returns:
        bool: Whether the condition matches
    """
    if operator == "answered":
        return answer_value is not None and str(answer_value).strip() != ""
    if operator == "not_answered":
        return answer_value is None or str(answer_value).strip() == ""

    if answer_value is None:
        return False

    answer_str = str(answer_value).strip()

    if operator == "equals":
        return answer_str == str(rule_value)
    if operator == "not_equals":
        return answer_str != str(rule_value)
    if operator == "contains":
        return str(rule_value) in answer_str

    if operator in ("gt", "lt"):
        try:
            answer_num = float(answer_str)
            rule_num = float(rule_value)
            if operator == "gt":
                return answer_num > rule_num
            return answer_num < rule_num
        except (ValueError, TypeError):
            return False

    if operator == "in_list":
        if isinstance(rule_value, list):
            return answer_str in [str(v) for v in rule_value]
        return answer_str == str(rule_value)

    return False


def get_next_question(
    current_question_id,
    answer_value,
    all_questions,
    routing_rules,
    answered_questions=None,
):
    """
    Determine the next question after answering the current one.

    Args:
        current_question_id: UUID string of the just-answered question
        answer_value: The user's answer (string, number, or None)
        all_questions: Ordered list of SurveyQuestion dicts with routing data
        routing_rules: List of routing rule dicts for the template
        answered_questions: Set of question IDs already answered (for back nav)

    Returns:
        RoutingResult with next action and question ID
    """
    question_map = {str(q["id"]): q for q in all_questions}
    question_ids = [str(q["id"]) for q in all_questions]

    if current_question_id not in question_map:
        return RoutingResult(action="next")

    rules_for_question = [r for r in routing_rules if str(r["source_question"]) == current_question_id]
    rules_for_question.sort(key=lambda r: r.get("order", 0))

    for rule in rules_for_question:
        rule_value = rule.get("value")
        if isinstance(rule_value, str):
            try:
                rule_value = json.loads(rule_value)
            except (json.JSONDecodeError, TypeError):
                pass

        if evaluate_condition(rule["operator"], rule_value, answer_value):
            if rule["action"] == "end_survey":
                return RoutingResult(action="end_survey", is_complete=True)
            if rule["action"] == "skip_to" and rule.get("target_question"):
                target_id = str(rule["target_question"])
                if target_id in question_map:
                    return RoutingResult(action="skip_to", next_question_id=target_id)

    try:
        current_idx = question_ids.index(current_question_id)
    except ValueError:
        return RoutingResult(action="next")

    remaining = question_ids[current_idx + 1 :]
    for next_id in remaining:
        next_q = question_map[next_id]
        if next_q.get("is_conditional"):
            continues = skip_conditional_chain(next_id, question_map, question_ids, routing_rules, answered_questions)
            if continues:
                continue
        return RoutingResult(action="next", next_question_id=next_id)

    return RoutingResult(action="next", is_complete=True)


def skip_conditional_chain(question_id, question_map, question_ids, routing_rules, answered_questions):
    """
    Check if a conditional question should be skipped (no routing rule targets it
    based on current answers).

    Returns True if the question should be skipped, False if it should be shown.
    """
    rules_targeting = [
        r for r in routing_rules if str(r.get("target_question")) == question_id and r["action"] == "skip_to"
    ]
    if rules_targeting:
        return False

    incoming_show_rules = [r for r in routing_rules if str(r.get("target_question")) == question_id]
    if not incoming_show_rules:
        return True

    return False


def resolve_question_path(all_questions, routing_rules):
    """
    Pre-compute the full question path with routing rules.

    Returns the list of questions in order with routing metadata
    attached to each question.
    """
    result = []
    for q in all_questions:
        q_data = {
            "id": str(q.id),
            "text": q.text,
            "text_ar": q.text_ar,
            "question_type": q.question_type,
            "order": q.order,
            "is_required": q.is_required,
            "choices_json": q.choices_json or [],
            "is_conditional": q.is_conditional,
            "routing_rules": [],
        }
        for r in routing_rules:
            if str(r.source_question_id) == str(q.id):
                q_data["routing_rules"].append(
                    {
                        "id": str(r.id),
                        "operator": r.operator,
                        "value": r.value,
                        "action": r.action,
                        "target_question_id": str(r.target_question_id) if r.target_question_id else None,
                        "order": r.order,
                    }
                )
        result.append(q_data)
    return result
