"""
Focus Areas Router
Manages dynamic focus area definitions and user preferences
"""
from fastapi import APIRouter, HTTPException, Depends
from pydantic import BaseModel, Field
from typing import Optional, List
from db import get_db, get_cursor, r2d
from auth import require_auth
from focus_area_usage_helpers import coerce_usage_type_keys

router = APIRouter(prefix="/api/focus-areas", tags=["focus-areas"])

# ============================================================================
# Models
# ============================================================================

class FocusAreaCreate(BaseModel):
    """Create new focus area definition"""
    key: str
    name_de: str
    name_en: Optional[str] = None
    icon: Optional[str] = None
    description: Optional[str] = None
    category: str = 'custom'

class FocusAreaUpdate(BaseModel):
    """Update focus area definition"""
    name_de: Optional[str] = None
    name_en: Optional[str] = None
    icon: Optional[str] = None
    description: Optional[str] = None
    category: Optional[str] = None
    is_active: Optional[bool] = None

class UserFocusPreferences(BaseModel):
    """User's focus area weightings (dynamic)"""
    preferences: dict  # {focus_area_id: weight_pct}


class FocusAreaUsageTypesUpdate(BaseModel):
    """Replace all usage-type assignments for one focus area (admin)."""
    usage_type_keys: List[str] = Field(default_factory=list)

# ============================================================================
# Focus Area Definitions (Admin)
# ============================================================================

@router.get("/definitions")
def list_focus_area_definitions(
    session: dict = Depends(require_auth),
    include_inactive: bool = False
):
    """
    List all available focus area definitions.

    Query params:
    - include_inactive: Include inactive focus areas (default: false)

    Returns focus areas grouped by category.
    """
    with get_db() as conn:
        cur = get_cursor(conn)

        query = """
            SELECT id, key, name_de, name_en, icon, description, category, is_active,
                   created_at, updated_at,
                   COALESCE(
                       (
                           SELECT json_agg(faut.key ORDER BY faut.sort_order, faut.key)
                           FROM focus_area_definition_usage_types fadut
                           JOIN focus_area_usage_types faut ON faut.id = fadut.usage_type_id
                           WHERE fadut.focus_area_id = focus_area_definitions.id
                       ),
                       '[]'::json
                   ) AS allowed_usage_type_keys
            FROM focus_area_definitions
            WHERE is_active = true OR %s
            ORDER BY category, name_de
        """

        cur.execute(query, (include_inactive,))
        areas = []
        for row in cur.fetchall():
            d = r2d(row)
            d['allowed_usage_type_keys'] = coerce_usage_type_keys(d.get('allowed_usage_type_keys'))
            areas.append(d)

        # Group by category
        grouped = {}
        for area in areas:
            cat = area['category'] or 'other'
            if cat not in grouped:
                grouped[cat] = []
            grouped[cat].append(area)

        if session.get('role') != 'admin':
            for area in areas:
                area.pop('allowed_usage_type_keys', None)

        return {
            "areas": areas,
            "grouped": grouped,
            "total": len(areas)
        }

@router.post("/definitions")
def create_focus_area_definition(
    data: FocusAreaCreate,
    session: dict = Depends(require_auth)
):
    """
    Create new focus area definition (Admin only).

    Note: Requires admin role.
    """
    # Admin check
    if session.get('role') != 'admin':
        raise HTTPException(status_code=403, detail="Admin-Rechte erforderlich")

    with get_db() as conn:
        cur = get_cursor(conn)

        # Check if key already exists
        cur.execute(
            "SELECT id FROM focus_area_definitions WHERE key = %s",
            (data.key,)
        )
        if cur.fetchone():
            raise HTTPException(
                status_code=400,
                detail=f"Focus Area mit Key '{data.key}' existiert bereits"
            )

        # Insert
        cur.execute("""
            INSERT INTO focus_area_definitions
                (key, name_de, name_en, icon, description, category)
            VALUES (%s, %s, %s, %s, %s, %s)
            RETURNING id
        """, (
            data.key, data.name_de, data.name_en,
            data.icon, data.description, data.category
        ))

        area_id = cur.fetchone()['id']

        return {
            "id": area_id,
            "message": f"Focus Area '{data.name_de}' erstellt"
        }

