"""Scoring endpoints — technique, tactic, threat actor, and organization scores.

Provides granular scoring with breakdowns and configurable weights.
"""

# Import Optional from typing
from typing import Optional

# Import APIRouter, Depends, Query from fastapi
from fastapi import APIRouter, Depends, Query

# Import BaseModel from pydantic
from pydantic import BaseModel

# Import Session from sqlalchemy.orm
from sqlalchemy.orm import Session

# Import get_db from app.database
from app.database import get_db

# Import get_current_user, require_role from app.dependencies.auth
from app.dependencies.auth import get_current_user, require_role

# Import UnitOfWork from app.domain.unit_of_work
from app.domain.unit_of_work import UnitOfWork

# Import User from app.models.user
from app.models.user import User

# Import  from app.services.scoring_config_service
from app.services.scoring_config_service import (
    get_weights_dict,
    update_scoring_weights,
)

# Import  from app.services.scoring_service
from app.services.scoring_service import (
    calculate_tactic_score,
    get_score_history,
    score_actor_by_id,
    score_technique_by_mitre_id,
)

# Assign router = APIRouter(prefix="/scores", tags=["scores"])
router = APIRouter(prefix="/scores", tags=["scores"])


# ── GET /scores/technique/{mitre_id} ─────────────────────────────────


@router.get("/technique/{mitre_id}")
# Define function score_technique
def score_technique(
    # Entry: mitre_id
    mitre_id: str,
    # Entry: db
    db: Session = Depends(get_db),
    # Entry: current_user
    current_user: User = Depends(get_current_user),
) -> dict:
    """Get detailed score with breakdown for a specific technique.

    Args:
        mitre_id (str): MITRE ATT&CK technique ID (e.g. ``T1059``).
        db (Session): SQLAlchemy database session.
        current_user (User): Authenticated user making the request.

    Returns:
        dict: Score value and component breakdown (tests, detection rules, recency, etc.).
    """
    # Return score_technique_by_mitre_id(db, mitre_id)
    return score_technique_by_mitre_id(db, mitre_id)


# ── GET /scores/tactic/{tactic} ──────────────────────────────────────


@router.get("/tactic/{tactic}")
# Define function score_tactic
def score_tactic(
    # Entry: tactic
    tactic: str,
    # Entry: db
    db: Session = Depends(get_db),
    # Entry: current_user
    current_user: User = Depends(get_current_user),
) -> dict:
    """Get average score for a tactic.

    Args:
        tactic (str): MITRE ATT&CK tactic slug (e.g. ``initial-access``).
        db (Session): SQLAlchemy database session.
        current_user (User): Authenticated user making the request.

    Returns:
        dict: Average score and per-technique breakdown for the tactic.
    """
    # Return calculate_tactic_score(tactic, db)
    return calculate_tactic_score(tactic, db)


# ── GET /scores/threat-actor/{id} ────────────────────────────────────


@router.get("/threat-actor/{actor_id}")
# Define function score_threat_actor
def score_threat_actor(
    # Entry: actor_id
    actor_id: str,
    # Entry: db
    db: Session = Depends(get_db),
    # Entry: current_user
    current_user: User = Depends(get_current_user),
) -> dict:
    """Get coverage score against a specific threat actor.

    Args:
        actor_id (str): UUID string of the threat actor to score against.
        db (Session): SQLAlchemy database session.
        current_user (User): Authenticated user making the request.

    Returns:
        dict: Coverage score and per-technique breakdown for the threat actor.
    """
    # Return score_actor_by_id(db, actor_id)
    return score_actor_by_id(db, actor_id)


# ── GET /scores/organization ─────────────────────────────────────────


