import logging
import os
import shutil
from pathlib import Path
from datetime import datetime
from typing import List, Optional
from uuid import uuid4

from fastapi import APIRouter, HTTPException, Query, UploadFile, File, Request
from fastapi.responses import FileResponse
from pydantic import BaseModel, Field
from app.core.database import execute_query, execute_query_single
from app.models.schemas import TodoStep, TodoStepCreate, TodoStepUpdate, QuickCreateAnalysis
from app.core.config import settings
from app.services.email_service import EmailService
from app.services.case_analysis_service import CaseAnalysisService

try:
    import extract_msg
except ImportError:
    extract_msg = None
import email
from email.header import decode_header

logger = logging.getLogger(__name__)
router = APIRouter()


def _get_user_id_from_request(request: Request) -> int:
    user_id = getattr(request.state, "user_id", None)
    if user_id is not None:
        try:
            return int(user_id)
        except ValueError:
            raise HTTPException(status_code=400, detail="Invalid user_id format")

    user_id = request.query_params.get("user_id")
    if user_id:
        try:
            return int(user_id)
        except ValueError:
            raise HTTPException(status_code=400, detail="Invalid user_id format")

    raise HTTPException(status_code=401, detail="User not authenticated - provide user_id query parameter")


def _normalize_case_status(status_value: Optional[str]) -> str:
    if not status_value:
        return "åben"

    normalized = str(status_value).strip().lower()
    if normalized == "afventer":
        return "åben"
    if normalized in {"åben", "lukket"}:
        return normalized
    return "åben"


def _normalize_optional_timestamp(value: Optional[str], field_name: str) -> Optional[str]:
    if value is None:
        return None

    if isinstance(value, datetime):
        return value.strftime("%Y-%m-%d %H:%M:%S")

    text = str(value).strip()
    if not text:
        return None

    try:
        parsed = datetime.fromisoformat(text.replace("Z", "+00:00"))
        if parsed.tzinfo is not None:
            parsed = parsed.replace(tzinfo=None)
        return parsed.strftime("%Y-%m-%d %H:%M:%S")
    except ValueError:
        raise HTTPException(status_code=400, detail=f"Invalid datetime format for {field_name}")


def _coerce_optional_int(value: Optional[object], field_name: str) -> Optional[int]:
    if value is None or value == "":
        return None
    try:
        return int(value)
    except (TypeError, ValueError):
        raise HTTPException(status_code=400, detail=f"Invalid {field_name}")


def _validate_user_id(user_id: Optional[int], field_name: str = "ansvarlig_bruger_id") -> None:
    if user_id is None:
        return
    exists = execute_query("SELECT 1 FROM users WHERE user_id = %s", (user_id,))
    if not exists:
        raise HTTPException(status_code=400, detail=f"Invalid {field_name}")


def _validate_group_id(group_id: Optional[int], field_name: str = "assigned_group_id") -> None:
    if group_id is None:
        return
    exists = execute_query("SELECT 1 FROM groups WHERE id = %s", (group_id,))
    if not exists:
        raise HTTPException(status_code=400, detail=f"Invalid {field_name}")

# ============================================================================
# QUICKCREATE AI ANALYSIS
# ============================================================================

class QuickCreateRequest(BaseModel):
    text: str = Field(..., min_length=1, max_length=5000)
    user_id: int