@router.put("/definitions/{area_id}")
def update_focus_area_definition(
    area_id: str,
    data: FocusAreaUpdate,
    session: dict = Depends(require_auth)
):
    """Update focus area definition (Admin only)"""
    # Admin check
    if session.get('role') != 'admin':
        raise HTTPException(status_code=403, detail="Admin-Rechte erforderlich")

    with get_db() as conn:
        cur = get_cursor(conn)

        # Build dynamic UPDATE
        updates = []
        values = []

        if data.name_de is not None:
            updates.append("name_de = %s")
            values.append(data.name_de)
        if data.name_en is not None:
            updates.append("name_en = %s")
            values.append(data.name_en)
        if data.icon is not None:
            updates.append("icon = %s")
            values.append(data.icon)
        if data.description is not None:
            updates.append("description = %s")
            values.append(data.description)
        if data.category is not None:
            updates.append("category = %s")
            values.append(data.category)
        if data.is_active is not None:
            updates.append("is_active = %s")
            values.append(data.is_active)

        if not updates:
            raise HTTPException(status_code=400, detail="Keine Änderungen angegeben")

        updates.append("updated_at = NOW()")
        values.append(area_id)

        query = f"""
            UPDATE focus_area_definitions
            SET {', '.join(updates)}
            WHERE id = %s
            RETURNING id
        """

        cur.execute(query, values)

        if not cur.fetchone():
            raise HTTPException(status_code=404, detail="Focus Area nicht gefunden")

        return {"message": "Focus Area aktualisiert"}

@router.delete("/definitions/{area_id}")
def delete_focus_area_definition(
    area_id: str,
    session: dict = Depends(require_auth)
):
    """
    Delete focus area definition (Admin only).

    Cascades: Deletes all goal_focus_contributions referencing this area.
    """
    # Admin check
    if session.get('role') != 'admin':
        raise HTTPException(status_code=403, detail="Admin-Rechte erforderlich")

    with get_db() as conn:
        cur = get_cursor(conn)

        # Check if area is used
        cur.execute(
            "SELECT COUNT(*) as count FROM goal_focus_contributions WHERE focus_area_id = %s",
            (area_id,)
        )
        count = cur.fetchone()['count']

        if count > 0:
            raise HTTPException(
                status_code=400,
                detail=f"Focus Area wird von {count} Ziel(en) verwendet. "
                       "Bitte erst Zuordnungen entfernen oder auf 'inaktiv' setzen."
            )

        # Delete
        cur.execute(
            "DELETE FROM focus_area_definitions WHERE id = %s RETURNING id",
            (area_id,)
        )

        if not cur.fetchone():
            raise HTTPException(status_code=404, detail="Focus Area nicht gefunden")

        return {"message": "Focus Area gelöscht"}


@router.get("/usage-types")
def list_focus_area_usage_types(session: dict = Depends(require_auth)):
    """
    Liste aller systemdefinierten Nutzungstypen (Admin, Konfigurations-UI).
    Keine freie Anlage neuer Typen über die API.
    """
    if session.get('role') != 'admin':
        raise HTTPException(status_code=403, detail="Admin-Rechte erforderlich")

    with get_db() as conn:
        cur = get_cursor(conn)
        cur.execute("""
            SELECT id, key, label_de, sort_order
            FROM focus_area_usage_types
            ORDER BY sort_order, key
        """)
        rows = [r2d(r) for r in cur.fetchall()]
        return {"usage_types": rows, "total": len(rows)}


@router.put("/definitions/{area_id}/usage-types")
def replace_focus_area_usage_types(
    area_id: str,
    data: FocusAreaUsageTypesUpdate,
    session: dict = Depends(require_auth),
):
    """
    Ersetzt die Nutzungstyp-Zuweisungen einer Focus Area (Admin).
    Leere Liste entfernt alle Zuordnungen. Unbekannte Keys → 400.
    """
    if session.get('role') != 'admin':
        raise HTTPException(status_code=403, detail="Admin-Rechte erforderlich")

    keys = list(dict.fromkeys(data.usage_type_keys))  # dedupe, preserve order

    with get_db() as conn:
        cur = get_cursor(conn)

        cur.execute(
            "SELECT id FROM focus_area_definitions WHERE id = %s",
            (area_id,),
        )
        if not cur.fetchone():
            raise HTTPException(status_code=404, detail="Focus Area nicht gefunden")

        if not keys:
            cur.execute(
                "DELETE FROM focus_area_definition_usage_types WHERE focus_area_id = %s",
                (area_id,),
            )
            return {"message": "Nutzungstyp-Zuweisungen entfernt", "usage_type_keys": []}

        placeholders = ','.join(['%s'] * len(keys))
        cur.execute(
            f"""
            SELECT id, key FROM focus_area_usage_types
            WHERE key IN ({placeholders})
            """,
            keys,
        )
        found = {row['key']: row['id'] for row in cur.fetchall()}
        missing = [k for k in keys if k not in found]
        if missing:
            raise HTTPException(
                status_code=400,
                detail=f"Unbekannte Nutzungstyp-Keys: {', '.join(missing)}",
            )

        cur.execute(
            "DELETE FROM focus_area_definition_usage_types WHERE focus_area_id = %s",
            (area_id,),
        )
        for k in keys:
            ut_id = found[k]
            cur.execute(
                """
                INSERT INTO focus_area_definition_usage_types (focus_area_id, usage_type_id)
                VALUES (%s, %s)
                ON CONFLICT (focus_area_id, usage_type_id) DO NOTHING
                """,
                (area_id, ut_id),
            )

        return {"message": "Nutzungstyp-Zuweisungen aktualisiert", "usage_type_keys": keys}