@router.get("/organization")
# Define function score_organization
def score_organization(
    # Entry: db
    db: Session = Depends(get_db),
    # Entry: current_user
    current_user: User = Depends(get_current_user),
) -> dict:
    """Get the overall organization security score (cached for 5 min).

    Args:
        db (Session): SQLAlchemy database session.
        current_user (User): Authenticated user making the request.

    Returns:
        dict: Aggregate organization score with tactic-level breakdowns.
    """
    # Import get_organization_score_cached from app.services.score_cache
    from app.services.score_cache import get_organization_score_cached

    # Return get_organization_score_cached(db)
    return get_organization_score_cached(db)


# ── GET /scores/history ──────────────────────────────────────────────


@router.get("/history")
# Define function score_history
def score_history(
    # Entry: period
    period: str = Query("90d", pattern="^(30d|90d|1y)$"),
    # Entry: db
    db: Session = Depends(get_db),
    # Entry: current_user
    current_user: User = Depends(get_current_user),
) -> dict:
    """Get historical score data points (weekly).

    Args:
        period (str): Time window for history — one of ``30d``, ``90d``, or ``1y``.
        db (Session): SQLAlchemy database session.
        current_user (User): Authenticated user making the request.

    Returns:
        dict: Weekly score data points for the requested period.
    """
    # Return get_score_history(db, period)
    return get_score_history(db, period)


# ── GET /scores/config ───────────────────────────────────────────────


@router.get("/config")
# Define function get_scoring_config
def get_scoring_config(
    # Entry: db
    db: Session = Depends(get_db),
    # Entry: current_user
    current_user: User = Depends(require_role("admin")),
) -> dict:
    """Get current scoring weights (admin only).

    Args:
        db (Session): SQLAlchemy database session.
        current_user (User): Authenticated admin user.

    Returns:
        dict: Current weight values for each scoring component.
    """
    # Return get_weights_dict(db)
    return get_weights_dict(db)


# ── PATCH /scores/config ─────────────────────────────────────────────


class ScoringConfigUpdate(BaseModel):
    """Partial update payload for the scoring weight configuration."""

    # Assign tests = None
    tests: Optional[float] = None
    # Assign detection_rules = None
    detection_rules: Optional[float] = None
    # Assign d3fend = None
    d3fend: Optional[float] = None
    # Assign recency = None
    recency: Optional[float] = None
    # Assign severity = None
    severity: Optional[float] = None
    # Assign freshness = None
    freshness: Optional[float] = None
    # Assign platform_diversity = None
    platform_diversity: Optional[float] = None


# Apply the @router.patch decorator
@router.patch("/config")
# Define function update_scoring_config
def update_scoring_config(
    # Entry: payload
    payload: ScoringConfigUpdate,
    # Entry: db
    db: Session = Depends(get_db),
    # Entry: current_user
    current_user: User = Depends(require_role("admin")),
) -> dict:
    """Update scoring weights (admin only).

    Weights are persisted in the database and survive restarts.
    Validation enforces that all weights are non-negative and sum to 100.

    Args:
        payload (ScoringConfigUpdate): Partial weight update; only set fields are changed.
        db (Session): SQLAlchemy database session.
        current_user (User): Authenticated admin user.

    Returns:
        dict: Confirmation message plus the full updated weight configuration.
    """
    # Open context manager
    with UnitOfWork(db) as uow:
        # Assign result = update_scoring_weights(
        result = update_scoring_weights(
            db,
            # Keyword argument: tests
            tests=payload.tests,
            # Keyword argument: detection_rules
            detection_rules=payload.detection_rules,
            # Keyword argument: d3fend
            d3fend=payload.d3fend,
            # Keyword argument: recency
            recency=payload.recency,
            # Keyword argument: severity
            severity=payload.severity,
            # Keyword argument: freshness
            freshness=payload.freshness,
            # Keyword argument: platform_diversity
            platform_diversity=payload.platform_diversity,
            # Keyword argument: updated_by
            updated_by=current_user.id,
        )
        # Call uow.commit()
        uow.commit()

    # Import invalidate from app.services.score_cache
    from app.services.score_cache import invalidate
    # Call invalidate()
    invalidate()

    # Return {"message": "Scoring config updated", **result}
    return {"message": "Scoring config updated", **result}