@router.post("/sag/analyze-quick-create", response_model=QuickCreateAnalysis)
async def analyze_quick_create(request: QuickCreateRequest):
    """
    Analyze case description text using AI for QuickCreate feature.
    Returns structured suggestions for customer, technician, priority, tags, etc.
    """
    try:
        logger.info(f"🔍 QuickCreate analysis requested by user {request.user_id}, text length: {len(request.text)}")
        
        # Initialize service and analyze
        service = CaseAnalysisService()
        analysis = await service.analyze_case_text(request.text, request.user_id)
        
        logger.info(f"✅ QuickCreate analysis complete: confidence={analysis.confidence}, priority={analysis.suggested_priority}")
        return analysis
        
    except Exception as e:
        logger.error(f"❌ QuickCreate analysis failed: {str(e)}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"Analysis failed: {str(e)}")

# ============================================================================
# SAGER - CRUD Operations
# ============================================================================

@router.get("/sag")
async def list_sager(
    status: Optional[str] = Query(None),
    tag: Optional[str] = Query(None),
    customer_id: Optional[int] = Query(None),
    ansvarlig_bruger_id: Optional[int] = Query(None),
    assigned_group_id: Optional[int] = Query(None),
    include_deferred: bool = Query(False),
    q: Optional[str] = Query(None),
    limit: Optional[int] = Query(None, ge=1, le=200),
    offset: int = Query(0, ge=0),
):
    """List all cases with optional filtering."""
    try:
        query = """
            SELECT s.*, 
                   COALESCE(u.full_name, u.username) AS ansvarlig_navn,
                   g.name AS assigned_group_name
            FROM sag_sager s
            LEFT JOIN users u ON u.user_id = s.ansvarlig_bruger_id
            LEFT JOIN groups g ON g.id = s.assigned_group_id
            WHERE s.deleted_at IS NULL
        """
        params = []

        if not include_deferred:
            query += " AND (deferred_until IS NULL OR deferred_until <= NOW())"
        
        if status:
            query += " AND s.status = %s"
            params.append(status)
        if customer_id:
            query += " AND s.customer_id = %s"
            params.append(customer_id)
        if ansvarlig_bruger_id:
            query += " AND s.ansvarlig_bruger_id = %s"
            params.append(ansvarlig_bruger_id)
        if assigned_group_id:
            query += " AND s.assigned_group_id = %s"
            params.append(assigned_group_id)

        if q:
            query += " AND (LOWER(s.titel) LIKE %s OR CAST(s.id AS TEXT) LIKE %s)"
            q_like = f"%{q.lower()}%"
            params.extend([q_like, q_like])
        
        query += " ORDER BY s.created_at DESC"

        if limit is not None:
            query += " LIMIT %s OFFSET %s"
            params.extend([limit, offset])
        
        cases = execute_query(query, tuple(params))
        
        # If tag filter, filter in Python after fetch
        if tag:
            case_ids = [case["id"] for case in cases]
            if case_ids:
                tag_query = "SELECT sag_id FROM sag_tags WHERE tag_navn = %s AND deleted_at IS NULL"
                tagged_cases = execute_query(tag_query, (tag,))
                tagged_ids = set(t["sag_id"] for t in tagged_cases)
                cases = [c for c in cases if c["id"] in tagged_ids]
        
        return cases
    except Exception as e:
        logger.error("❌ Error listing cases: %s", e)
        raise HTTPException(status_code=500, detail="Failed to list cases")

@router.get("/sag/sale-items/all")
async def list_all_sale_items(
    type: Optional[str] = Query(None),
    status: Optional[str] = Query(None),
    q: Optional[str] = Query(None),
    customer_id: Optional[int] = Query(None),
    sag_id: Optional[int] = Query(None),
    date_from: Optional[str] = Query(None),
    date_to: Optional[str] = Query(None),
):
    """List all sale items across cases (orders overview)."""
    try:
        query = """
            SELECT si.*, s.titel AS sag_titel, s.customer_id, c.name AS customer_name
            FROM sag_salgsvarer si
            JOIN sag_sager s ON s.id = si.sag_id
            LEFT JOIN customers c ON c.id = s.customer_id
            WHERE s.deleted_at IS NULL
        """
        params = []

        if type:
            query += " AND LOWER(si.type) = %s"
            params.append(type.lower())
        if status:
            query += " AND LOWER(si.status) = %s"
            params.append(status.lower())
        if q:
            query += " AND (LOWER(si.description) LIKE %s OR LOWER(si.external_ref) LIKE %s OR LOWER(s.titel) LIKE %s OR LOWER(c.name) LIKE %s)"
            like = f"%{q.lower()}%"
            params.extend([like, like, like, like])
        if customer_id:
            query += " AND s.customer_id = %s"
            params.append(customer_id)
        if sag_id:
            query += " AND s.id = %s"
            params.append(sag_id)
        if date_from:
            query += " AND si.line_date >= %s"
            params.append(date_from)
        if date_to:
            query += " AND si.line_date <= %s"
            params.append(date_to)

        query += " ORDER BY si.line_date DESC NULLS LAST, si.id DESC"

        return execute_query(query, tuple(params)) or []
    except Exception as e:
        logger.error("❌ Error listing sale items: %s", e)
        raise HTTPException(status_code=500, detail="Failed to list sale items")

@router.post("/sag")
async def create_sag(data: dict):
    """Create a new case."""
    try:
        if not data.get("titel"):
            raise HTTPException(status_code=400, detail="titel is required")
        if not data.get("customer_id"):
            raise HTTPException(status_code=400, detail="customer_id is required")
        
        status = _normalize_case_status(data.get("status"))
        deadline = _normalize_optional_timestamp(data.get("deadline"), "deadline")
        deferred_until = _normalize_optional_timestamp(data.get("deferred_until"), "deferred_until")
        ansvarlig_bruger_id = _coerce_optional_int(data.get("ansvarlig_bruger_id"), "ansvarlig_bruger_id")
        assigned_group_id = _coerce_optional_int(data.get("assigned_group_id"), "assigned_group_id")

        _validate_user_id(ansvarlig_bruger_id)
        _validate_group_id(assigned_group_id)

        query = """
            INSERT INTO sag_sager 
            (titel, beskrivelse, template_key, status, customer_id, ansvarlig_bruger_id, assigned_group_id, created_by_user_id, deadline, deferred_until, deferred_until_case_id, deferred_until_status)
            VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
            RETURNING *
        """
        params = (
            data.get("titel"),
            data.get("beskrivelse", ""),
            data.get("template_key") or data.get("type", "ticket"),
            status,
            data.get("customer_id"),
            ansvarlig_bruger_id,
            assigned_group_id,
            data.get("created_by_user_id", 1),
            deadline,
            deferred_until,
            data.get("deferred_until_case_id"),
            data.get("deferred_until_status"),
        )
        
        result = execute_query(query, params)
        if result:
            logger.info("✅ Case created: %s", result[0]["id"])
            return result[0]
        raise HTTPException(status_code=500, detail="Failed to create case")
    except Exception as e:
        logger.error("❌ Error creating case: %s", e)
        raise HTTPException(status_code=500, detail="Failed to create case")

@router.get("/sag/{sag_id}")
async def get_sag(sag_id: int):
    """Get a specific case."""
    try:
        query = """
            SELECT s.*, 
                   COALESCE(u.full_name, u.username) AS ansvarlig_navn,
                   g.name AS assigned_group_name
            FROM sag_sager s
            LEFT JOIN users u ON u.user_id = s.ansvarlig_bruger_id
            LEFT JOIN groups g ON g.id = s.assigned_group_id
            WHERE s.id = %s AND s.deleted_at IS NULL
        """
        result = execute_query(query, (sag_id,))
        if not result:
            raise HTTPException(status_code=404, detail="Case not found")
        return result[0]
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error getting case: %s", e)
        raise HTTPException(status_code=500, detail="Failed to get case")


@router.get("/sag/{sag_id}/modules")
async def get_case_module_prefs(sag_id: int):
    """Get module visibility preferences for a case."""
    try:
        query = "SELECT module_key, is_enabled FROM sag_module_prefs WHERE sag_id = %s"
        prefs = execute_query(query, (sag_id,))
        return prefs or []
    except Exception as e:
        logger.error("❌ Error getting module prefs: %s", e)
        raise HTTPException(status_code=500, detail="Failed to get module prefs")


@router.post("/sag/{sag_id}/modules")
async def set_case_module_pref(sag_id: int, data: dict):
    """Set module visibility preference for a case."""
    try:
        module_key = data.get("module_key")
        is_enabled = data.get("is_enabled")

        if not module_key or is_enabled is None:
            raise HTTPException(status_code=400, detail="module_key and is_enabled are required")

        query = """
            INSERT INTO sag_module_prefs (sag_id, module_key, is_enabled)
            VALUES (%s, %s, %s)
            ON CONFLICT (sag_id, module_key)
            DO UPDATE SET is_enabled = EXCLUDED.is_enabled
            RETURNING module_key, is_enabled
        """
        result = execute_query(query, (sag_id, module_key, bool(is_enabled)))
        return result[0] if result else {"module_key": module_key, "is_enabled": bool(is_enabled)}
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error setting module pref: %s", e)
        raise HTTPException(status_code=500, detail="Failed to set module pref")


# =========================================================================
# SAG TODO STEPS
# =========================================================================

@router.get("/sag/{sag_id}/todo-steps", response_model=List[TodoStep])
async def list_todo_steps(sag_id: int):
    try:
        query = """
            SELECT
                t.*, 
                COALESCE(u_created.full_name, u_created.username) AS created_by_name,
                COALESCE(u_completed.full_name, u_completed.username) AS completed_by_name
            FROM sag_todo_steps t
            LEFT JOIN users u_created ON u_created.user_id = t.created_by_user_id
            LEFT JOIN users u_completed ON u_completed.user_id = t.completed_by_user_id
            WHERE t.sag_id = %s AND t.deleted_at IS NULL
            ORDER BY t.is_done ASC, t.due_date NULLS LAST, t.created_at DESC
        """
        return execute_query(query, (sag_id,)) or []
    except Exception as e:
        logger.error("❌ Error listing todo steps: %s", e)
        raise HTTPException(status_code=500, detail="Failed to list todo steps")


@router.post("/sag/{sag_id}/todo-steps", response_model=TodoStep)
async def create_todo_step(sag_id: int, request: Request, data: TodoStepCreate):
    try:
        user_id = _get_user_id_from_request(request)
        if not data.title:
            raise HTTPException(status_code=400, detail="title is required")

        insert_query = """
            INSERT INTO sag_todo_steps
                (sag_id, title, description, due_date, created_by_user_id)
            VALUES (%s, %s, %s, %s, %s)
            RETURNING id
        """
        result = execute_query(insert_query, (
            sag_id,
            data.title,
            data.description,
            data.due_date,
            user_id
        ))
        if not result:
            raise HTTPException(status_code=500, detail="Failed to create todo step")

        step_id = result[0]["id"]
        return execute_query(
            """
            SELECT
                t.*, 
                COALESCE(u_created.full_name, u_created.username) AS created_by_name,
                COALESCE(u_completed.full_name, u_completed.username) AS completed_by_name
            FROM sag_todo_steps t
            LEFT JOIN users u_created ON u_created.user_id = t.created_by_user_id
            LEFT JOIN users u_completed ON u_completed.user_id = t.completed_by_user_id
            WHERE t.id = %s
            """,
            (step_id,)
        )[0]
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error creating todo step: %s", e)
        raise HTTPException(status_code=500, detail="Failed to create todo step")


@router.patch("/sag/todo-steps/{step_id}", response_model=TodoStep)
async def update_todo_step(step_id: int, request: Request, data: TodoStepUpdate):
    try:
        if data.is_done is None:
            raise HTTPException(status_code=400, detail="is_done is required")

        user_id = _get_user_id_from_request(request)
        if data.is_done:
            update_query = """
                UPDATE sag_todo_steps
                SET is_done = TRUE,
                    completed_by_user_id = %s,
                    completed_at = CURRENT_TIMESTAMP
                WHERE id = %s AND deleted_at IS NULL
                RETURNING id
            """
            result = execute_query(update_query, (user_id, step_id))
        else:
            update_query = """
                UPDATE sag_todo_steps
                SET is_done = FALSE,
                    completed_by_user_id = NULL,
                    completed_at = NULL
                WHERE id = %s AND deleted_at IS NULL
                RETURNING id
            """
            result = execute_query(update_query, (step_id,))

        if not result:
            raise HTTPException(status_code=404, detail="Todo step not found")

        return execute_query(
            """
            SELECT
                t.*, 
                COALESCE(u_created.full_name, u_created.username) AS created_by_name,
                COALESCE(u_completed.full_name, u_completed.username) AS completed_by_name
            FROM sag_todo_steps t
            LEFT JOIN users u_created ON u_created.user_id = t.created_by_user_id
            LEFT JOIN users u_completed ON u_completed.user_id = t.completed_by_user_id
            WHERE t.id = %s
            """,
            (step_id,)
        )[0]
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error updating todo step: %s", e)
        raise HTTPException(status_code=500, detail="Failed to update todo step")


@router.delete("/sag/todo-steps/{step_id}")
async def delete_todo_step(step_id: int):
    try:
        result = execute_query(
            """
            UPDATE sag_todo_steps
            SET deleted_at = CURRENT_TIMESTAMP
            WHERE id = %s AND deleted_at IS NULL
            RETURNING id
            """,
            (step_id,)
        )
        if not result:
            raise HTTPException(status_code=404, detail="Todo step not found")
        return {"status": "deleted"}
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error deleting todo step: %s", e)
        raise HTTPException(status_code=500, detail="Failed to delete todo step")

@router.patch("/sag/{sag_id}")
async def update_sag(sag_id: int, updates: dict):
    """Update a case."""
    try:
        # Check if case exists
        check = execute_query("SELECT id FROM sag_sager WHERE id = %s AND deleted_at IS NULL", (sag_id,))
        if not check:
            raise HTTPException(status_code=404, detail="Case not found")
        
        # Backwards compatibility: frontend sends "type", DB stores "template_key"
        if "type" in updates and "template_key" not in updates:
            updates["template_key"] = updates.get("type")

        if "status" in updates:
            updates["status"] = _normalize_case_status(updates.get("status"))
        if "deadline" in updates:
            updates["deadline"] = _normalize_optional_timestamp(updates.get("deadline"), "deadline")
        if "deferred_until" in updates:
            updates["deferred_until"] = _normalize_optional_timestamp(updates.get("deferred_until"), "deferred_until")
        if "ansvarlig_bruger_id" in updates:
            updates["ansvarlig_bruger_id"] = _coerce_optional_int(updates.get("ansvarlig_bruger_id"), "ansvarlig_bruger_id")
            _validate_user_id(updates["ansvarlig_bruger_id"])
        if "assigned_group_id" in updates:
            updates["assigned_group_id"] = _coerce_optional_int(updates.get("assigned_group_id"), "assigned_group_id")
            _validate_group_id(updates["assigned_group_id"])

        # Build dynamic update query
        allowed_fields = [
            "titel",
            "beskrivelse",
            "template_key",
            "status",
            "ansvarlig_bruger_id",
            "assigned_group_id",
            "deadline",
            "deferred_until",
            "deferred_until_case_id",
            "deferred_until_status",
        ]
        set_clauses = []
        params = []
        
        for field in allowed_fields:
            if field in updates:
                set_clauses.append(f"{field} = %s")
                params.append(updates[field])
        
        if not set_clauses:
            raise HTTPException(status_code=400, detail="No valid fields to update")
        
        params.append(sag_id)
        set_sql = ", ".join(set_clauses)
        query = f"UPDATE sag_sager SET {set_sql} WHERE id = %s RETURNING *"
        
        result = execute_query(query, tuple(params))
        if result:
            logger.info("✅ Case updated: %s", sag_id)
            return result[0]
        raise HTTPException(status_code=500, detail="Failed to update case")
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error updating case: %s", e)
        raise HTTPException(status_code=500, detail="Failed to update case")


class PipelineUpdate(BaseModel):
    amount: Optional[float] = None
    probability: Optional[int] = Field(default=None, ge=0, le=100)
    stage_id: Optional[int] = None
    description: Optional[str] = None


@router.patch("/sag/{sag_id}/pipeline")
async def update_sag_pipeline(sag_id: int, pipeline_data: PipelineUpdate):
    """Update pipeline fields for a case."""
    try:
        exists = execute_query(
            "SELECT id FROM sag_sager WHERE id = %s AND deleted_at IS NULL",
            (sag_id,)
        )
        if not exists:
            raise HTTPException(status_code=404, detail="Case not found")

        provided = pipeline_data.model_dump(exclude_unset=True)

        if not provided:
            raise HTTPException(status_code=400, detail="No pipeline fields provided")

        if "stage_id" in provided and provided["stage_id"] is not None:
            stage_exists = execute_query(
                "SELECT id FROM pipeline_stages WHERE id = %s",
                (provided["stage_id"],)
            )
            if not stage_exists:
                raise HTTPException(status_code=400, detail="Invalid pipeline stage")

        set_clauses = []
        params = []

        if "amount" in provided:
            set_clauses.append("pipeline_amount = %s")
            params.append(provided["amount"])

        if "probability" in provided:
            set_clauses.append("pipeline_probability = %s")
            params.append(provided["probability"])

        if "stage_id" in provided:
            set_clauses.append("pipeline_stage_id = %s")
            params.append(provided["stage_id"])

        if "description" in provided:
            set_clauses.append("pipeline_description = %s")
            params.append(provided["description"])

        params.append(sag_id)
        query = f"UPDATE sag_sager SET {', '.join(set_clauses)} WHERE id = %s RETURNING *"
        result = execute_query(query, tuple(params))

        if not result:
            raise HTTPException(status_code=500, detail="Failed to update pipeline")

        logger.info("✅ Pipeline updated for case: %s", sag_id)
        return result[0]
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error updating pipeline for case %s: %s", sag_id, e)
        raise HTTPException(status_code=500, detail="Failed to update pipeline")

@router.delete("/sag/{sag_id}")
async def delete_sag(sag_id: int):
    """Soft-delete a case."""
    try:
        check = execute_query("SELECT id FROM sag_sager WHERE id = %s AND deleted_at IS NULL", (sag_id,))
        if not check:
            raise HTTPException(status_code=404, detail="Case not found")
        
        query = "UPDATE sag_sager SET deleted_at = NOW() WHERE id = %s RETURNING id"
        result = execute_query(query, (sag_id,))
        
        if result:
            logger.info("✅ Case soft-deleted: %s", sag_id)
            return {"status": "deleted", "id": sag_id}
        raise HTTPException(status_code=500, detail="Failed to delete case")
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error deleting case: %s", e)
        raise HTTPException(status_code=500, detail="Failed to delete case")

# ============================================================================
# RELATIONER - Case Relations
# ============================================================================

@router.get("/sag/{sag_id}/relationer")
async def get_relationer(sag_id: int):
    """Get all relations for a case."""
    try:
        # Check if case exists
        check = execute_query("SELECT id FROM sag_sager WHERE id = %s AND deleted_at IS NULL", (sag_id,))
        if not check:
            raise HTTPException(status_code=404, detail="Case not found")
        
        query = """
            SELECT sr.*, 
                   ss_kilde.titel as kilde_titel,
                   ss_mål.titel as mål_titel
            FROM sag_relationer sr
            JOIN sag_sager ss_kilde ON sr.kilde_sag_id = ss_kilde.id
            JOIN sag_sager ss_mål ON sr.målsag_id = ss_mål.id
            WHERE (sr.kilde_sag_id = %s OR sr.målsag_id = %s)
            AND sr.deleted_at IS NULL
            ORDER BY sr.created_at DESC
        """
        result = execute_query(query, (sag_id, sag_id))
        return result
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error getting relations: %s", e)
        raise HTTPException(status_code=500, detail="Failed to get relations")

@router.post("/sag/{sag_id}/relationer")
async def create_relation(sag_id: int, data: dict):
    """Add a relation to another case."""
    try:
        if not data.get("målsag_id") or not data.get("relationstype"):
            raise HTTPException(status_code=400, detail="målsag_id and relationstype required")
        
        målsag_id = data.get("målsag_id")
        relationstype = data.get("relationstype")
        
        # Validate both cases exist
        check1 = execute_query("SELECT id FROM sag_sager WHERE id = %s AND deleted_at IS NULL", (sag_id,))
        check2 = execute_query("SELECT id FROM sag_sager WHERE id = %s AND deleted_at IS NULL", (målsag_id,))
        
        if not check1 or not check2:
            raise HTTPException(status_code=404, detail="One or both cases not found")
        
        query = """
            INSERT INTO sag_relationer (kilde_sag_id, målsag_id, relationstype)
            VALUES (%s, %s, %s)
            RETURNING *
        """
        result = execute_query(query, (sag_id, målsag_id, relationstype))
        
        if result:
            logger.info("✅ Relation created: %s -> %s (%s)", sag_id, målsag_id, relationstype)
            return result[0]
        raise HTTPException(status_code=500, detail="Failed to create relation")
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error creating relation: %s", e)
        raise HTTPException(status_code=500, detail="Failed to create relation")

@router.delete("/sag/{sag_id}/relationer/{relation_id}")
async def delete_relation(sag_id: int, relation_id: int):
    """Soft-delete a relation."""
    try:
        check = execute_query(
            "SELECT id FROM sag_relationer WHERE id = %s AND deleted_at IS NULL AND (kilde_sag_id = %s OR målsag_id = %s)",
            (relation_id, sag_id, sag_id)
        )
        if not check:
            raise HTTPException(status_code=404, detail="Relation not found")
        
        query = "UPDATE sag_relationer SET deleted_at = NOW() WHERE id = %s RETURNING id"
        result = execute_query(query, (relation_id,))
        
        if result:
            logger.info("✅ Relation soft-deleted: %s", relation_id)
            return {"status": "deleted", "id": relation_id}
        raise HTTPException(status_code=500, detail="Failed to delete relation")
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error deleting relation: %s", e)
        raise HTTPException(status_code=500, detail="Failed to delete relation")

# ============================================================================
# TAGS - Case Tags
# ============================================================================

@router.get("/sag/{sag_id}/tags")
async def get_tags(sag_id: int):
    """Get all tags for a case."""
    try:
        check = execute_query("SELECT id FROM sag_sager WHERE id = %s AND deleted_at IS NULL", (sag_id,))
        if not check:
            raise HTTPException(status_code=404, detail="Case not found")
        
        query = "SELECT * FROM sag_tags WHERE sag_id = %s AND deleted_at IS NULL ORDER BY created_at DESC"
        result = execute_query(query, (sag_id,))
        return result
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error getting tags: %s", e)
        raise HTTPException(status_code=500, detail="Failed to get tags")

@router.post("/sag/{sag_id}/tags")
async def add_tag(sag_id: int, data: dict):
    """Add a tag to a case."""
    try:
        if not data.get("tag_navn"):
            raise HTTPException(status_code=400, detail="tag_navn is required")
        
        check = execute_query("SELECT id FROM sag_sager WHERE id = %s AND deleted_at IS NULL", (sag_id,))
        if not check:
            raise HTTPException(status_code=404, detail="Case not found")
        
        query = """
            INSERT INTO sag_tags (sag_id, tag_navn)
            VALUES (%s, %s)
            RETURNING *
        """
        result = execute_query(query, (sag_id, data.get("tag_navn")))
        
        if result:
            logger.info("✅ Tag added: %s -> %s", sag_id, data.get("tag_navn"))
            return result[0]
        raise HTTPException(status_code=500, detail="Failed to add tag")
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error adding tag: %s", e)
        raise HTTPException(status_code=500, detail="Failed to add tag")

@router.delete("/sag/{sag_id}/tags/{tag_id}")
async def delete_tag(sag_id: int, tag_id: int):
    """Soft-delete a tag."""
    try:
        check = execute_query(
            "SELECT id FROM sag_tags WHERE id = %s AND sag_id = %s AND deleted_at IS NULL",
            (tag_id, sag_id)
        )
        if not check:
            raise HTTPException(status_code=404, detail="Tag not found")
        
        query = "UPDATE sag_tags SET deleted_at = NOW() WHERE id = %s RETURNING id"
        result = execute_query(query, (tag_id,))
        
        if result:
            logger.info("✅ Tag soft-deleted: %s", tag_id)
            return {"status": "deleted", "id": tag_id}
        raise HTTPException(status_code=500, detail="Failed to delete tag")
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error deleting tag: %s", e)
        raise HTTPException(status_code=500, detail="Failed to delete tag")


# ============================================================================
# CUSTOMERS - Case Customers (Many-to-Many)
# ============================================================================

@router.get("/sag/{sag_id}/customers")
async def list_case_customers(sag_id: int):
    """List customers associated with a case."""
    try:
        query = """
            SELECT sk.*, c.name as customer_name, c.email as customer_email
            FROM sag_kunder sk
            JOIN customers c ON sk.customer_id = c.id
            WHERE sk.sag_id = %s AND sk.deleted_at IS NULL
        """
        result = execute_query(query, (sag_id,))
        return result
    except Exception as e:
        logger.error("❌ Error listing case customers: %s", e)
        raise HTTPException(status_code=500, detail="Failed to list case customers")

@router.post("/sag/{sag_id}/customers")
async def add_case_customer(sag_id: int, data: dict):
    """Add a customer to a case."""
    try:
        if not data.get("customer_id"):
            raise HTTPException(status_code=400, detail="customer_id is required")
        
        role = data.get("role", "Kunde")
        
        # Check if already exists
        check = execute_query(
            "SELECT id FROM sag_kunder WHERE sag_id = %s AND customer_id = %s AND deleted_at IS NULL",
            (sag_id, data["customer_id"])
        )
        if check:
            return check[0] # Already linked
            
        query = """
            INSERT INTO sag_kunder (sag_id, customer_id, role)
            VALUES (%s, %s, %s)
            RETURNING *
        """
        result = execute_query(query, (sag_id, data["customer_id"], role))
        
        if result:
            logger.info("✅ Customer %s added to case %s", data["customer_id"], sag_id)
            return result[0]
        raise HTTPException(status_code=500, detail="Failed to add case customer")
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error adding case customer: %s", e)
        raise HTTPException(status_code=500, detail="Failed to add case customer")

@router.delete("/sag/{sag_id}/customers/{customer_id}")
async def remove_case_customer(sag_id: int, customer_id: int):
    """Remove customer from case."""
    try:
        query = "UPDATE sag_kunder SET deleted_at = NOW() WHERE sag_id = %s AND customer_id = %s RETURNING id"
        result = execute_query(query, (sag_id, customer_id))
        
        if result:
            logger.info("✅ Customer %s removed from case %s", customer_id, sag_id)
            return {"status": "deleted"}
        raise HTTPException(status_code=404, detail="Customer link not found")
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error removing case customer: %s", e)
        raise HTTPException(status_code=500, detail="Failed to remove case customer")


# ============================================================================
# CONTACTS - Case Contacts
# ============================================================================

@router.get("/sag/{sag_id}/contacts")
async def list_case_contacts(sag_id: int):
    """List contacts associated with a case."""
    try:
        query = """
            SELECT
                sk.*,
                c.first_name,
                c.last_name,
                c.email,
                c.phone,
                c.mobile,
                c.title,
                company.customer_name
            FROM sag_kontakter sk
            JOIN contacts c ON sk.contact_id = c.id
            LEFT JOIN LATERAL (
                SELECT cu.name AS customer_name
                FROM contact_companies cc
                JOIN customers cu ON cu.id = cc.customer_id
                WHERE cc.contact_id = c.id
                ORDER BY cc.is_primary DESC, cu.name
                LIMIT 1
            ) company ON TRUE
            WHERE sk.sag_id = %s AND sk.deleted_at IS NULL
        """
        result = execute_query(query, (sag_id,))
        return result
    except Exception as e:
        logger.error("❌ Error listing case contacts: %s", e)
        raise HTTPException(status_code=500, detail="Failed to list case contacts")

@router.post("/sag/{sag_id}/contacts")
async def add_case_contact(sag_id: int, data: dict):
    """Add a contact to a case."""
    try:
        if not data.get("contact_id"):
            raise HTTPException(status_code=400, detail="contact_id is required")
        
        role = data.get("role", "Kontakt")
        is_primary = bool(data.get("is_primary", False))
        
        # Check if already exists
        check = execute_query(
            "SELECT id FROM sag_kontakter WHERE sag_id = %s AND contact_id = %s AND deleted_at IS NULL",
            (sag_id, data["contact_id"])
        )
        if check:
            return check[0] # Already linked
            
        if is_primary:
            execute_update(
                "UPDATE sag_kontakter SET is_primary = FALSE WHERE sag_id = %s",
                (sag_id,)
            )

        query = """
            INSERT INTO sag_kontakter (sag_id, contact_id, role, is_primary)
            VALUES (%s, %s, %s, %s)
            RETURNING *
        """
        result = execute_query(query, (sag_id, data["contact_id"], role, is_primary))
        
        if result:
            logger.info("✅ Contact added to case %s: %s", sag_id, data["contact_id"])
            return result[0]
        raise HTTPException(status_code=500, detail="Failed to add contact")
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error adding case contact: %s", e)
        raise HTTPException(status_code=500, detail="Failed to add contact")

@router.delete("/sag/{sag_id}/contacts/{contact_id}")
async def remove_case_contact(sag_id: int, contact_id: int):
    """Remove contact from case."""
    try:
        query = "UPDATE sag_kontakter SET deleted_at = NOW() WHERE sag_id = %s AND contact_id = %s RETURNING id"
        result = execute_query(query, (sag_id, contact_id))
        
        if result:
            logger.info("✅ Contact %s removed from case %s", contact_id, sag_id)
            return {"status": "deleted"}
        raise HTTPException(status_code=404, detail="Contact link not found")
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error removing case contact: %s", e)
        raise HTTPException(status_code=500, detail="Failed to remove case contact")


@router.patch("/sag/{sag_id}/contacts/{contact_id}")
async def update_case_contact(sag_id: int, contact_id: int, data: dict):
    """Update role or primary status for a case contact."""
    try:
        existing = execute_query(
            "SELECT id FROM sag_kontakter WHERE sag_id = %s AND contact_id = %s AND deleted_at IS NULL",
            (sag_id, contact_id)
        )
        if not existing:
            raise HTTPException(status_code=404, detail="Contact link not found")

        role = data.get("role")
        is_primary = data.get("is_primary")

        updates = []
        params = []

        if role is not None:
            updates.append("role = %s")
            params.append(role)

        if is_primary is not None:
            if bool(is_primary):
                execute_update(
                    "UPDATE sag_kontakter SET is_primary = FALSE WHERE sag_id = %s",
                    (sag_id,)
                )
            updates.append("is_primary = %s")
            params.append(bool(is_primary))

        if not updates:
            raise HTTPException(status_code=400, detail="No fields to update")

        params.extend([sag_id, contact_id])
        query = f"""
            UPDATE sag_kontakter
            SET {', '.join(updates)}
            WHERE sag_id = %s AND contact_id = %s
            RETURNING *
        """

        result = execute_query(query, tuple(params))
        if result:
            return result[0]
        raise HTTPException(status_code=500, detail="Failed to update contact")
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error updating case contact: %s", e)
        raise HTTPException(status_code=500, detail="Failed to update contact")


# ============================================================================
# HARDWARE - Placeholder endpoints for frontend compatibility
# ============================================================================

@router.get("/sag/{sag_id}/hardware")
async def list_case_hardware(sag_id: int):
    """List hardware associated with a case."""
    try:
        query = """
            SELECT sh.id as relation_id, h.*
            FROM sag_hardware sh
            JOIN hardware_assets h ON sh.hardware_id = h.id
            WHERE sh.sag_id = %s AND sh.deleted_at IS NULL
            ORDER BY sh.created_at DESC
        """
        result = execute_query(query, (sag_id,))
        return result
    except Exception as e:
        logger.error("❌ Error listing case hardware: %s", e)
        raise HTTPException(status_code=500, detail="Failed to list case hardware")

@router.post("/sag/{sag_id}/hardware")
async def add_case_hardware(sag_id: int, data: dict):
    """Add hardware to case."""
    try:
        if not data.get("hardware_id"):
            raise HTTPException(status_code=400, detail="hardware_id is required")
            
        hardware_id = data.get("hardware_id")
        
        # Check if already linked
        check = execute_query(
            "SELECT id FROM sag_hardware WHERE sag_id = %s AND hardware_id = %s AND deleted_at IS NULL",
            (sag_id, hardware_id)
        )
        if check:
            return check[0] # Already linked
            
        query = """
            INSERT INTO sag_hardware (sag_id, hardware_id, note)
            VALUES (%s, %s, %s)
            ON CONFLICT (sag_id, hardware_id) 
            DO UPDATE SET deleted_at = NULL, note = EXCLUDED.note
            RETURNING *
        """
        result = execute_query(query, (sag_id, hardware_id, data.get("note")))
        
        if result:
            logger.info("✅ Hardware %s added to case %s", hardware_id, sag_id)
            return result[0]
        raise HTTPException(status_code=500, detail="Failed to add hardware")
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error adding case hardware: %s", e)
        raise HTTPException(status_code=500, detail="Failed to add hardware")

@router.delete("/sag/{sag_id}/hardware/{hardware_id}")
async def remove_case_hardware(sag_id: int, hardware_id: int):
    """Remove hardware from case."""
    try:
        query = "UPDATE sag_hardware SET deleted_at = NOW() WHERE sag_id = %s AND hardware_id = %s RETURNING id"
        result = execute_query(query, (sag_id, hardware_id))
        
        if result:
            logger.info("✅ Hardware %s removed from case %s", hardware_id, sag_id)
            return {"status": "deleted"}
        raise HTTPException(status_code=404, detail="Hardware link not found")
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error removing case hardware: %s", e)
        raise HTTPException(status_code=500, detail="Failed to remove hardware")


# ============================================================================
# LOCATIONS - Placeholder endpoints for frontend compatibility
# ============================================================================

@router.get("/sag/{sag_id}/locations")
async def list_case_locations(sag_id: int):
    """List locations associated with a case."""
    try:
        query = """
            SELECT sl.id as relation_id, l.*
            FROM sag_lokationer sl
            JOIN locations_locations l ON sl.location_id = l.id
            WHERE sl.sag_id = %s AND sl.deleted_at IS NULL
            ORDER BY sl.created_at DESC
        """
        result = execute_query(query, (sag_id,))
        return result
    except Exception as e:
        logger.error("❌ Error listing case locations: %s", e)
        raise HTTPException(status_code=500, detail="Failed to list case locations")

@router.post("/sag/{sag_id}/locations")
async def add_case_location(sag_id: int, data: dict):
    """Add location to case."""
    try:
        if not data.get("location_id"):
            raise HTTPException(status_code=400, detail="location_id is required")
            
        location_id = data.get("location_id")
        
        # Check if already linked
        check = execute_query(
            "SELECT id FROM sag_lokationer WHERE sag_id = %s AND location_id = %s AND deleted_at IS NULL",
            (sag_id, location_id)
        )
        if check:
            return check[0] # Already linked
            
        query = """
            INSERT INTO sag_lokationer (sag_id, location_id, note)
            VALUES (%s, %s, %s)
            ON CONFLICT (sag_id, location_id) 
            DO UPDATE SET deleted_at = NULL, note = EXCLUDED.note
            RETURNING *
        """
        result = execute_query(query, (sag_id, location_id, data.get("note")))
        
        if result:
            logger.info("✅ Location %s added to case %s", location_id, sag_id)
            return result[0]
        raise HTTPException(status_code=500, detail="Failed to add location")
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error adding case location: %s", e)
        raise HTTPException(status_code=500, detail="Failed to add location")

@router.delete("/sag/{sag_id}/locations/{location_id}")
async def remove_case_location(sag_id: int, location_id: int):
    """Remove location from case."""
    try:
        query = """
            UPDATE sag_lokationer
            SET deleted_at = NOW()
            WHERE sag_id = %s
              AND deleted_at IS NULL
              AND (location_id = %s OR id = %s)
            RETURNING id
        """
        result = execute_query(query, (sag_id, location_id, location_id))
        
        if result:
            logger.info("✅ Location %s removed from case %s", location_id, sag_id)
            return {"status": "deleted"}
        raise HTTPException(status_code=404, detail="Location link not found")
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error removing case location: %s", e)
        raise HTTPException(status_code=500, detail="Failed to remove location")

# ============================================================================
# VAREKØB & SALG - Aggregation (Case Tree)
# ============================================================================

@router.get("/sag/{sag_id}/varekob-salg")
async def get_varekob_salg(sag_id: int, include_subcases: bool = True):
    """Aggregate time entries (and future sales items) for a case tree."""
    try:
        check = execute_query("SELECT id FROM sag_sager WHERE id = %s AND deleted_at IS NULL", (sag_id,))
        if not check:
            raise HTTPException(status_code=404, detail="Case not found")

        if include_subcases:
            case_tree_query = """
                WITH RECURSIVE normalized_relations AS (
                    SELECT
                        CASE
                            WHEN LOWER(relationstype) IN ('afledt af', 'afledt_af') THEN målsag_id
                            WHEN LOWER(relationstype) IN ('årsag til', 'årsag_til') THEN kilde_sag_id
                            ELSE kilde_sag_id
                        END AS parent_id,
                        CASE
                            WHEN LOWER(relationstype) IN ('afledt af', 'afledt_af') THEN kilde_sag_id
                            WHEN LOWER(relationstype) IN ('årsag til', 'årsag_til') THEN målsag_id
                            ELSE målsag_id
                        END AS child_id
                    FROM sag_relationer
                    WHERE deleted_at IS NULL
                ),
                case_tree AS (
                    SELECT id FROM sag_sager WHERE id = %s AND deleted_at IS NULL
                    UNION
                    SELECT nr.child_id
                    FROM normalized_relations nr
                    JOIN case_tree ct ON nr.parent_id = ct.id
                )
                SELECT s.id, s.titel
                FROM sag_sager s
                JOIN case_tree ct ON s.id = ct.id
                WHERE s.deleted_at IS NULL
                ORDER BY s.id
            """
            case_tree = execute_query(case_tree_query, (sag_id,))

            time_query = """
                WITH RECURSIVE normalized_relations AS (
                    SELECT
                        CASE
                            WHEN LOWER(relationstype) IN ('afledt af', 'afledt_af') THEN målsag_id
                            WHEN LOWER(relationstype) IN ('årsag til', 'årsag_til') THEN kilde_sag_id
                            ELSE kilde_sag_id
                        END AS parent_id,
                        CASE
                            WHEN LOWER(relationstype) IN ('afledt af', 'afledt_af') THEN kilde_sag_id
                            WHEN LOWER(relationstype) IN ('årsag til', 'årsag_til') THEN målsag_id
                            ELSE målsag_id
                        END AS child_id
                    FROM sag_relationer
                    WHERE deleted_at IS NULL
                ),
                case_tree AS (
                    SELECT id FROM sag_sager WHERE id = %s AND deleted_at IS NULL
                    UNION
                    SELECT nr.child_id
                    FROM normalized_relations nr
                    JOIN case_tree ct ON nr.parent_id = ct.id
                )
                SELECT t.*, s.titel AS source_sag_titel
                FROM tmodule_times t
                JOIN case_tree ct ON t.sag_id = ct.id
                LEFT JOIN sag_sager s ON s.id = t.sag_id
                ORDER BY t.worked_date DESC NULLS LAST, t.id DESC
            """
            time_entries = execute_query(time_query, (sag_id,))

            sale_items_query = """
                WITH RECURSIVE normalized_relations AS (
                    SELECT
                        CASE
                            WHEN LOWER(relationstype) IN ('afledt af', 'afledt_af') THEN målsag_id
                            WHEN LOWER(relationstype) IN ('årsag til', 'årsag_til') THEN kilde_sag_id
                            ELSE kilde_sag_id
                        END AS parent_id,
                        CASE
                            WHEN LOWER(relationstype) IN ('afledt af', 'afledt_af') THEN kilde_sag_id
                            WHEN LOWER(relationstype) IN ('årsag til', 'årsag_til') THEN målsag_id
                            ELSE målsag_id
                        END AS child_id
                    FROM sag_relationer
                    WHERE deleted_at IS NULL
                ),
                case_tree AS (
                    SELECT id FROM sag_sager WHERE id = %s AND deleted_at IS NULL
                    UNION
                    SELECT nr.child_id
                    FROM normalized_relations nr
                    JOIN case_tree ct ON nr.parent_id = ct.id
                )
                SELECT si.*, s.titel AS source_sag_titel
                FROM sag_salgsvarer si
                JOIN case_tree ct ON si.sag_id = ct.id
                LEFT JOIN sag_sager s ON s.id = si.sag_id
                ORDER BY si.line_date DESC NULLS LAST, si.id DESC
            """
            sale_items = execute_query(sale_items_query, (sag_id,))
        else:
            case_tree = execute_query(
                "SELECT id, titel FROM sag_sager WHERE id = %s AND deleted_at IS NULL",
                (sag_id,)
            )
            time_query = """
                SELECT t.*, s.titel AS source_sag_titel
                FROM tmodule_times t
                LEFT JOIN sag_sager s ON s.id = t.sag_id
                WHERE t.sag_id = %s
                ORDER BY t.worked_date DESC NULLS LAST, t.id DESC
            """
            time_entries = execute_query(time_query, (sag_id,))

            sale_items_query = """
                SELECT si.*, s.titel AS source_sag_titel
                FROM sag_salgsvarer si
                LEFT JOIN sag_sager s ON s.id = si.sag_id
                WHERE si.sag_id = %s
                ORDER BY si.line_date DESC NULLS LAST, si.id DESC
            """
            sale_items = execute_query(sale_items_query, (sag_id,))

        total_entries = len(time_entries or [])
        total_hours = 0
        billable_hours = 0

        sale_total = 0
        purchase_total = 0

        for entry in time_entries or []:
            hours = entry.get("approved_hours") or entry.get("original_hours") or 0
            total_hours += float(hours)
            if entry.get("billable"):
                billable_hours += float(hours)

        for item in sale_items or []:
            amount = item.get("amount") or 0
            if (item.get("type") or "").lower() == "purchase":
                purchase_total += float(amount)
            else:
                sale_total += float(amount)

        return {
            "sag_id": sag_id,
            "include_subcases": include_subcases,
            "case_tree": case_tree or [],
            "time_entries": time_entries or [],
            "sale_items": sale_items or [],
            "totals": {
                "total_entries": total_entries,
                "total_hours": round(total_hours, 2),
                "billable_hours": round(billable_hours, 2),
                "sale_total": round(sale_total, 2),
                "purchase_total": round(purchase_total, 2),
                "net_total": round(sale_total - purchase_total, 2)
            }
        }
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error aggregating case data: %s", e)
        raise HTTPException(status_code=500, detail="Failed to aggregate case data")


@router.get("/sag/{sag_id}/calendar-events")
async def get_case_calendar_events(sag_id: int, include_children: bool = True):
    """Return calendar events for a case and optionally its child cases."""
    try:
        check = execute_query("SELECT id FROM sag_sager WHERE id = %s AND deleted_at IS NULL", (sag_id,))
        if not check:
            raise HTTPException(status_code=404, detail="Case not found")

        if include_children:
            case_tree_query = """
                WITH RECURSIVE normalized_relations AS (
                    SELECT
                        CASE
                            WHEN LOWER(relationstype) IN ('afledt af', 'afledt_af') THEN målsag_id
                            WHEN LOWER(relationstype) IN ('årsag til', 'årsag_til') THEN kilde_sag_id
                            ELSE kilde_sag_id
                        END AS parent_id,
                        CASE
                            WHEN LOWER(relationstype) IN ('afledt af', 'afledt_af') THEN kilde_sag_id
                            WHEN LOWER(relationstype) IN ('årsag til', 'årsag_til') THEN målsag_id
                            ELSE målsag_id
                        END AS child_id
                    FROM sag_relationer
                    WHERE deleted_at IS NULL
                ),
                case_tree AS (
                    SELECT id FROM sag_sager WHERE id = %s AND deleted_at IS NULL
                    UNION
                    SELECT nr.child_id
                    FROM normalized_relations nr
                    JOIN case_tree ct ON nr.parent_id = ct.id
                )
                SELECT s.id, s.titel
                FROM sag_sager s
                JOIN case_tree ct ON s.id = ct.id
                WHERE s.deleted_at IS NULL
                ORDER BY s.id
            """
            case_rows = execute_query(case_tree_query, (sag_id,)) or []
        else:
            case_rows = execute_query(
                "SELECT id, titel FROM sag_sager WHERE id = %s AND deleted_at IS NULL",
                (sag_id,)
            ) or []

        case_ids = [row["id"] for row in case_rows]
        case_titles = {row["id"]: row.get("titel") for row in case_rows}

        if not case_ids:
            return {"current": [], "children": []}

        placeholders = ",".join(["%s"] * len(case_ids))

        reminder_query = f"""
            SELECT r.id, r.sag_id, r.title, r.message, r.event_type, r.priority,
                   r.next_check_at, r.scheduled_at
            FROM sag_reminders r
            WHERE r.deleted_at IS NULL
              AND r.is_active = true
              AND r.sag_id IN ({placeholders})
        """
        reminders = execute_query(reminder_query, tuple(case_ids)) or []

        case_query = f"""
            SELECT id, titel, deadline, deferred_until
            FROM sag_sager
            WHERE deleted_at IS NULL
              AND id IN ({placeholders})
        """
        case_dates = execute_query(case_query, tuple(case_ids)) or []

        events_by_case: dict[int, list] = {cid: [] for cid in case_ids}

        for row in reminders:
            start_value = row.get("next_check_at") or row.get("scheduled_at")
            if not start_value:
                continue
            events_by_case[row["sag_id"]].append({
                "id": f"reminder:{row['id']}",
                "title": row.get("title"),
                "message": row.get("message"),
                "event_kind": row.get("event_type") or "reminder",
                "start": start_value.isoformat(),
                "url": f"/sag/{row['sag_id']}"
            })

        for row in case_dates:
            if row.get("deadline"):
                events_by_case[row["id"]].append({
                    "id": f"deadline:{row['id']}",
                    "title": f"Deadline: {row.get('titel')}",
                    "event_kind": "deadline",
                    "start": row["deadline"].isoformat(),
                    "url": f"/sag/{row['id']}"
                })
            if row.get("deferred_until"):
                events_by_case[row["id"]].append({
                    "id": f"deferred:{row['id']}",
                    "title": f"Deferred: {row.get('titel')}",
                    "event_kind": "deferred",
                    "start": row["deferred_until"].isoformat(),
                    "url": f"/sag/{row['id']}"
                })

        current_events = events_by_case.get(sag_id, [])
        children = []
        for cid in case_ids:
            if cid == sag_id:
                continue
            children.append({
                "case_id": cid,
                "case_title": case_titles.get(cid) or f"Sag #{cid}",
                "events": events_by_case.get(cid, [])
            })

        return {"current": current_events, "children": children}
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error loading case calendar events: %s", e)
        raise HTTPException(status_code=500, detail="Failed to load calendar events")

# ============================================================================
# VAREKØB & SALG - CRUD (Case-linked sale items)
# ============================================================================

@router.get("/sag/{sag_id}/sale-items")
async def list_sale_items(sag_id: int):
    """List sale items for a case."""
    try:
        check = execute_query("SELECT id FROM sag_sager WHERE id = %s AND deleted_at IS NULL", (sag_id,))
        if not check:
            raise HTTPException(status_code=404, detail="Case not found")

        query = """
            SELECT si.*, s.titel AS source_sag_titel
            FROM sag_salgsvarer si
            LEFT JOIN sag_sager s ON s.id = si.sag_id
            WHERE si.sag_id = %s
            ORDER BY si.line_date DESC NULLS LAST, si.id DESC
        """
        return execute_query(query, (sag_id,)) or []
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error listing sale items: %s", e)
        raise HTTPException(status_code=500, detail="Failed to list sale items")


@router.post("/sag/{sag_id}/sale-items")
async def create_sale_item(sag_id: int, data: dict):
    """Create a sale item for a case."""
    try:
        check = execute_query("SELECT id FROM sag_sager WHERE id = %s AND deleted_at IS NULL", (sag_id,))
        if not check:
            raise HTTPException(status_code=404, detail="Case not found")

        description = data.get("description")
        amount = data.get("amount")
        if not description:
            raise HTTPException(status_code=400, detail="description is required")
        if amount is None:
            raise HTTPException(status_code=400, detail="amount is required")

        item_type = (data.get("type") or "sale").lower()
        if item_type not in ("sale", "purchase"):
            raise HTTPException(status_code=400, detail="type must be sale or purchase")

        status = (data.get("status") or "draft").lower()
        if status not in ("draft", "confirmed", "cancelled"):
            raise HTTPException(status_code=400, detail="status must be draft, confirmed, or cancelled")

        query = """
            INSERT INTO sag_salgsvarer
                (sag_id, type, description, quantity, unit, unit_price, amount, currency, status, line_date, external_ref, product_id)
            VALUES
                (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
            RETURNING *
        """
        params = (
            sag_id,
            item_type,
            description,
            data.get("quantity"),
            data.get("unit"),
            data.get("unit_price"),
            amount,
            data.get("currency", "DKK"),
            status,
            data.get("line_date"),
            data.get("external_ref"),
            data.get("product_id"),
        )
        result = execute_query(query, params)
        if result:
            logger.info("✅ Sale item created for case %s", sag_id)
            return result[0]
        raise HTTPException(status_code=500, detail="Failed to create sale item")
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error creating sale item: %s", e)
        raise HTTPException(status_code=500, detail="Failed to create sale item")


@router.get("/sag/{sag_id}/sale-items/{item_id}")
async def get_sale_item(sag_id: int, item_id: int):
    """Get a single sale item for a case."""
    try:
        query = """
            SELECT si.*, s.titel AS source_sag_titel
            FROM sag_salgsvarer si
            LEFT JOIN sag_sager s ON s.id = si.sag_id
            WHERE si.sag_id = %s AND si.id = %s
        """
        result = execute_query(query, (sag_id, item_id))
        if not result:
            raise HTTPException(status_code=404, detail="Sale item not found")
        return result[0]
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error getting sale item: %s", e)
        raise HTTPException(status_code=500, detail="Failed to get sale item")


@router.patch("/sag/{sag_id}/sale-items/{item_id}")
async def update_sale_item(sag_id: int, item_id: int, updates: dict):
    """Update a sale item for a case."""
    try:
        check = execute_query(
            "SELECT id FROM sag_salgsvarer WHERE id = %s AND sag_id = %s",
            (item_id, sag_id)
        )
        if not check:
            raise HTTPException(status_code=404, detail="Sale item not found")

        allowed_fields = [
            "type",
            "description",
            "quantity",
            "unit",
            "unit_price",
            "amount",
            "currency",
            "status",
            "line_date",
            "external_ref",
            "product_id",
        ]

        set_clauses = []
        params = []

        for field in allowed_fields:
            if field in updates:
                if field == "type":
                    value = (updates[field] or "").lower()
                    if value not in ("sale", "purchase"):
                        raise HTTPException(status_code=400, detail="type must be sale or purchase")
                    set_clauses.append("type = %s")
                    params.append(value)
                elif field == "status":
                    value = (updates[field] or "").lower()
                    if value not in ("draft", "confirmed", "cancelled"):
                        raise HTTPException(status_code=400, detail="status must be draft, confirmed, or cancelled")
                    set_clauses.append("status = %s")
                    params.append(value)
                else:
                    set_clauses.append(f"{field} = %s")
                    params.append(updates[field])

        if not set_clauses:
            raise HTTPException(status_code=400, detail="No valid fields to update")

        params.extend([item_id, sag_id])
        query = f"UPDATE sag_salgsvarer SET {', '.join(set_clauses)} WHERE id = %s AND sag_id = %s RETURNING *"
        result = execute_query(query, tuple(params))
        if result:
            logger.info("✅ Sale item updated: %s", item_id)
            return result[0]
        raise HTTPException(status_code=500, detail="Failed to update sale item")
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error updating sale item: %s", e)
        raise HTTPException(status_code=500, detail="Failed to update sale item")


@router.delete("/sag/{sag_id}/sale-items/{item_id}")
async def delete_sale_item(sag_id: int, item_id: int):
    """Delete a sale item for a case."""
    try:
        query = "DELETE FROM sag_salgsvarer WHERE id = %s AND sag_id = %s RETURNING id"
        result = execute_query(query, (item_id, sag_id))
        if result:
            logger.info("✅ Sale item deleted: %s", item_id)
            return {"status": "deleted", "id": item_id}
        raise HTTPException(status_code=404, detail="Sale item not found")
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error deleting sale item: %s", e)
        raise HTTPException(status_code=500, detail="Failed to delete sale item")

# ============================================================================
# KOMMENTARER - Case Comments
# ============================================================================

@router.get("/sag/{sag_id}/kommentarer")
async def get_kommentarer(sag_id: int):
    """Get all comments for a case."""
    try:
        check = execute_query("SELECT id FROM sag_sager WHERE id = %s AND deleted_at IS NULL", (sag_id,))
        if not check:
            raise HTTPException(status_code=404, detail="Case not found")
        
        query = "SELECT * FROM sag_kommentarer WHERE sag_id = %s AND deleted_at IS NULL ORDER BY created_at ASC"
        result = execute_query(query, (sag_id,))
        return result
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error getting comments: %s", e)
        raise HTTPException(status_code=500, detail="Failed to get comments")

@router.post("/sag/{sag_id}/kommentarer")
async def add_kommentar(sag_id: int, data: dict):
    """Add a comment to a case."""
    try:
        if not data.get("indhold"):
            raise HTTPException(status_code=400, detail="indhold is required")
        
        check = execute_query("SELECT id FROM sag_sager WHERE id = %s AND deleted_at IS NULL", (sag_id,))
        if not check:
            raise HTTPException(status_code=404, detail="Case not found")
        
        # Default author to current user or provided in body (if system)
        # simplistic auth for now
        forfatter = data.get("forfatter", "Bruger") 
        er_system_besked = data.get("er_system_besked", False)
        
        query = """
            INSERT INTO sag_kommentarer (sag_id, forfatter, indhold, er_system_besked)
            VALUES (%s, %s, %s, %s)
            RETURNING *
        """
        result = execute_query(query, (sag_id, forfatter, data.get("indhold"), er_system_besked))
        
        if result:
            logger.info("✅ Comment added to case %s by %s", sag_id, forfatter)
            return result[0]
        raise HTTPException(status_code=500, detail="Failed to add comment")
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error adding comment: %s", e)
        raise HTTPException(status_code=500, detail="Failed to add comment")


# ============================================================================
# FILES - Case Files
# ============================================================================

UPLOAD_BASE_PATH = Path(settings.UPLOAD_DIR).resolve()
SAG_FILE_SUBDIR = "sag_files"
(UPLOAD_BASE_PATH / SAG_FILE_SUBDIR).mkdir(parents=True, exist_ok=True)
ALLOWED_EXTENSIONS = {ext.lower() for ext in settings.ALLOWED_EXTENSIONS}
MAX_ATTACHMENT_SIZE = settings.EMAIL_MAX_UPLOAD_SIZE_MB * 1024 * 1024

def _generate_stored_name(filename: str, subdir: str) -> str:
    cleaned = Path(filename).name
    unique = f"{uuid4().hex}_{cleaned}"
    return f"{subdir}/{unique}"

def _resolve_attachment_path(stored_name: str) -> Path:
    return UPLOAD_BASE_PATH / stored_name

def _store_upload_file(upload_file: UploadFile, subdir: str):
    if not upload_file.filename:
        raise HTTPException(400, detail="Filename missing")
    
    ext = Path(upload_file.filename).suffix.lower().lstrip(".")
    # Basic check - allow more types for generic files?
    # if ext not in ALLOWED_EXTENSIONS: ...
    
    upload_file.file.seek(0, os.SEEK_END)
    size = upload_file.file.tell()
    upload_file.file.seek(0)
    
    if size > MAX_ATTACHMENT_SIZE:
         raise HTTPException(400, detail=f"File too large (> {settings.EMAIL_MAX_UPLOAD_SIZE_MB}MB)")

    stored_name = _generate_stored_name(upload_file.filename, subdir)
    destination = _resolve_attachment_path(stored_name)
    destination.parent.mkdir(parents=True, exist_ok=True)
    
    try:
        with destination.open("wb") as buffer:
            shutil.copyfileobj(upload_file.file, buffer)
    except Exception as e:
        logger.error(f"Upload failed: {e}")
        raise HTTPException(500, detail="Server upload failed")
        
    return stored_name, size

@router.get("/sag/{sag_id}/files")
async def list_sag_files(sag_id: int):
    """List files attached to a case."""
    try:
        query = """
            SELECT * FROM sag_files 
            WHERE sag_id = %s 
            ORDER BY created_at DESC
        """
        files = execute_query(query, (sag_id,))
        # Add download URL
        if files:
            for f in files:
                f["download_url"] = f"/api/v1/sag/{sag_id}/files/{f['id']}"
        return files or []
    except Exception as e:
        logger.error("❌ Error listing files: %s", e)
        raise HTTPException(status_code=500, detail="Failed to list files")

@router.post("/sag/{sag_id}/files")
async def upload_sag_files(sag_id: int, files: List[UploadFile] = File(...)):
    """Upload files to a case."""
    check = execute_query("SELECT id FROM sag_sager WHERE id = %s AND deleted_at IS NULL", (sag_id,))
    if not check:
        raise HTTPException(status_code=404, detail="Case not found")

    saved_files = []
    
    for file in files:
        try:
            stored_name, size = _store_upload_file(file, SAG_FILE_SUBDIR)
            
            query = """
                INSERT INTO sag_files (sag_id, filename, content_type, size_bytes, stored_name)
                VALUES (%s, %s, %s, %s, %s)
                RETURNING id, filename, created_at
            """
            result = execute_query(query, (sag_id, file.filename, file.content_type, size, stored_name))
            if result:
                saved = result[0]
                saved["download_url"] = f"/api/v1/sag/{sag_id}/files/{saved['id']}"
                saved_files.append(saved)
        except HTTPException:
            continue # Skip invalid
        except Exception as e:
            logger.error(f"Error saving file {file.filename}: {e}")
            continue

    return saved_files

@router.get("/sag/{sag_id}/files/{file_id}")
async def download_sag_file(sag_id: int, file_id: int, download: bool = False):
    """Download or preview a specific file.
    
    Args:
        download: If True, force download. If False (default), display inline in browser.
    """
    query = "SELECT * FROM sag_files WHERE id = %s AND sag_id = %s"
    result = execute_query(query, (file_id, sag_id))
    
    if not result:
        raise HTTPException(status_code=404, detail="File not found")
    
    file_data = result[0]
    path = _resolve_attachment_path(file_data["stored_name"])
    
    if not path.exists():
        raise HTTPException(status_code=404, detail="File lost on server")
    
    # Determine content disposition
    headers = {}
    if download:
        headers["Content-Disposition"] = f'attachment; filename="{file_data["filename"]}"'
    else:
        headers["Content-Disposition"] = f'inline; filename="{file_data["filename"]}"'
        
    return FileResponse(
        path=path, 
        filename=file_data["filename"],
        media_type=file_data.get("content_type", "application/octet-stream"),
        headers=headers
    )

@router.delete("/sag/{sag_id}/files/{file_id}")
async def delete_sag_file(sag_id: int, file_id: int):
    """Delete a file."""
    query = "DELETE FROM sag_files WHERE id = %s AND sag_id = %s RETURNING stored_name"
    result = execute_query(query, (file_id, sag_id))
    
    if result:
        # Clean up disk
        path = _resolve_attachment_path(result[0]["stored_name"])
        if path.exists():
            try:
                os.remove(path)
            except: 
                pass
        return {"status": "deleted"}
    raise HTTPException(status_code=404, detail="File not found")

# ============================================================================
# EMAILS - Case Emails (Linked)
# ============================================================================

@router.post("/sag/{sag_id}/email-links")
async def add_sag_email_link(sag_id: int, payload: dict):
    """Link an existing email to a case."""
    email_id = payload.get("email_id")
    if not email_id:
        raise HTTPException(status_code=400, detail="email_id required")
        
    query = """
        INSERT INTO sag_emails (sag_id, email_id)
        VALUES (%s, %s)
        ON CONFLICT DO NOTHING
        RETURNING *
    """
    execute_query(query, (sag_id, email_id))
    return {"status": "linked"}

@router.get("/sag/{sag_id}/email-links")
async def get_sag_emails(sag_id: int):
    """Get emails linked to a case."""
    query = """
        SELECT e.* 
        FROM email_messages e
        JOIN sag_emails se ON e.id = se.email_id
        WHERE se.sag_id = %s
        ORDER BY e.received_date DESC
    """
    return execute_query(query, (sag_id,)) or []

@router.delete("/sag/{sag_id}/email-links/{email_id}")
async def remove_sag_email_link(sag_id: int, email_id: int):
    """Unlink an email."""
    query = "DELETE FROM sag_emails WHERE sag_id = %s AND email_id = %s"
    execute_query(query, (sag_id, email_id))
    return {"status": "unlinked"}

def _decode_header_str(header_val):
    if not header_val: return ""
    try:
        decoded_list = decode_header(header_val)
        result = ""
        for content, encoding in decoded_list:
            if isinstance(content, bytes):
                if encoding:
                    try:
                        result += content.decode(encoding)
                    except:
                        result += content.decode('utf-8', errors='ignore')
                else:
                    result += content.decode('utf-8', errors='ignore')
            else:
                result += str(content)
        return result
    except:
        return str(header_val)

@router.post("/sag/{sag_id}/upload-email")
async def upload_sag_email(sag_id: int, file: UploadFile = File(...)):
    """Upload .eml/.msg, parse it, save to email_messages, and link to case."""
    content = await file.read()
    filename = file.filename.lower()
    email_data = {}
    temp_id = uuid4().hex
    
    # 1. Parse File
    if filename.endswith('.msg'):
         if not extract_msg: raise HTTPException(500, "extract-msg missing")
         import io
         msg = extract_msg.Message(io.BytesIO(content))
         email_data = {
             'message_id': msg.messageId or f"msg-{temp_id}",
             'subject': msg.subject or "No Subject",
             'sender_email': msg.sender or "",
             'sender_name': msg.sender or "",
             'recipient_email': msg.to or "",
             'cc': msg.cc or "",
             'body_text': msg.body,
             'body_html': msg.htmlBody,
             'received_date': msg.date or datetime.now(),
             'folder': 'Imported',
             'attachments': [],
             'has_attachments': False,
             'attachment_count': 0
         }
    elif filename.endswith('.eml'):
        msg = email.message_from_bytes(content)
        body_text = ""
        if msg.is_multipart():
            for part in msg.walk():
                if part.get_content_type() == "text/plain":
                    body_text = part.get_payload(decode=True).decode('utf-8', errors='ignore')
                    break
        else:
            body_text = msg.get_payload(decode=True).decode('utf-8', errors='ignore')
            
        email_data = {
            'message_id': msg.get('Message-ID', f"eml-{temp_id}"),
            'subject': _decode_header_str(msg.get('Subject', 'No Subject')),
            'sender_email': _decode_header_str(msg.get('From', '')),
            'sender_name': _decode_header_str(msg.get('From', '')),
            'recipient_email': _decode_header_str(msg.get('To', '')),
            'cc': _decode_header_str(msg.get('Cc', '')),
            'received_date': datetime.now(),
            'body_text': body_text,
            'body_html': "",
            'folder': 'Imported',
            'has_attachments': False,
            'attachment_count': 0,
            'attachments': []
        }
    else:
        raise HTTPException(400, "Only .msg or .eml files allowed")

    # 2. Save Email via Service (deduplicate)
    svc = EmailService()
    existing = execute_query_single("SELECT id FROM email_messages WHERE message_id = %s", (email_data['message_id'],))
    
    if existing:
        email_id = existing['id']
    else:
        # We try to strict save, keeping it simple
        email_id = await svc.save_email(email_data)

    # 3. Link
    await add_sag_email_link(sag_id, {"email_id": email_id})
    return {"status": "imported", "email_id": email_id}

# ============================================================================
# SOLUTIONS
# ============================================================================
from . import solutions
router.include_router(solutions.router)