# ============================================================================
# User Focus Preferences
# ============================================================================

@router.get("/user-preferences")
def get_user_focus_preferences(session: dict = Depends(require_auth)):
    """
    Get user's focus area weightings (dynamic system).

    Returns focus areas with user-set weights, grouped by category.
    """
    pid = session['profile_id']

    with get_db() as conn:
        cur = get_cursor(conn)

        # Get dynamic preferences (Migration 032)
        try:
            cur.execute("""
                SELECT
                    fa.id, fa.key, fa.name_de, fa.name_en, fa.icon,
                    fa.category, fa.description,
                    ufw.weight
                FROM user_focus_area_weights ufw
                JOIN focus_area_definitions fa ON ufw.focus_area_id = fa.id
                WHERE ufw.profile_id = %s AND ufw.weight > 0
                ORDER BY fa.category, fa.name_de
            """, (pid,))

            weights = [r2d(row) for row in cur.fetchall()]

            # Calculate percentages from weights
            total_weight = sum(w['weight'] for w in weights)
            if total_weight > 0:
                for w in weights:
                    w['percentage'] = round((w['weight'] / total_weight) * 100)
            else:
                for w in weights:
                    w['percentage'] = 0

            # Group by category
            grouped = {}
            for w in weights:
                cat = w['category'] or 'other'
                if cat not in grouped:
                    grouped[cat] = []
                grouped[cat].append(w)

            return {
                "weights": weights,
                "grouped": grouped,
                "total_weight": total_weight
            }

        except Exception as e:
            # Migration 032 not applied yet - return empty
            print(f"[WARNING] user_focus_area_weights not found: {e}")
            return {
                "weights": [],
                "grouped": {},
                "total_weight": 0
            }

@router.put("/user-preferences")
def update_user_focus_preferences(
    data: dict,
    session: dict = Depends(require_auth)
):
    """
    Update user's focus area weightings (dynamic system).

    Expects: { "weights": { "focus_area_id": weight, ... } }
    Weights are relative (0-100), normalized in display only.
    """
    pid = session['profile_id']

    if 'weights' not in data:
        raise HTTPException(status_code=400, detail="'weights' field required")

    weights = data['weights']  # Dict: focus_area_id → weight

    with get_db() as conn:
        cur = get_cursor(conn)

        # Delete existing weights
        cur.execute(
            "DELETE FROM user_focus_area_weights WHERE profile_id = %s",
            (pid,)
        )

        # Insert new weights (only non-zero)
        for focus_area_id, weight in weights.items():
            weight_int = int(weight)
            if weight_int > 0:
                cur.execute("""
                    INSERT INTO user_focus_area_weights
                        (profile_id, focus_area_id, weight)
                    VALUES (%s, %s, %s)
                    ON CONFLICT (profile_id, focus_area_id)
                    DO UPDATE SET
                        weight = EXCLUDED.weight,
                        updated_at = NOW()
                """, (pid, focus_area_id, weight_int))

        return {
            "message": "Focus Area Gewichtungen aktualisiert",
            "count": len([w for w in weights.values() if int(w) > 0])
        }

# ============================================================================
# Stats & Analytics
# ============================================================================

@router.get("/stats")
def get_focus_area_stats(session: dict = Depends(require_auth)):
    """
    Get focus area statistics for current user.

    Returns:
    - Progress per focus area (avg of all contributing goals)
    - Goal count per focus area
    - Top/bottom performing areas
    """
    pid = session['profile_id']

    with get_db() as conn:
        cur = get_cursor(conn)

        cur.execute("""
            SELECT
                fa.id, fa.key, fa.name_de, fa.icon, fa.category,
                COUNT(DISTINCT gfc.goal_id) as goal_count,
                AVG(g.progress_pct) as avg_progress,
                SUM(gfc.contribution_weight) as total_contribution
            FROM focus_area_definitions fa
            LEFT JOIN goal_focus_contributions gfc ON fa.id = gfc.focus_area_id
            LEFT JOIN goals g ON gfc.goal_id = g.id AND g.profile_id = %s
            WHERE fa.is_active = true
            GROUP BY fa.id
            HAVING COUNT(DISTINCT gfc.goal_id) > 0  -- Only areas with goals
            ORDER BY avg_progress DESC NULLS LAST
        """, (pid,))

        stats = [r2d(row) for row in cur.fetchall()]

        return {
            "stats": stats,
            "top_area": stats[0] if stats else None,
            "bottom_area": stats[-1] if len(stats) > 1 else None
        }
