"""
Ticket Module API Router
=========================

REST API endpoints for ticket system.
"""

import logging
import hashlib
import json
import re
import asyncio
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query, status
from fastapi.responses import JSONResponse

from app.ticket.backend.ticket_service import TicketService
from app.services.simplycrm_service import SimplyCRMService
from app.services.vtiger_service import get_vtiger_service
from app.core.config import settings
from app.ticket.backend.economic_export import ticket_economic_service
from app.ticket.backend.models import (
    TTicket,
    TTicketCreate,
    TTicketUpdate,
    TTicketWithStats,
    TTicketComment,
    TTicketCommentCreate,
    TTicketWorklog,
    TTicketWorklogCreate,
    TTicketWorklogUpdate,
    TTicketWorklogWithDetails,
    TicketListResponse,
    TicketStatusUpdateRequest,
    WorklogReviewResponse,
    WorklogBillingRequest,
    # Migration 026 models
    TTicketRelation,
    TTicketRelationCreate,
    TTicketCalendarEvent,
    TTicketCalendarEventCreate,
    CalendarEventStatus,
    TTicketTemplate,
    TTicketTemplateCreate,
    TemplateRenderRequest,
    TemplateRenderResponse,
    TTicketAISuggestion,
    TTicketAISuggestionCreate,
    AISuggestionStatus,
    AISuggestionType,
    AISuggestionReviewRequest,
    TTicketAuditLog,
    TicketMergeRequest,
    TicketSplitRequest,
    TicketDeadlineUpdateRequest
)
from app.core.database import execute_query, execute_insert, execute_update, execute_query_single
from app.core.auth_dependencies import require_any_permission
from datetime import date, datetime

logger = logging.getLogger(__name__)

router = APIRouter()
sync_admin_access = require_any_permission("users.manage", "system.admin")


def _get_first_value(data: dict, keys: List[str]) -> Optional[str]:
    for key in keys:
        value = data.get(key)
        if value not in (None, ""):
            return value
    return None


def _parse_datetime(value: Optional[str]) -> Optional[datetime]:
    if not value:
        return None
    if isinstance(value, datetime):
        return value
    if isinstance(value, date):
        return datetime.combine(value, datetime.min.time())

    value_str = str(value).strip()
    for fmt in ("%Y-%m-%d %H:%M:%S", "%Y-%m-%d"):
        try:
            return datetime.strptime(value_str, fmt)
        except ValueError:
            continue
    try:
        return datetime.fromisoformat(value_str.replace("Z", "+00:00"))
    except ValueError:
        return None


def _parse_hours(value: Optional[str]) -> Optional[float]:
    if value in (None, ""):
        return None
    if isinstance(value, (int, float)):
        return float(value)

    value_str = str(value).strip()
    if ":" in value_str:
        parts = value_str.split(":")
        if len(parts) == 2:
            try:
                hours = float(parts[0])
                minutes = float(parts[1])
                return hours + minutes / 60.0
            except ValueError:
                return None
    try:
        return float(value_str)
    except ValueError:
        return None


def _looks_like_external_id(value: Optional[str]) -> bool:
    if not value:
        return False
    return bool(re.match(r"^\d+x\d+$", str(value)))


def _calculate_hash(data: dict) -> str:
    payload = json.dumps(data, sort_keys=True, default=str).encode("utf-8")
    return hashlib.sha256(payload).hexdigest()


def _escape_simply_value(value: str) -> str:
    return value.replace("'", "''")


def _extract_count_value(rows: List[dict]) -> Optional[int]:
    if not rows:
        return None

    row = rows[0] or {}
    if not isinstance(row, dict):
        return None

    for key in ("total_count", "count", "count(*)", "COUNT(*)"):
        value = row.get(key)
        if value is not None:
            try:
                return int(value)
            except (TypeError, ValueError):
                continue

    for value in row.values():
        try:
            return int(value)
        except (TypeError, ValueError):
            continue

    return None


async def _vtiger_query_with_retry(vtiger, query_string: str, retries: int = 5, base_delay: float = 1.25) -> List[dict]:
    """Run vTiger query with exponential backoff on rate-limit responses."""
    for attempt in range(retries + 1):
        await asyncio.sleep(0.15)
        result = await vtiger.query(query_string)
        status_code = getattr(vtiger, "last_query_status", None)
        error = getattr(vtiger, "last_query_error", None) or {}
        error_code = error.get("code") if isinstance(error, dict) else None

        if status_code != 429 and error_code != "TOO_MANY_REQUESTS":
            return result

        if attempt < retries:
            await asyncio.sleep(base_delay * (2 ** attempt))

    return []


# ============================================================================
# TICKET ENDPOINTS
# ============================================================================

@router.get("/tickets", response_model=TicketListResponse, tags=["Tickets"])
async def list_tickets(
    status: Optional[str] = Query(None, description="Filter by status"),
    priority: Optional[str] = Query(None, description="Filter by priority"),
    customer_id: Optional[int] = Query(None, description="Filter by customer"),
    assigned_to_user_id: Optional[int] = Query(None, description="Filter by assigned user"),
    search: Optional[str] = Query(None, description="Search in subject/description"),
    limit: int = Query(50, ge=1, le=100, description="Number of results"),
    offset: int = Query(0, ge=0, description="Offset for pagination")
):
    """
    List tickets with optional filters
    
    - **status**: Filter by ticket status
    - **priority**: Filter by priority level
    - **customer_id**: Show tickets for specific customer
    - **assigned_to_user_id**: Show tickets assigned to user
    - **search**: Search in subject and description
    """
    try:
        tickets = TicketService.list_tickets(
            status=status,
            priority=priority,
            customer_id=customer_id,
            assigned_to_user_id=assigned_to_user_id,
            search=search,
            limit=limit,
            offset=offset
        )
        
        # Get total count for pagination
        total_query = "SELECT COUNT(*) as count FROM tticket_tickets WHERE 1=1"
        params = []
        
        if status:
            total_query += " AND status = %s"
            params.append(status)
        if customer_id:
            total_query += " AND customer_id = %s"
            params.append(customer_id)
        
        total_result = execute_query_single(total_query, tuple(params))
        total = total_result['count'] if total_result else 0
        
        return TicketListResponse(
            tickets=tickets,
            total=total,
            page=offset // limit + 1,
            page_size=limit
        )
        
    except Exception as e:
        logger.error(f"❌ Error listing tickets: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/tickets/{ticket_id}", response_model=TTicketWithStats, tags=["Tickets"])
async def get_ticket(ticket_id: int):
    """
    Get single ticket with statistics
    
    Returns ticket with comment count, worklog hours, etc.
    """
    try:
        ticket = TicketService.get_ticket_with_stats(ticket_id)
        
        if not ticket:
            raise HTTPException(status_code=404, detail=f"Ticket {ticket_id} not found")
        
        return ticket
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"❌ Error getting ticket {ticket_id}: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/tickets", response_model=TTicket, status_code=status.HTTP_201_CREATED, tags=["Tickets"])
async def create_ticket(
    ticket_data: TTicketCreate,
    user_id: Optional[int] = Query(None, description="User creating ticket")
):
    """
    Create new ticket
    
    Ticket number will be auto-generated if not provided.
    """
    try:
        ticket = TicketService.create_ticket(ticket_data, user_id=user_id)
        logger.info(f"✅ Created ticket {ticket['ticket_number']}")
        return ticket
        
    except Exception as e:
        logger.error(f"❌ Error creating ticket: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.patch("/tickets/{ticket_id}", response_model=TTicket, tags=["Tickets"])
async def update_ticket(
    ticket_id: int,
    update_data: TTicketUpdate,
    user_id: Optional[int] = Query(None, description="User making update")
):
    """
    Update ticket (partial update)
    
    Only provided fields will be updated.
    """
    try:
        ticket = TicketService.update_ticket(ticket_id, update_data, user_id=user_id)
        return ticket
        
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))
    except Exception as e:
        logger.error(f"❌ Error updating ticket {ticket_id}: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.put("/tickets/{ticket_id}/status", response_model=TTicket, tags=["Tickets"])
async def update_ticket_status(
    ticket_id: int,
    request: TicketStatusUpdateRequest,
    user_id: Optional[int] = Query(None, description="User changing status")
):
    """
    Update ticket status with validation
    
    Status transitions are validated according to workflow rules.
    """
    try:
        ticket = TicketService.update_ticket_status(
            ticket_id,
            request.status.value,
            user_id=user_id,
            note=request.note
        )
        return ticket
        
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error(f"❌ Error updating status for ticket {ticket_id}: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.put("/tickets/{ticket_id}/assign", response_model=TTicket, tags=["Tickets"])
async def assign_ticket(
    ticket_id: int,
    assigned_to_user_id: int = Query(..., description="User to assign to"),
    user_id: Optional[int] = Query(None, description="User making assignment")
):
    """
    Assign ticket to a user
    """
    try:
        ticket = TicketService.assign_ticket(ticket_id, assigned_to_user_id, user_id=user_id)
        return ticket
        
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))
    except Exception as e:
        logger.error(f"❌ Error assigning ticket {ticket_id}: {e}")
        raise HTTPException(status_code=500, detail=str(e))


# ============================================================================
# COMMENT ENDPOINTS
# ============================================================================

@router.get("/tickets/{ticket_id}/comments", response_model=List[TTicketComment], tags=["Comments"])
async def list_comments(ticket_id: int):
    """
    List all comments for a ticket
    """
    try:
        comments = execute_query_single(
            "SELECT * FROM tticket_comments WHERE ticket_id = %s ORDER BY created_at ASC",
            (ticket_id,)
        )
        return comments or []
        
    except Exception as e:
        logger.error(f"❌ Error listing comments for ticket {ticket_id}: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/tickets/{ticket_id}/comments", response_model=TTicketComment, status_code=status.HTTP_201_CREATED, tags=["Comments"])
async def add_comment(
    ticket_id: int,
    comment_text: str = Query(..., min_length=1, description="Comment text"),
    is_internal: bool = Query(False, description="Is internal note"),
    user_id: Optional[int] = Query(None, description="User adding comment")
):
    """
    Add comment to ticket
    
    - **is_internal**: If true, comment is only visible to staff
    """
    try:
        comment = TicketService.add_comment(
            ticket_id,
            comment_text,
            user_id=user_id,
            is_internal=is_internal
        )
        return comment
        
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))
    except Exception as e:
        logger.error(f"❌ Error adding comment to ticket {ticket_id}: {e}")
        raise HTTPException(status_code=500, detail=str(e))


# ============================================================================
# TICKET CONTACTS ENDPOINTS
# ============================================================================

@router.get("/tickets/{ticket_id}/contacts", tags=["Contacts"])
async def get_ticket_contacts(ticket_id: int):
    """Get all contacts for a ticket with their roles"""
    try:
        query = """
            SELECT 
                tc.id,
                tc.contact_id,
                c.first_name,
                c.last_name,
                c.email,
                c.phone,
                c.mobile,
                c.title,
                tc.role,
                tc.added_at,
                tc.notes
            FROM tticket_contacts tc
            JOIN contacts c ON tc.contact_id = c.id
            WHERE tc.ticket_id = %s
            ORDER BY 
                CASE tc.role
                    WHEN 'primary' THEN 1
                    WHEN 'requester' THEN 2
                    WHEN 'assignee' THEN 3
                    WHEN 'cc' THEN 4
                    WHEN 'observer' THEN 5
                    ELSE 6
                END,
                tc.added_at
        """
        contacts = execute_query(query, (ticket_id,))
        return {"contacts": contacts}
    except Exception as e:
        logger.error(f"❌ Error fetching contacts for ticket {ticket_id}: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/tickets/{ticket_id}/contacts", status_code=status.HTTP_201_CREATED, tags=["Contacts"])
async def add_ticket_contact(
    ticket_id: int,
    contact_id: int = Query(..., description="Contact ID to add"),
    role: str = Query("observer", description="Role: primary, cc, observer, assignee, requester, eller custom (ekstern_it, third_party, electrician, etc.)"),
    notes: Optional[str] = Query(None, description="Optional notes about this contact's role"),
    user_id: Optional[int] = Query(None, description="User adding the contact")
):
    """
    Add a contact to a ticket with a specific role
    
    Standard Roles:
    - **primary**: Main contact person
    - **requester**: Original person who requested the ticket
    - **assignee**: Person assigned to work on it  
    - **cc**: Should be kept in the loop (carbon copy)
    - **observer**: Passively following the ticket
    
    Custom Roles (eksempler):
    - **ekstern_it**: Ekstern IT konsulent
    - **third_party**: 3. parts leverandør
    - **electrician**: Elektriker
    - **consultant**: Konsulent
    - Eller hvilken som helst custom rolle
    """
    try:
        # Normalize role (lowercase, underscores)
        role = role.lower().replace(' ', '_').replace('-', '_')
        
        # Check if ticket exists
        ticket_check = execute_query_single("SELECT id FROM tticket_tickets WHERE id = %s", (ticket_id,))
        if not ticket_check:
            raise HTTPException(status_code=404, detail="Ticket not found")
        
        # Check if contact exists
        contact_check = execute_query_single("SELECT id, first_name, last_name FROM contacts WHERE id = %s", (contact_id,))
        if not contact_check:
            raise HTTPException(status_code=404, detail="Contact not found")
        
        # Check if this is the first contact - if so, force role to 'primary'
        existing_contacts = execute_query("SELECT COUNT(*) as count FROM tticket_contacts WHERE ticket_id = %s", (ticket_id,))
        if existing_contacts and existing_contacts[0]['count'] == 0:
            role = 'primary'
            logger.info(f"✨ First contact on ticket {ticket_id} - auto-setting role to 'primary'")
        
        # Insert (will fail if duplicate due to UNIQUE constraint)
        query = """
            INSERT INTO tticket_contacts (ticket_id, contact_id, role, notes, added_by_user_id)
            VALUES (%s, %s, %s, %s, %s)
            RETURNING id
        """
        result = execute_insert(query, (ticket_id, contact_id, role, notes, user_id))
        
        logger.info(f"✅ Added contact {contact_id} ({contact_check['first_name']} {contact_check['last_name']}) to ticket {ticket_id} as {role}")
        
        return {
            "id": result,
            "ticket_id": ticket_id,
            "contact_id": contact_id,
            "role": role,
            "contact_name": f"{contact_check['first_name']} {contact_check['last_name']}"
        }
        
    except Exception as e:
        if "duplicate key" in str(e).lower():
            raise HTTPException(status_code=400, detail="Contact already added to this ticket")
        logger.error(f"❌ Error adding contact to ticket: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.put("/tickets/{ticket_id}/contacts/{contact_id}", tags=["Contacts"])
async def update_ticket_contact_role(
    ticket_id: int,
    contact_id: int,
    role: str = Query(..., description="New role (standard eller custom)"),
    notes: Optional[str] = Query(None, description="Updated notes")
):
    """Update a contact's role on a ticket. Accepts both standard and custom roles."""
    try:
        # Normalize role
        role = role.lower().replace(' ', '_').replace('-', '_')
        
        query = """
            UPDATE tticket_contacts 
            SET role = %s, notes = %s
            WHERE ticket_id = %s AND contact_id = %s
            RETURNING id
        """
        result = execute_update(query, (role, notes, ticket_id, contact_id))
        
        if not result:
            raise HTTPException(status_code=404, detail="Contact not found on this ticket")
        
        logger.info(f"✅ Updated contact {contact_id} role to {role} on ticket {ticket_id}")
        return {"success": True, "role": role}
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"❌ Error updating contact role: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.delete("/tickets/{ticket_id}/contacts/{contact_id}", tags=["Contacts"])
async def remove_ticket_contact(ticket_id: int, contact_id: int):
    """Remove a contact from a ticket"""
    try:
        query = "DELETE FROM tticket_contacts WHERE ticket_id = %s AND contact_id = %s RETURNING id"
        result = execute_query_single(query, (ticket_id, contact_id))
        
        if not result:
            raise HTTPException(status_code=404, detail="Contact not found on this ticket")
        
        logger.info(f"✅ Removed contact {contact_id} from ticket {ticket_id}")
        return {"success": True, "removed": True}
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"❌ Error removing contact: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/contacts/roles", tags=["Contacts"])
async def get_contact_roles():
    """Get all used contact roles with usage statistics"""
    try:
        query = "SELECT * FROM vw_ticket_contact_roles"
        roles = execute_query(query)
        
        # Add standard roles with 0 count if not used yet
        standard_roles = [
            {'role': 'primary', 'label': '⭐ Primær kontakt', 'category': 'standard'},
            {'role': 'requester', 'label': '📝 Anmoder', 'category': 'standard'},
            {'role': 'assignee', 'label': '👤 Ansvarlig', 'category': 'standard'},
            {'role': 'cc', 'label': '📧 CC', 'category': 'standard'},
            {'role': 'observer', 'label': '👁 Observer', 'category': 'standard'},
            {'role': 'ekstern_it', 'label': '💻 Ekstern IT', 'category': 'common'},
            {'role': 'third_party', 'label': '🤝 3. part', 'category': 'common'},
            {'role': 'electrician', 'label': '⚡ Elektriker', 'category': 'common'},
            {'role': 'consultant', 'label': '🎓 Konsulent', 'category': 'common'},
            {'role': 'vendor', 'label': '🏢 Leverandør', 'category': 'common'},
        ]
        
        # Merge with used roles
        used_role_names = {r['role'] for r in roles}
        all_roles = standard_roles.copy()
        
        # Add custom roles that are actually in use
        for role in roles:
            if role['role'] not in {r['role'] for r in standard_roles}:
                all_roles.append({
                    'role': role['role'],
                    'label': role['role'].replace('_', ' ').title(),
                    'category': 'custom',
                    'usage_count': role['usage_count'],
                    'tickets_count': role['tickets_count']
                })
        
        return {"roles": all_roles}
        
    except Exception as e:
        logger.error(f"❌ Error fetching contact roles: {e}")
        raise HTTPException(status_code=500, detail=str(e))


# ============================================================================
# WORKLOG ENDPOINTS
# ============================================================================

@router.get("/tickets/{ticket_id}/worklog", response_model=List[TTicketWorklog], tags=["Worklog"])
async def list_worklog(ticket_id: int):
    """
    List all worklog entries for a ticket
    """
    try:
        worklog = execute_query(
            "SELECT * FROM tticket_worklog WHERE ticket_id = %s ORDER BY work_date DESC",
            (ticket_id,)
        )
        return worklog or []
        
    except Exception as e:
        logger.error(f"❌ Error listing worklog for ticket {ticket_id}: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/tickets/{ticket_id}/worklog", response_model=TTicketWorklog, status_code=status.HTTP_201_CREATED, tags=["Worklog"])
async def create_worklog(
    ticket_id: int,
    worklog_data: TTicketWorklogCreate,
    user_id: Optional[int] = Query(None, description="User creating worklog")
):
    """
    Create worklog entry for ticket
    
    Creates time entry in draft status.
    If billing_method is 'prepaid_card', validates and auto-selects card when only 1 active.
    """
    try:
        from psycopg2.extras import Json
        
        # Handle prepaid card selection/validation
        prepaid_card_id = worklog_data.prepaid_card_id
        if worklog_data.billing_method.value == 'prepaid_card':
            # Get customer_id from ticket
            ticket = execute_query_single(
                "SELECT customer_id FROM tticket_tickets WHERE id = %s",
                (ticket_id,))
            if not ticket:
                raise HTTPException(status_code=404, detail="Ticket not found")
            
            customer_id = ticket['customer_id']
            
            # Get active prepaid cards for customer
            active_cards = execute_query(
                """SELECT id, remaining_hours, expires_at 
                   FROM tticket_prepaid_cards 
                   WHERE customer_id = %s AND status = 'active'
                   ORDER BY expires_at ASC NULLS LAST, created_at ASC""",
                (customer_id,))
            
            if not active_cards:
                raise HTTPException(
                    status_code=400, 
                    detail="Kunden har ingen aktive klippekort")
            
            if len(active_cards) == 1:
                # Auto-select if only 1 active
                if not prepaid_card_id:
                    prepaid_card_id = active_cards[0]['id']
                    logger.info(f"🎫 Auto-selected prepaid card {prepaid_card_id} (only active card)")
            else:
                # Multiple active cards: require explicit selection
                if not prepaid_card_id:
                    raise HTTPException(
                        status_code=400,
                        detail=f"Kunden har {len(active_cards)} aktive klippekort. Vælg et konkret kort.")
            
            # Validate selected card is active and belongs to customer
            selected = next((c for c in active_cards if c['id'] == prepaid_card_id), None)
            if not selected:
                raise HTTPException(
                    status_code=400,
                    detail="Valgt klippekort er ikke aktivt eller tilhører ikke kunden")
        
        # Calculate rounded hours if prepaid
        rounded_hours = None
        if prepaid_card_id:
            card = execute_query_single(
                "SELECT rounding_minutes FROM tticket_prepaid_cards WHERE id = %s",
                (prepaid_card_id,)
            )
            if card:
                rounding_minutes = int(card.get('rounding_minutes') or 0)
                if rounding_minutes > 0:
                    from decimal import Decimal, ROUND_CEILING
                    interval = Decimal(rounding_minutes) / Decimal(60)
                    rounded_hours = float(
                        (Decimal(str(worklog_data.hours)) / interval).to_integral_value(rounding=ROUND_CEILING) * interval
                    )
                else:
                    rounded_hours = float(worklog_data.hours)
        
        worklog_id = execute_insert(
            """
            INSERT INTO tticket_worklog 
            (ticket_id, work_date, hours, work_type, description, billing_method, status, user_id, prepaid_card_id, is_internal, rounded_hours)
            VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
            RETURNING id
            """,
            (
                ticket_id,
                worklog_data.work_date,
                worklog_data.hours,
                worklog_data.work_type.value,
                worklog_data.description,
                worklog_data.billing_method.value,
                'draft',
                user_id or worklog_data.user_id,
                prepaid_card_id,
                worklog_data.is_internal,
                rounded_hours
            )
        )
        
        # Log audit
        TicketService.log_audit(
            ticket_id=ticket_id,
            entity_type="worklog",
            entity_id=worklog_id,
            user_id=user_id,
            action="created",
            details={
                "hours": float(worklog_data.hours), 
                "work_type": worklog_data.work_type.value,
                "is_internal": worklog_data.is_internal
            }
        )
        
        worklog = execute_query_single(
            "SELECT * FROM tticket_worklog WHERE id = %s",
            (worklog_id,))
        
        logger.info(f"✅ Created worklog entry {worklog_id} for ticket {ticket_id}")
        return worklog
        
    except Exception as e:
        logger.error(f"❌ Error creating worklog: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.patch("/worklog/{worklog_id}", response_model=TTicketWorklog, tags=["Worklog"])
async def update_worklog(
    worklog_id: int,
    update_data: TTicketWorklogUpdate,
    user_id: Optional[int] = Query(None, description="User updating worklog")
):
    """
    Update worklog entry (partial update)
    
    Only draft entries can be fully edited.
    If billing_method changes to 'prepaid_card', validates and auto-selects card when only 1 active.
    """
    try:
        # Get current worklog
        current = execute_query_single(
            "SELECT w.*, t.customer_id FROM tticket_worklog w JOIN tticket_tickets t ON w.ticket_id = t.id WHERE w.id = %s",
            (worklog_id,))
        
        if not current:
            raise HTTPException(status_code=404, detail=f"Worklog {worklog_id} not found")
        
        # Build update query
        updates = []
        params = []
        
        update_dict = update_data.model_dump(exclude_unset=True)
        
        # Handle prepaid card selection/validation if billing_method is being set to prepaid_card
        if 'billing_method' in update_dict and update_dict['billing_method'] == 'prepaid_card':
            customer_id = current['customer_id']
            
            # Get active prepaid cards for customer
            active_cards = execute_query(
                """SELECT id, remaining_hours, expires_at 
                   FROM tticket_prepaid_cards 
                   WHERE customer_id = %s AND status = 'active'
                   ORDER BY expires_at ASC NULLS LAST, created_at ASC""",
                (customer_id,))
            
            if not active_cards:
                raise HTTPException(
                    status_code=400, 
                    detail="Kunden har ingen aktive klippekort")
            
            if len(active_cards) == 1:
                # Auto-select if only 1 active and not explicitly provided
                if 'prepaid_card_id' not in update_dict or not update_dict['prepaid_card_id']:
                    update_dict['prepaid_card_id'] = active_cards[0]['id']
                    logger.info(f"🎫 Auto-selected prepaid card {update_dict['prepaid_card_id']} (only active card)")
            else:
                # Multiple active cards: require explicit selection
                if 'prepaid_card_id' not in update_dict or not update_dict['prepaid_card_id']:
                    raise HTTPException(
                        status_code=400,
                        detail=f"Kunden har {len(active_cards)} aktive klippekort. Vælg et konkret kort.")
            
            # Validate selected card if provided
            if 'prepaid_card_id' in update_dict and update_dict['prepaid_card_id']:
                selected = next((c for c in active_cards if c['id'] == update_dict['prepaid_card_id']), None)
                if not selected:
                    raise HTTPException(
                        status_code=400,
                        detail="Valgt klippekort er ikke aktivt eller tilhører ikke kunden")
        
        for field, value in update_dict.items():
            if hasattr(value, 'value'):
                value = value.value
            updates.append(f"{field} = %s")
            params.append(value)
        
        if updates:
            params.append(worklog_id)
            query = f"UPDATE tticket_worklog SET {', '.join(updates)} WHERE id = %s"
            execute_update(query, tuple(params))
            
            # Log audit
            TicketService.log_audit(
                ticket_id=current['ticket_id'],
                entity_type="worklog",
                entity_id=worklog_id,
                user_id=user_id,
                action="updated",
                details=update_dict
            )
        
        # Fetch updated
        worklog = execute_query_single(
            "SELECT * FROM tticket_worklog WHERE id = %s",
            (worklog_id,))
        
        return worklog
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"❌ Error updating worklog {worklog_id}: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/worklog/review", response_model=WorklogReviewResponse, tags=["Worklog"])
async def review_worklog(
    customer_id: Optional[int] = Query(None, description="Filter by customer"),
    status: str = Query("draft", description="Filter by status (default: draft)")
):
    """
    Get worklog entries for review/billing
    
    Returns entries ready for review with ticket context.
    """
    try:
        from decimal import Decimal
        
        query = """
            SELECT w.*, t.ticket_number, t.subject AS ticket_subject, 
                   t.customer_id, t.status AS ticket_status
            FROM tticket_worklog w
            JOIN tticket_tickets t ON w.ticket_id = t.id
            WHERE w.status = %s
        """
        params = [status]
        
        if customer_id:
            query += " AND t.customer_id = %s"
            params.append(customer_id)
        
        query += " ORDER BY w.work_date DESC, t.customer_id"
        
        worklogs = execute_query_single(query, tuple(params))
        
        # Calculate totals
        total_hours = Decimal('0')
        total_billable_hours = Decimal('0')
        
        for w in worklogs or []:
            total_hours += Decimal(str(w['hours']))
            if w['status'] in ['draft', 'billable']:
                total_billable_hours += Decimal(str(w['hours']))
        
        return WorklogReviewResponse(
            worklogs=worklogs or [],
            total=len(worklogs) if worklogs else 0,
            total_hours=total_hours,
            total_billable_hours=total_billable_hours
        )
        
    except Exception as e:
        logger.error(f"❌ Error getting worklog review: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/worklog/mark-billable", tags=["Worklog"])
async def mark_worklog_billable(
    request: WorklogBillingRequest,
    user_id: Optional[int] = Query(None, description="User marking as billable")
):
    """
    Mark worklog entries as billable
    
    Changes status from draft to billable for selected entries.
    """
    try:
        updated_count = 0
        
        for worklog_id in request.worklog_ids:
            # Get worklog
            worklog = execute_query(
                "SELECT * FROM tticket_worklog WHERE id = %s",
                (worklog_id,))
            
            if not worklog:
                logger.warning(f"⚠️ Worklog {worklog_id} not found, skipping")
                continue
            
            if worklog['status'] != 'draft':
                logger.warning(f"⚠️ Worklog {worklog_id} not in draft status, skipping")
                continue
            
            # Update to billable
            execute_update(
                "UPDATE tticket_worklog SET status = 'billable' WHERE id = %s",
                (worklog_id,)
            )
            
            # Log audit
            TicketService.log_audit(
                ticket_id=worklog['ticket_id'],
                entity_type="worklog",
                entity_id=worklog_id,
                user_id=user_id,
                action="marked_billable",
                old_value="draft",
                new_value="billable",
                details={"note": request.note} if request.note else None
            )
            
            updated_count += 1
        
        logger.info(f"✅ Marked {updated_count} worklog entries as billable")
        
        return JSONResponse(
            content={
                "success": True,
                "updated_count": updated_count,
                "message": f"Marked {updated_count} entries as billable"
            }
        )
        
    except Exception as e:
        logger.error(f"❌ Error marking worklog as billable: {e}")
        raise HTTPException(status_code=500, detail=str(e))


# ============================================================================
# PREPAID CARD (KLIPPEKORT) ENDPOINTS
# ============================================================================

from app.ticket.backend.klippekort_service import KlippekortService
from app.ticket.backend.models import (
    TPrepaidCard,
    TPrepaidCardCreate,
    TPrepaidCardUpdate,
    TPrepaidCardWithStats,
    TPrepaidTransaction,
    PrepaidCardBalanceResponse,
    PrepaidCardTopUpRequest
)


@router.get("/prepaid-cards", response_model=List[TPrepaidCard], tags=["Prepaid Cards"])
async def list_prepaid_cards(
    customer_id: Optional[int] = Query(None, description="Filter by customer"),
    status: Optional[str] = Query(None, description="Filter by status"),
    limit: int = Query(50, ge=1, le=100),
    offset: int = Query(0, ge=0)
):
    """
    List prepaid cards with optional filters
    """
    try:
        cards = KlippekortService.list_cards(
            customer_id=customer_id,
            status=status,
            limit=limit,
            offset=offset
        )
        return cards
        
    except Exception as e:
        logger.error(f"❌ Error listing prepaid cards: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/prepaid-cards/{card_id}", response_model=TPrepaidCardWithStats, tags=["Prepaid Cards"])
async def get_prepaid_card(card_id: int):
    """
    Get prepaid card with usage statistics
    """
    try:
        card = KlippekortService.get_card_with_stats(card_id)
        
        if not card:
            raise HTTPException(status_code=404, detail=f"Prepaid card {card_id} not found")
        
        return card
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"❌ Error getting prepaid card {card_id}: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/prepaid-cards", response_model=TPrepaidCard, status_code=status.HTTP_201_CREATED, tags=["Prepaid Cards"])
async def purchase_prepaid_card(
    card_data: TPrepaidCardCreate,
    user_id: Optional[int] = Query(None, description="User purchasing card")
):
    """
    Purchase new prepaid card
    
    CONSTRAINT: Only 1 active card allowed per customer.
    Will fail if customer already has an active card.
    """
    try:
        card = KlippekortService.purchase_card(card_data, user_id=user_id)
        logger.info(f"✅ Purchased prepaid card {card['card_number']}")
        return card
        
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error(f"❌ Error purchasing prepaid card: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/prepaid-cards/customer/{customer_id}/balance", response_model=PrepaidCardBalanceResponse, tags=["Prepaid Cards"])
async def check_customer_balance(customer_id: int):
    """
    Check prepaid card balance for customer
    
    Returns balance info for customer's active card.
    """
    try:
        balance_info = KlippekortService.check_balance(customer_id)
        
        if not balance_info['has_card']:
            return PrepaidCardBalanceResponse(
                card=None,
                can_deduct=False,
                message=f"Customer {customer_id} has no active prepaid card"
            )
        
        # Get card details
        card = KlippekortService.get_card_with_stats(balance_info['card_id'])
        
        return PrepaidCardBalanceResponse(
            card=card,
            can_deduct=balance_info['balance_hours'] > 0,
            message=f"Balance: {balance_info['balance_hours']}h"
        )
        
    except Exception as e:
        logger.error(f"❌ Error checking balance for customer {customer_id}: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/prepaid-cards/{card_id}/top-up", response_model=TPrepaidTransaction, tags=["Prepaid Cards"])
async def top_up_prepaid_card(
    card_id: int,
    request: PrepaidCardTopUpRequest,
    user_id: Optional[int] = Query(None, description="User performing top-up")
):
    """
    Top up prepaid card with additional hours
    """
    try:
        transaction = KlippekortService.top_up_card(
            card_id,
            request.hours,
            user_id=user_id,
            note=request.note
        )
        return transaction
        
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error(f"❌ Error topping up card {card_id}: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/prepaid-cards/{card_id}/transactions", response_model=List[TPrepaidTransaction], tags=["Prepaid Cards"])
async def get_card_transactions(
    card_id: int,
    limit: int = Query(100, ge=1, le=500)
):
    """
    Get transaction history for prepaid card
    """
    try:
        transactions = KlippekortService.get_transactions(card_id, limit=limit)
        return transactions
        
    except Exception as e:
        logger.error(f"❌ Error getting transactions for card {card_id}: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.delete("/prepaid-cards/{card_id}", response_model=TPrepaidCard, tags=["Prepaid Cards"])
async def cancel_prepaid_card(
    card_id: int,
    reason: Optional[str] = Query(None, description="Cancellation reason"),
    user_id: Optional[int] = Query(None, description="User cancelling card")
):
    """
    Cancel/deactivate prepaid card
    """
    try:
        card = KlippekortService.cancel_card(card_id, user_id=user_id, reason=reason)
        return card
        
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error(f"❌ Error cancelling card {card_id}: {e}")
        raise HTTPException(status_code=500, detail=str(e))


# ============================================================================
# STATISTICS ENDPOINTS
# ============================================================================

@router.get("/tickets/stats/by-status", tags=["Statistics"])
async def get_stats_by_status():
    """
    Get ticket statistics grouped by status
    """
    try:
        stats = execute_query_single(
            "SELECT * FROM tticket_stats_by_status ORDER BY status"
        )
        return stats or []
        
    except Exception as e:
        logger.error(f"❌ Error getting stats: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/tickets/stats/open", tags=["Statistics"])
async def get_open_tickets_stats():
    """
    Get statistics for open tickets
    """
    try:
        stats = execute_query(
            """
            SELECT 
                COUNT(*) as total_open,
                COUNT(*) FILTER (WHERE status = 'open') as new_tickets,
                COUNT(*) FILTER (WHERE status = 'in_progress') as in_progress,
                COUNT(*) FILTER (WHERE priority = 'urgent') as urgent_count,
                AVG(age_hours) as avg_age_hours
            FROM tticket_open_tickets
            """)
        
        return stats or {}
        
    except Exception as e:
        logger.error(f"❌ Error getting open tickets stats: {e}")
        raise HTTPException(status_code=500, detail=str(e))


# ============================================================================
# E-CONOMIC EXPORT ENDPOINTS
# ============================================================================

@router.post("/worklog/export/preview", tags=["E-conomic Export"])
async def preview_economic_export(
    customer_id: int = Query(..., description="Customer ID"),
    worklog_ids: Optional[List[int]] = Query(None, description="Specific worklog IDs to export"),
    date_from: Optional[date] = Query(None, description="Start date filter"),
    date_to: Optional[date] = Query(None, description="End date filter")
):
    """
    Preview what would be exported to e-conomic without actually exporting
    
    **Safety**: This is read-only and safe to call
    """
    try:
        preview = await ticket_economic_service.get_export_preview(
            customer_id=customer_id,
            worklog_ids=worklog_ids,
            date_from=date_from,
            date_to=date_to
        )
        
        return preview
        
    except Exception as e:
        logger.error(f"❌ Error generating export preview: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/worklog/export/execute", tags=["E-conomic Export"])
async def execute_economic_export(
    customer_id: int = Query(..., description="Customer ID"),
    worklog_ids: Optional[List[int]] = Query(None, description="Specific worklog IDs to export"),
    date_from: Optional[date] = Query(None, description="Start date filter"),
    date_to: Optional[date] = Query(None, description="End date filter")
):
    """
    Export billable worklog entries to e-conomic as draft invoice
    
    **⚠️ WARNING**: This creates invoices in e-conomic (subject to safety switches)
    
    **Safety Switches**:
    - `TICKET_ECONOMIC_READ_ONLY=true`: Blocks execution
    - `TICKET_ECONOMIC_DRY_RUN=true`: Logs but doesn't execute
    - Both must be `false` to actually export
    
    **Process**:
    1. Validates customer has e-conomic mapping
    2. Collects billable worklog entries
    3. Creates draft invoice in e-conomic
    4. Marks worklog entries as "billed"
    """
    try:
        result = await ticket_economic_service.export_billable_worklog_batch(
            customer_id=customer_id,
            worklog_ids=worklog_ids,
            date_from=date_from,
            date_to=date_to
        )
        
        if result['status'] == 'blocked':
            return JSONResponse(
                status_code=403,
                content={
                    'error': 'Export blocked by safety switches',
                    'read_only': result.get('read_only'),
                    'dry_run': result.get('dry_run'),
                    'message': 'Set TICKET_ECONOMIC_READ_ONLY=false and TICKET_ECONOMIC_DRY_RUN=false to enable'
                }
            )
        
        return result
        
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error(f"❌ Error executing export: {e}")
        raise HTTPException(status_code=500, detail=str(e))


# ============================================================================
# TICKET RELATIONS ENDPOINTS (Migration 026)
# ============================================================================

@router.post("/tickets/{ticket_id}/merge", tags=["Ticket Relations"])
async def merge_tickets(ticket_id: int, request: TicketMergeRequest):
    """
    Flet flere tickets sammen til én primær ticket
    
    **Process**:
    1. Validerer at alle source tickets eksisterer
    2. Kopierer kommentarer og worklogs til target ticket
    3. Opretter relation records
    4. Markerer source tickets som merged
    5. Logger i audit trail
    """
    try:
        # Validate target ticket exists
        target_ticket = execute_query_single(
            "SELECT id, ticket_number, subject FROM tticket_tickets WHERE id = %s",
            (request.target_ticket_id,)
        )
        if not target_ticket:
            raise HTTPException(status_code=404, detail=f"Target ticket {request.target_ticket_id} not found")
        
        merged_count = 0
        for source_id in request.source_ticket_ids:
            # Validate source ticket
            source_ticket = execute_query_single(
                "SELECT id, ticket_number FROM tticket_tickets WHERE id = %s",
                (source_id,)
            )
            if not source_ticket:
                logger.warning(f"⚠️ Source ticket {source_id} not found, skipping")
                continue
            
            # Create relation
            execute_query(
                """INSERT INTO tticket_relations (ticket_id, related_ticket_id, relation_type, reason, created_by_user_id)
                   VALUES (%s, %s, 'merged_into', %s, 1)
                   ON CONFLICT (ticket_id, related_ticket_id, relation_type) DO NOTHING""",
                (source_id, request.target_ticket_id, request.reason)
            )
            
            # Mark source as merged
            execute_query(
                """UPDATE tticket_tickets 
                   SET is_merged = true, merged_into_ticket_id = %s, status = 'closed'
                   WHERE id = %s""",
                (request.target_ticket_id, source_id),
                fetch=False
            )
            
            # Log audit
            execute_query(
                """INSERT INTO tticket_audit_log (ticket_id, action, new_value, reason)
                   VALUES (%s, 'merged_into', %s, %s)""",
                (source_id, str(request.target_ticket_id), request.reason)
            )
            
            merged_count += 1
            logger.info(f"✅ Merged ticket {source_id} into {request.target_ticket_id}")
        
        return {
            "status": "success",
            "merged_count": merged_count,
            "target_ticket": target_ticket,
            "message": f"Successfully merged {merged_count} ticket(s)"
        }
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"❌ Error merging tickets: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/tickets/{ticket_id}/split", tags=["Ticket Relations"])
async def split_ticket(ticket_id: int, request: TicketSplitRequest):
    """
    Opdel en ticket i to - flyt kommentarer til ny ticket
    
    **Process**:
    1. Opretter ny ticket med nyt subject
    2. Flytter valgte kommentarer til ny ticket
    3. Opretter relation
    4. Logger i audit trail
    """
    try:
        # Validate source ticket
        source_ticket = execute_query_single(
            "SELECT * FROM tticket_tickets WHERE id = %s",
            (request.source_ticket_id,)
        )
        if not source_ticket:
            raise HTTPException(status_code=404, detail=f"Source ticket {request.source_ticket_id} not found")
        
        # Create new ticket (inherit customer, contact, priority)
        new_ticket_id = execute_insert(
            """INSERT INTO tticket_tickets 
               (subject, description, status, priority, customer_id, contact_id, source, created_by_user_id)
               VALUES (%s, %s, 'open', %s, %s, %s, 'manual', 1)
               RETURNING id""",
            (request.new_subject, request.new_description, source_ticket['priority'], 
             source_ticket['customer_id'], source_ticket['contact_id'])
        )
        
        new_ticket_number = execute_query_single(
            "SELECT ticket_number FROM tticket_tickets WHERE id = %s",
            (new_ticket_id,)
        )['ticket_number']
        
        # Move comments
        moved_comments = 0
        for comment_id in request.comment_ids:
            result = execute_query(
                "UPDATE tticket_comments SET ticket_id = %s WHERE id = %s AND ticket_id = %s",
                (new_ticket_id, comment_id, request.source_ticket_id),
                fetch=False
            )
            if result:
                moved_comments += 1
        
        # Create relation
        execute_query(
            """INSERT INTO tticket_relations (ticket_id, related_ticket_id, relation_type, reason, created_by_user_id)
               VALUES (%s, %s, 'split_from', %s, 1)""",
            (new_ticket_id, request.source_ticket_id, request.reason)
        )
        
        # Log audit
        execute_query(
            """INSERT INTO tticket_audit_log (ticket_id, action, new_value, reason)
               VALUES (%s, 'split_into', %s, %s)""",
            (request.source_ticket_id, str(new_ticket_id), request.reason)
        )
        
        logger.info(f"✅ Split ticket {request.source_ticket_id} into {new_ticket_id}, moved {moved_comments} comments")
        
        return {
            "status": "success",
            "new_ticket_id": new_ticket_id,
            "new_ticket_number": new_ticket_number,
            "moved_comments": moved_comments,
            "message": f"Successfully split ticket into {new_ticket_number}"
        }
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"❌ Error splitting ticket: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/tickets/{ticket_id}/relations", tags=["Ticket Relations"])
async def get_ticket_relations(ticket_id: int):
    """Hent alle relationer for en ticket (begge retninger)"""
    try:
        relations = execute_query(
            """SELECT r.*, 
                      t.ticket_number as related_ticket_number,
                      t.subject as related_subject,
                      t.status as related_status
               FROM tticket_all_relations r
               LEFT JOIN tticket_tickets t ON r.related_ticket_id = t.id
               WHERE r.ticket_id = %s
               ORDER BY r.created_at DESC""",
            (ticket_id,)
        )
        
        return {"relations": relations, "total": len(relations)}
        
    except Exception as e:
        logger.error(f"❌ Error fetching relations: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/tickets/{ticket_id}/relations", tags=["Ticket Relations"])
async def create_ticket_relation(ticket_id: int, relation: TTicketRelationCreate):
    """Opret en relation mellem to tickets"""
    try:
        # Validate both tickets exist
        for tid in [relation.ticket_id, relation.related_ticket_id]:
            ticket = execute_query_single("SELECT id FROM tticket_tickets WHERE id = %s", (tid,))
            if not ticket:
                raise HTTPException(status_code=404, detail=f"Ticket {tid} not found")
        
        execute_query(
            """INSERT INTO tticket_relations (ticket_id, related_ticket_id, relation_type, reason, created_by_user_id)
               VALUES (%s, %s, %s, %s, 1)""",
            (relation.ticket_id, relation.related_ticket_id, relation.relation_type, relation.reason)
        )
        
        return {"status": "success", "message": "Relation created"}
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"❌ Error creating relation: {e}")
        raise HTTPException(status_code=500, detail=str(e))


# ============================================================================
# CALENDAR EVENTS ENDPOINTS
# ============================================================================

@router.get("/tickets/{ticket_id}/calendar-events", tags=["Calendar"])
async def get_calendar_events(ticket_id: int):
    """Hent alle kalender events for en ticket"""
    try:
        events = execute_query(
            """SELECT * FROM tticket_calendar_events 
               WHERE ticket_id = %s 
               ORDER BY event_date DESC, event_time DESC NULLS LAST""",
            (ticket_id,)
        )
        
        return {"events": events, "total": len(events)}
        
    except Exception as e:
        logger.error(f"❌ Error fetching calendar events: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/tickets/{ticket_id}/calendar-events", tags=["Calendar"])
async def create_calendar_event(ticket_id: int, event: TTicketCalendarEventCreate):
    """Opret kalender event (manual eller AI-foreslået)"""
    try:
        event_id = execute_insert(
            """INSERT INTO tticket_calendar_events 
               (ticket_id, title, description, event_type, event_date, event_time, 
                duration_minutes, all_day, status, suggested_by_ai, ai_confidence, 
                ai_source_text, created_by_user_id)
               VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 1)
               RETURNING id""",
            (ticket_id, event.title, event.description, event.event_type, 
             event.event_date, event.event_time, event.duration_minutes, 
             event.all_day, event.status, event.suggested_by_ai, 
             event.ai_confidence, event.ai_source_text)
        )
        
        logger.info(f"✅ Created calendar event {event_id} for ticket {ticket_id}")
        
        return {"status": "success", "event_id": event_id, "message": "Calendar event created"}
        
    except Exception as e:
        logger.error(f"❌ Error creating calendar event: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.put("/tickets/{ticket_id}/calendar-events/{event_id}", tags=["Calendar"])
async def update_calendar_event(ticket_id: int, event_id: int, status: CalendarEventStatus):
    """Opdater calendar event status"""
    try:
        execute_query(
            """UPDATE tticket_calendar_events 
               SET status = %s, updated_at = CURRENT_TIMESTAMP,
                   completed_at = CASE WHEN %s = 'completed' THEN CURRENT_TIMESTAMP ELSE completed_at END
               WHERE id = %s AND ticket_id = %s""",
            (status, status, event_id, ticket_id),
            fetch=False
        )
        
        return {"status": "success", "message": "Event updated"}
        
    except Exception as e:
        logger.error(f"❌ Error updating calendar event: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.delete("/tickets/{ticket_id}/calendar-events/{event_id}", tags=["Calendar"])
async def delete_calendar_event(ticket_id: int, event_id: int):
    """Slet calendar event"""
    try:
        execute_query(
            "DELETE FROM tticket_calendar_events WHERE id = %s AND ticket_id = %s",
            (event_id, ticket_id),
            fetch=False
        )
        
        return {"status": "success", "message": "Event deleted"}
        
    except Exception as e:
        logger.error(f"❌ Error deleting calendar event: {e}")
        raise HTTPException(status_code=500, detail=str(e))


# ============================================================================
# TEMPLATES ENDPOINTS
# ============================================================================

@router.get("/templates", response_model=List[TTicketTemplate], tags=["Templates"])
async def list_templates(
    category: Optional[str] = Query(None, description="Filter by category"),
    active_only: bool = Query(True, description="Only show active templates")
):
    """List alle tilgængelige templates"""
    try:
        query = "SELECT * FROM tticket_templates WHERE 1=1"
        params = []
        
        if category:
            query += " AND category = %s"
            params.append(category)
        
        if active_only:
            query += " AND is_active = true"
        
        query += " ORDER BY category, name"
        
        templates = execute_query(query, tuple(params) if params else None)
        
        return templates
        
    except Exception as e:
        logger.error(f"❌ Error listing templates: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/templates", tags=["Templates"])
async def create_template(template: TTicketTemplateCreate):
    """Opret ny template"""
    try:
        template_id = execute_insert(
            """INSERT INTO tticket_templates 
               (name, description, category, subject_template, body_template, 
                available_placeholders, default_attachments, is_active, 
                requires_approval, created_by_user_id)
               VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, 1)
               RETURNING id""",
            (template.name, template.description, template.category, 
             template.subject_template, template.body_template, 
             template.available_placeholders, template.default_attachments, 
             template.is_active, template.requires_approval)
        )
        
        logger.info(f"✅ Created template {template_id}: {template.name}")
        
        return {"status": "success", "template_id": template_id, "message": "Template created"}
        
    except Exception as e:
        logger.error(f"❌ Error creating template: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/tickets/{ticket_id}/render-template", response_model=TemplateRenderResponse, tags=["Templates"])
async def render_template(ticket_id: int, request: TemplateRenderRequest):
    """
    Render template med ticket data
    
    Erstatter placeholders med faktiske værdier:
    - {{ticket_number}}
    - {{ticket_subject}}
    - {{customer_name}}
    - {{contact_name}}
    - etc.
    """
    try:
        # Get template
        template = execute_query_single(
            "SELECT * FROM tticket_templates WHERE id = %s",
            (request.template_id,)
        )
        if not template:
            raise HTTPException(status_code=404, detail="Template not found")
        
        # Get ticket with customer and contact data
        ticket_data = execute_query_single(
            """SELECT t.*, 
                      c.name as customer_name,
                      con.name as contact_name,
                      con.email as contact_email
               FROM tticket_tickets t
               LEFT JOIN customers c ON t.customer_id = c.id
               LEFT JOIN contacts con ON t.contact_id = con.id
               WHERE t.id = %s""",
            (ticket_id,)
        )
        if not ticket_data:
            raise HTTPException(status_code=404, detail="Ticket not found")
        
        # Build replacement dict
        replacements = {
            '{{ticket_number}}': ticket_data.get('ticket_number', ''),
            '{{ticket_subject}}': ticket_data.get('subject', ''),
            '{{customer_name}}': ticket_data.get('customer_name', ''),
            '{{contact_name}}': ticket_data.get('contact_name', ''),
            '{{contact_email}}': ticket_data.get('contact_email', ''),
        }
        
        # Add custom data
        if request.custom_data:
            for key, value in request.custom_data.items():
                replacements[f'{{{{{key}}}}}'] = str(value)
        
        # Render subject and body
        rendered_subject = template['subject_template']
        rendered_body = template['body_template']
        placeholders_used = []
        
        for placeholder, value in replacements.items():
            if placeholder in rendered_body or (rendered_subject and placeholder in rendered_subject):
                placeholders_used.append(placeholder)
                if rendered_subject:
                    rendered_subject = rendered_subject.replace(placeholder, value)
                rendered_body = rendered_body.replace(placeholder, value)
        
        return TemplateRenderResponse(
            subject=rendered_subject,
            body=rendered_body,
            placeholders_used=placeholders_used
        )
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"❌ Error rendering template: {e}")
        raise HTTPException(status_code=500, detail=str(e))


# ============================================================================
# AI SUGGESTIONS ENDPOINTS
# ============================================================================

@router.get("/tickets/{ticket_id}/suggestions", response_model=List[TTicketAISuggestion], tags=["AI Suggestions"])
async def get_ai_suggestions(
    ticket_id: int,
    status: Optional[AISuggestionStatus] = Query(None, description="Filter by status"),
    suggestion_type: Optional[AISuggestionType] = Query(None, description="Filter by type")
):
    """Hent AI forslag for ticket"""
    try:
        query = "SELECT * FROM tticket_ai_suggestions WHERE ticket_id = %s"
        params = [ticket_id]
        
        if status:
            query += " AND status = %s"
            params.append(status)
        
        if suggestion_type:
            query += " AND suggestion_type = %s"
            params.append(suggestion_type)
        
        query += " ORDER BY created_at DESC"
        
        suggestions = execute_query(query, tuple(params))
        
        return suggestions
        
    except Exception as e:
        logger.error(f"❌ Error fetching AI suggestions: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/tickets/{ticket_id}/suggestions/{suggestion_id}/review", tags=["AI Suggestions"])
async def review_ai_suggestion(ticket_id: int, suggestion_id: int, review: AISuggestionReviewRequest):
    """
    Accepter eller afvis AI forslag
    
    **VIGTIGT**: Denne endpoint ændrer KUN suggestion status.
    Den udfører IKKE automatisk den foreslåede handling.
    Brugeren skal selv implementere ændringen efter accept.
    """
    try:
        # Get suggestion
        suggestion = execute_query_single(
            "SELECT * FROM tticket_ai_suggestions WHERE id = %s AND ticket_id = %s",
            (suggestion_id, ticket_id)
        )
        if not suggestion:
            raise HTTPException(status_code=404, detail="Suggestion not found")
        
        if suggestion['status'] != 'pending':
            raise HTTPException(status_code=400, detail=f"Suggestion already {suggestion['status']}")
        
        # Update status
        new_status = 'accepted' if review.action == 'accept' else 'rejected'
        execute_query(
            """UPDATE tticket_ai_suggestions 
               SET status = %s, reviewed_by_user_id = 1, reviewed_at = CURRENT_TIMESTAMP
               WHERE id = %s""",
            (new_status, suggestion_id),
            fetch=False
        )
        
        # Log audit
        execute_query(
            """INSERT INTO tticket_audit_log (ticket_id, action, new_value, reason)
               VALUES (%s, %s, %s, %s)""",
            (ticket_id, f'ai_suggestion_{review.action}ed', 
             f"{suggestion['suggestion_type']}: {suggestion_id}", review.note)
        )
        
        logger.info(f"✅ AI suggestion {suggestion_id} {review.action}ed for ticket {ticket_id}")
        
        return {
            "status": "success",
            "action": review.action,
            "suggestion_type": suggestion['suggestion_type'],
            "message": f"Suggestion {review.action}ed. Manual implementation required if accepted."
        }
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"❌ Error reviewing AI suggestion: {e}")
        raise HTTPException(status_code=500, detail=str(e))


# ============================================================================
# DEADLINE ENDPOINT
# ============================================================================

@router.put("/tickets/{ticket_id}/deadline", tags=["Tickets"])
async def update_ticket_deadline(ticket_id: int, request: TicketDeadlineUpdateRequest):
    """Opdater ticket deadline"""
    try:
        # Get current deadline
        current = execute_query_single(
            "SELECT deadline FROM tticket_tickets WHERE id = %s",
            (ticket_id,)
        )
        if not current:
            raise HTTPException(status_code=404, detail="Ticket not found")
        
        # Update deadline
        execute_query(
            "UPDATE tticket_tickets SET deadline = %s WHERE id = %s",
            (request.deadline, ticket_id),
            fetch=False
        )
        
        # Log audit (handled by trigger automatically)
        if request.reason:
            execute_query(
                """INSERT INTO tticket_audit_log (ticket_id, action, field_name, old_value, new_value, reason)
                   VALUES (%s, 'deadline_change', 'deadline', %s, %s, %s)""",
                (ticket_id, str(current.get('deadline')), str(request.deadline), request.reason)
            )
        
        logger.info(f"✅ Updated deadline for ticket {ticket_id}: {request.deadline}")
        
        return {
            "status": "success",
            "old_deadline": current.get('deadline'),
            "new_deadline": request.deadline,
            "message": "Deadline updated"
        }
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"❌ Error updating deadline: {e}")
        raise HTTPException(status_code=500, detail=str(e))


# ============================================================================
# AUDIT LOG ENDPOINT
# ============================================================================

@router.get("/tickets/{ticket_id}/audit-log", response_model=List[TTicketAuditLog], tags=["Audit"])
async def get_audit_log(
    ticket_id: int,
    limit: int = Query(50, ge=1, le=200, description="Number of entries"),
    offset: int = Query(0, ge=0, description="Offset for pagination")
):
    """Hent audit log for ticket (sporbarhed)"""
    try:
        logs = execute_query(
            """SELECT * FROM tticket_audit_log 
               WHERE ticket_id = %s 
               ORDER BY performed_at DESC 
               LIMIT %s OFFSET %s""",
            (ticket_id, limit, offset)
        )
        
        return logs
        
    except Exception as e:
        logger.error(f"❌ Error fetching audit log: {e}")
        raise HTTPException(status_code=500, detail=str(e))


# ============================================================================
# ARCHIVED TICKETS (SIMPLY-CRM IMPORT)
# ============================================================================

@router.post("/archived/simply/import", tags=["Archived Tickets"])
async def import_simply_archived_tickets(
    limit: int = Query(5000, ge=1, le=50000, description="Maximum tickets to import"),
    include_messages: bool = Query(True, description="Include comments and emails"),
    ticket_number: Optional[str] = Query(None, description="Import a single ticket by number"),
    force: bool = Query(False, description="Update even if sync hash matches"),
    current_user: dict = Depends(sync_admin_access)
):
    """
    One-time import of archived tickets from Simply-CRM.
    """
    stats = {"imported": 0, "updated": 0, "skipped": 0, "errors": 0, "messages_imported": 0}

    try:
        async with SimplyCRMService() as service:
            if ticket_number:
                module_name = getattr(settings, "SIMPLYCRM_TICKET_MODULE", "HelpDesk")
                sanitized = _escape_simply_value(ticket_number)
                tickets = []
                for field in ("ticket_no", "ticketnumber", "ticket_number"):
                    query = f"SELECT * FROM {module_name} WHERE {field} = '{sanitized}';"
                    tickets = await service.query(query)
                    if tickets:
                        break
            else:
                tickets = await service.fetch_tickets(limit=limit)

            logger.info(f"🔍 Importing {len(tickets)} archived tickets from Simply-CRM")

            account_cache: dict[str, Optional[str]] = {}
            contact_cache: dict[str, Optional[str]] = {}

            for ticket in tickets:
                try:
                    external_id = _get_first_value(ticket, ["id", "ticketid", "ticket_id"])
                    if not external_id:
                        stats["skipped"] += 1
                        continue

                    data_hash = _calculate_hash(ticket)
                    existing = execute_query_single(
                        """SELECT id, sync_hash
                           FROM tticket_archived_tickets
                           WHERE source_system = %s AND external_id = %s""",
                        ("simplycrm", external_id)
                    )

                    ticket_number = _get_first_value(
                        ticket,
                        ["ticket_no", "ticketnumber", "ticket_number", "ticketid", "ticket_id", "id"]
                    )
                    title = _get_first_value(
                        ticket,
                        ["title", "subject", "ticket_title", "tickettitle", "summary"]
                    )
                    contact_name = _get_first_value(
                        ticket,
                        ["contactname", "contact_name", "contact"]
                    )
                    organization_name = _get_first_value(
                        ticket,
                        ["accountname", "account_name", "organization", "company"]
                    )

                    account_id = _get_first_value(
                        ticket,
                        ["parent_id", "account_id", "accountid", "account"]
                    )
                    if not organization_name and _looks_like_external_id(account_id):
                        if account_id not in account_cache:
                            related = await service.retrieve(account_id)
                            account_cache[account_id] = _get_first_value(
                                related or {},
                                ["accountname", "account_name", "name"]
                            )
                            if not account_cache[account_id]:
                                first_name = _get_first_value(related or {}, ["firstname", "first_name", "first"])
                                last_name = _get_first_value(related or {}, ["lastname", "last_name", "last"])
                                combined = " ".join([name for name in [first_name, last_name] if name])
                                if combined:
                                    account_cache[account_id] = None
                                    if not contact_name:
                                        contact_name = combined
                                related_account_id = _get_first_value(
                                    related or {},
                                    ["account_id", "accountid", "account"]
                                )
                                if related_account_id and _looks_like_external_id(related_account_id):
                                    if related_account_id not in account_cache:
                                        account = await service.retrieve(related_account_id)
                                        account_cache[related_account_id] = _get_first_value(
                                            account or {},
                                            ["accountname", "account_name", "name"]
                                        )
                                    organization_name = account_cache.get(related_account_id)
                        if not organization_name:
                            organization_name = account_cache.get(account_id)

                    contact_name = _get_first_value(
                        ticket,
                        ["contactname", "contact_name", "contact"]
                    )

                    contact_id = _get_first_value(
                        ticket,
                        ["contact_id", "contactid"]
                    )
                    if not contact_name and _looks_like_external_id(contact_id):
                        if contact_id not in contact_cache:
                            contact = await service.retrieve(contact_id)
                            first_name = _get_first_value(contact or {}, ["firstname", "first_name", "first"])
                            last_name = _get_first_value(contact or {}, ["lastname", "last_name", "last"])
                            combined = " ".join([name for name in [first_name, last_name] if name])
                            contact_cache[contact_id] = combined or _get_first_value(
                                contact or {},
                                ["contactname", "name"]
                            )
                            if not organization_name:
                                related_account_id = _get_first_value(
                                    contact or {},
                                    ["account_id", "accountid", "account"]
                                )
                                if related_account_id and _looks_like_external_id(related_account_id):
                                    if related_account_id not in account_cache:
                                        account = await service.retrieve(related_account_id)
                                        account_cache[related_account_id] = _get_first_value(
                                            account or {},
                                            ["accountname", "account_name", "name"]
                                        )
                                    organization_name = account_cache.get(related_account_id)
                        contact_name = contact_cache.get(contact_id)

                    email_from = _get_first_value(
                        ticket,
                        ["email_from", "from_email", "from", "email", "email_from_address"]
                    )
                    time_spent_hours = _parse_hours(
                        _get_first_value(ticket, ["time_spent", "hours", "time_spent_hours", "spent_time", "cf_time_spent", "cf_tid_brugt"])
                    )
                    description = _get_first_value(
                        ticket,
                        ["description", "ticket_description", "comments", "issue"]
                    )
                    solution = _get_first_value(
                        ticket,
                        ["solution", "resolution", "solutiontext", "resolution_text"]
                    )
                    status = _get_first_value(
                        ticket,
                        ["status", "ticketstatus", "state"]
                    )
                    priority = _get_first_value(
                        ticket,
                        ["priority", "ticketpriorities", "ticketpriority"]
                    )
                    source_created_at = _parse_datetime(
                        _get_first_value(ticket, ["createdtime", "created_at", "createdon", "created_time"])
                    )
                    source_updated_at = _parse_datetime(
                        _get_first_value(ticket, ["modifiedtime", "updated_at", "modified_time", "updatedtime"])
                    )

                    if existing:
                        hash_matches = existing.get("sync_hash") == data_hash
                        if not force and hash_matches:
                            archived_ticket_id = existing["id"]
                            stats["skipped"] += 1
                        else:
                            execute_update(
                                """
                                UPDATE tticket_archived_tickets
                                SET ticket_number = %s,
                                    title = %s,
                                    organization_name = %s,
                                    contact_name = %s,
                                    email_from = %s,
                                    time_spent_hours = %s,
                                    description = %s,
                                    solution = %s,
                                    status = %s,
                                    priority = %s,
                                    source_created_at = %s,
                                    source_updated_at = %s,
                                    last_synced_at = CURRENT_TIMESTAMP,
                                    sync_hash = %s,
                                    raw_data = %s::jsonb
                                WHERE id = %s
                                """,
                                (
                                    ticket_number,
                                    title,
                                    organization_name,
                                    contact_name,
                                    email_from,
                                    time_spent_hours,
                                    description,
                                    solution,
                                    status,
                                    priority,
                                    source_created_at,
                                    source_updated_at,
                                    data_hash,
                                    json.dumps(ticket, default=str),
                                    existing["id"]
                                )
                            )
                            archived_ticket_id = existing["id"]
                            stats["updated"] += 1
                    else:
                        archived_ticket_id = execute_insert(
                            """
                            INSERT INTO tticket_archived_tickets (
                                source_system,
                                external_id,
                                ticket_number,
                                title,
                                organization_name,
                                contact_name,
                                email_from,
                                time_spent_hours,
                                description,
                                solution,
                                status,
                                priority,
                                source_created_at,
                                source_updated_at,
                                last_synced_at,
                                sync_hash,
                                raw_data
                            ) VALUES (
                                %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
                                CURRENT_TIMESTAMP, %s, %s::jsonb
                            )
                            RETURNING id
                            """,
                            (
                                "simplycrm",
                                external_id,
                                ticket_number,
                                title,
                                organization_name,
                                contact_name,
                                email_from,
                                time_spent_hours,
                                description,
                                solution,
                                status,
                                priority,
                                source_created_at,
                                source_updated_at,
                                data_hash,
                                json.dumps(ticket, default=str)
                            )
                        )
                        stats["imported"] += 1

                    if include_messages and archived_ticket_id:
                        execute_update(
                            "DELETE FROM tticket_archived_messages WHERE archived_ticket_id = %s",
                            (archived_ticket_id,)
                        )

                        comments = await service.fetch_ticket_comments(external_id)
                        emails = await service.fetch_ticket_emails(external_id)

                        for comment in comments:
                            execute_insert(
                                """
                                INSERT INTO tticket_archived_messages (
                                    archived_ticket_id,
                                    message_type,
                                    subject,
                                    body,
                                    author_name,
                                    author_email,
                                    source_created_at,
                                    raw_data
                                ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s::jsonb)
                                RETURNING id
                                """,
                                (
                                    archived_ticket_id,
                                    "comment",
                                    None,
                                    _get_first_value(comment, ["commentcontent", "comment", "content", "description"]),
                                    _get_first_value(comment, ["author", "assigned_user_id", "created_by", "creator"]),
                                    _get_first_value(comment, ["email", "author_email", "from_email"]),
                                    _parse_datetime(_get_first_value(comment, ["createdtime", "created_at", "created_time"])),
                                    json.dumps(comment, default=str)
                                )
                            )
                            stats["messages_imported"] += 1

                        for email in emails:
                            execute_insert(
                                """
                                INSERT INTO tticket_archived_messages (
                                    archived_ticket_id,
                                    message_type,
                                    subject,
                                    body,
                                    author_name,
                                    author_email,
                                    source_created_at,
                                    raw_data
                                ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s::jsonb)
                                RETURNING id
                                """,
                                (
                                    archived_ticket_id,
                                    "email",
                                    _get_first_value(email, ["subject", "title"]),
                                    _get_first_value(email, ["description", "body", "email_body", "content"]),
                                    _get_first_value(email, ["from_name", "sender", "assigned_user_id"]),
                                    _get_first_value(email, ["from_email", "email", "sender_email"]),
                                    _parse_datetime(_get_first_value(email, ["createdtime", "created_at", "created_time"])),
                                    json.dumps(email, default=str)
                                )
                            )
                            stats["messages_imported"] += 1

                except Exception as e:
                    logger.error(f"❌ Archived ticket import failed: {e}")
                    stats["errors"] += 1

            logger.info(f"✅ Archived ticket import complete: {stats}")
            return stats

    except Exception as e:
        logger.error(f"❌ Archived ticket import failed: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/archived/vtiger/import", tags=["Archived Tickets"])
async def import_vtiger_archived_tickets(
    limit: int = Query(5000, ge=1, le=50000, description="Maximum tickets to import"),
    include_messages: bool = Query(True, description="Include comments and emails"),
    ticket_number: Optional[str] = Query(None, description="Import a single ticket by number"),
    force: bool = Query(False, description="Update even if sync hash matches"),
    current_user: dict = Depends(sync_admin_access)
):
    """
    One-time import of archived tickets from vTiger (Cases module).
    """
    stats = {"imported": 0, "updated": 0, "skipped": 0, "errors": 0, "messages_imported": 0}

    try:
        vtiger = get_vtiger_service()

        if ticket_number:
            sanitized = _escape_simply_value(ticket_number)
            tickets = []
            for field in ("ticket_no", "ticketnumber", "ticket_number"):
                query = f"SELECT * FROM Cases WHERE {field} = '{sanitized}' LIMIT 1;"
                tickets = await _vtiger_query_with_retry(vtiger, query)
                if tickets:
                    break
        else:
            tickets = []
            offset = 0
            batch_size = 200
            while len(tickets) < limit:
                query = f"SELECT * FROM Cases LIMIT {offset}, {batch_size};"
                batch = await _vtiger_query_with_retry(vtiger, query)
                if not batch:
                    break
                tickets.extend(batch)
                offset += batch_size
                if len(batch) < batch_size:
                    break

            tickets = tickets[:limit]

        logger.info(f"🔍 Importing {len(tickets)} archived tickets from vTiger")

        account_cache: dict[str, Optional[str]] = {}
        contact_cache: dict[str, Optional[str]] = {}
        contact_account_cache: dict[str, Optional[str]] = {}

        for ticket in tickets:
            try:
                external_id = _get_first_value(ticket, ["id", "ticketid", "ticket_id"])
                if not external_id:
                    stats["skipped"] += 1
                    continue

                data_hash = _calculate_hash(ticket)
                existing = execute_query_single(
                    """SELECT id, sync_hash
                       FROM tticket_archived_tickets
                       WHERE source_system = %s AND external_id = %s""",
                    ("vtiger", external_id)
                )

                ticket_no_value = _get_first_value(
                    ticket,
                    ["ticket_no", "ticketnumber", "ticket_number", "case_no", "casenumber", "id"]
                )
                title = _get_first_value(
                    ticket,
                    ["title", "subject", "ticket_title", "tickettitle", "summary"]
                )
                contact_name = _get_first_value(
                    ticket,
                    ["contactname", "contact_name", "contact", "firstname", "lastname"]
                )
                organization_name = _get_first_value(
                    ticket,
                    ["accountname", "account_name", "organization", "company"]
                )

                account_id = _get_first_value(
                    ticket,
                    ["parent_id", "account_id", "accountid", "account"]
                )
                if not organization_name and _looks_like_external_id(account_id):
                    if account_id not in account_cache:
                        account_rows = await _vtiger_query_with_retry(
                            vtiger,
                            f"SELECT * FROM Accounts WHERE id='{account_id}' LIMIT 1;"
                        )
                        account_cache[account_id] = _get_first_value(
                            account_rows[0] if account_rows else {},
                            ["accountname", "account_name", "name"]
                        )
                    organization_name = account_cache.get(account_id)

                contact_id = _get_first_value(
                    ticket,
                    ["contact_id", "contactid"]
                )
                if _looks_like_external_id(contact_id):
                    if contact_id not in contact_cache or contact_id not in contact_account_cache:
                        contact_rows = await _vtiger_query_with_retry(
                            vtiger,
                            f"SELECT * FROM Contacts WHERE id='{contact_id}' LIMIT 1;"
                        )
                        contact_data = contact_rows[0] if contact_rows else {}
                        first_name = _get_first_value(contact_data, ["firstname", "first_name", "first"])
                        last_name = _get_first_value(contact_data, ["lastname", "last_name", "last"])
                        combined_name = " ".join([name for name in [first_name, last_name] if name]).strip()
                        contact_cache[contact_id] = combined_name or _get_first_value(
                            contact_data,
                            ["contactname", "contact_name", "name"]
                        )
                        related_account_id = _get_first_value(
                            contact_data,
                            ["account_id", "accountid", "account", "parent_id"]
                        )
                        contact_account_cache[contact_id] = related_account_id if _looks_like_external_id(related_account_id) else None

                    if not contact_name:
                        contact_name = contact_cache.get(contact_id)

                    if not organization_name:
                        related_account_id = contact_account_cache.get(contact_id)
                        if related_account_id:
                            if related_account_id not in account_cache:
                                account_rows = await _vtiger_query_with_retry(
                                    vtiger,
                                    f"SELECT * FROM Accounts WHERE id='{related_account_id}' LIMIT 1;"
                                )
                                account_cache[related_account_id] = _get_first_value(
                                    account_rows[0] if account_rows else {},
                                    ["accountname", "account_name", "name"]
                                )
                            organization_name = account_cache.get(related_account_id)

                email_from = _get_first_value(
                    ticket,
                    ["email_from", "from_email", "from", "email", "email_from_address"]
                )
                time_spent_hours = _parse_hours(
                    _get_first_value(ticket, ["time_spent", "hours", "time_spent_hours", "spent_time", "cf_time_spent", "cf_tid_brugt"])
                )
                description = _get_first_value(
                    ticket,
                    ["description", "ticket_description", "comments", "issue"]
                )
                solution = _get_first_value(
                    ticket,
                    ["solution", "resolution", "solutiontext", "resolution_text", "answer"]
                )
                status = _get_first_value(
                    ticket,
                    ["status", "casestatus", "ticketstatus", "state"]
                )
                priority = _get_first_value(
                    ticket,
                    ["priority", "ticketpriorities", "ticketpriority"]
                )
                source_created_at = _parse_datetime(
                    _get_first_value(ticket, ["createdtime", "created_at", "createdon", "created_time"])
                )
                source_updated_at = _parse_datetime(
                    _get_first_value(ticket, ["modifiedtime", "updated_at", "modified_time", "updatedtime"])
                )

                if existing:
                    hash_matches = existing.get("sync_hash") == data_hash
                    if not force and hash_matches:
                        archived_ticket_id = existing["id"]
                        stats["skipped"] += 1
                    else:
                        execute_update(
                            """
                            UPDATE tticket_archived_tickets
                            SET ticket_number = %s,
                                title = %s,
                                organization_name = %s,
                                contact_name = %s,
                                email_from = %s,
                                time_spent_hours = %s,
                                description = %s,
                                solution = %s,
                                status = %s,
                                priority = %s,
                                source_created_at = %s,
                                source_updated_at = %s,
                                last_synced_at = CURRENT_TIMESTAMP,
                                sync_hash = %s,
                                raw_data = %s::jsonb
                            WHERE id = %s
                            """,
                            (
                                ticket_no_value,
                                title,
                                organization_name,
                                contact_name,
                                email_from,
                                time_spent_hours,
                                description,
                                solution,
                                status,
                                priority,
                                source_created_at,
                                source_updated_at,
                                data_hash,
                                json.dumps(ticket, default=str),
                                existing["id"]
                            )
                        )
                        archived_ticket_id = existing["id"]
                        stats["updated"] += 1
                else:
                    archived_ticket_id = execute_insert(
                        """
                        INSERT INTO tticket_archived_tickets (
                            source_system,
                            external_id,
                            ticket_number,
                            title,
                            organization_name,
                            contact_name,
                            email_from,
                            time_spent_hours,
                            description,
                            solution,
                            status,
                            priority,
                            source_created_at,
                            source_updated_at,
                            last_synced_at,
                            sync_hash,
                            raw_data
                        ) VALUES (
                            %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
                            CURRENT_TIMESTAMP, %s, %s::jsonb
                        )
                        RETURNING id
                        """,
                        (
                            "vtiger",
                            external_id,
                            ticket_no_value,
                            title,
                            organization_name,
                            contact_name,
                            email_from,
                            time_spent_hours,
                            description,
                            solution,
                            status,
                            priority,
                            source_created_at,
                            source_updated_at,
                            data_hash,
                            json.dumps(ticket, default=str)
                        )
                    )
                    stats["imported"] += 1

                if include_messages and archived_ticket_id:
                    execute_update(
                        "DELETE FROM tticket_archived_messages WHERE archived_ticket_id = %s",
                        (archived_ticket_id,)
                    )

                    comments = await _vtiger_query_with_retry(
                        vtiger,
                        f"SELECT * FROM ModComments WHERE related_to = '{external_id}';"
                    )
                    emails = await _vtiger_query_with_retry(
                        vtiger,
                        f"SELECT * FROM Emails WHERE parent_id = '{external_id}';"
                    )

                    for comment in comments:
                        execute_insert(
                            """
                            INSERT INTO tticket_archived_messages (
                                archived_ticket_id,
                                message_type,
                                subject,
                                body,
                                author_name,
                                author_email,
                                source_created_at,
                                raw_data
                            ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s::jsonb)
                            RETURNING id
                            """,
                            (
                                archived_ticket_id,
                                "comment",
                                None,
                                _get_first_value(comment, ["commentcontent", "comment", "content", "description"]),
                                _get_first_value(comment, ["author", "assigned_user_id", "created_by", "creator"]),
                                _get_first_value(comment, ["email", "author_email", "from_email"]),
                                _parse_datetime(_get_first_value(comment, ["createdtime", "created_at", "created_time"])),
                                json.dumps(comment, default=str)
                            )
                        )
                        stats["messages_imported"] += 1

                    for email in emails:
                        execute_insert(
                            """
                            INSERT INTO tticket_archived_messages (
                                archived_ticket_id,
                                message_type,
                                subject,
                                body,
                                author_name,
                                author_email,
                                source_created_at,
                                raw_data
                            ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s::jsonb)
                            RETURNING id
                            """,
                            (
                                archived_ticket_id,
                                "email",
                                _get_first_value(email, ["subject", "title"]),
                                _get_first_value(email, ["description", "body", "email_body", "content"]),
                                _get_first_value(email, ["from_name", "sender", "assigned_user_id"]),
                                _get_first_value(email, ["from_email", "email", "sender_email"]),
                                _parse_datetime(_get_first_value(email, ["createdtime", "created_at", "created_time"])),
                                json.dumps(email, default=str)
                            )
                        )
                        stats["messages_imported"] += 1

            except Exception as e:
                logger.error(f"❌ vTiger archived ticket import failed: {e}")
                stats["errors"] += 1

        logger.info(f"✅ vTiger archived ticket import complete: {stats}")
        return stats

    except Exception as e:
        logger.error(f"❌ vTiger archived ticket import failed: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/archived/status", tags=["Archived Tickets"])
async def get_archived_sync_status(current_user: dict = Depends(sync_admin_access)):
    """
    Return archived sync parity status for Simply-CRM and vTiger.
    """
    source_keys = ("simplycrm", "vtiger")
    sources: dict[str, dict] = {}

    for source_key in source_keys:
        local_ticket_row = execute_query_single(
            """
            SELECT COUNT(*) AS total_tickets,
                   MAX(last_synced_at) AS last_synced_at
            FROM tticket_archived_tickets
            WHERE source_system = %s
            """,
            (source_key,)
        ) or {}

        local_message_row = execute_query_single(
            """
            SELECT COUNT(*) AS total_messages
            FROM tticket_archived_messages m
            INNER JOIN tticket_archived_tickets t ON t.id = m.archived_ticket_id
            WHERE t.source_system = %s
            """,
            (source_key,)
        ) or {}

        local_tickets = int(local_ticket_row.get("total_tickets") or 0)
        local_messages = int(local_message_row.get("total_messages") or 0)
        last_synced_value = local_ticket_row.get("last_synced_at")
        if isinstance(last_synced_value, (datetime, date)):
            last_synced_at_iso = last_synced_value.isoformat()
        else:
            last_synced_at_iso = None

        sources[source_key] = {
            "remote_total_tickets": None,
            "local_total_tickets": local_tickets,
            "local_total_messages": local_messages,
            "last_synced_at": last_synced_at_iso,
            "diff": None,
            "is_synced": False,
            "error": None,
        }

    try:
        async with SimplyCRMService() as service:
            module_name = getattr(settings, "SIMPLYCRM_TICKET_MODULE", "Tickets")
            simply_rows = await service.query(f"SELECT count(*) AS total_count FROM {module_name};")
            simply_remote_count = _extract_count_value(simply_rows)
            sources["simplycrm"]["remote_total_tickets"] = simply_remote_count
            if simply_remote_count is not None:
                sources["simplycrm"]["diff"] = simply_remote_count - sources["simplycrm"]["local_total_tickets"]
                sources["simplycrm"]["is_synced"] = sources["simplycrm"]["diff"] == 0
            elif service.last_query_error:
                sources["simplycrm"]["error"] = service.last_query_error.get("message") or str(service.last_query_error)
    except Exception as e:
        logger.warning("⚠️ Simply-CRM archived status check failed: %s", e)
        sources["simplycrm"]["error"] = str(e)

    try:
        vtiger = get_vtiger_service()
        vtiger_rows = await _vtiger_query_with_retry(vtiger, "SELECT count(*) AS total_count FROM Cases;")
        vtiger_remote_count = _extract_count_value(vtiger_rows)
        sources["vtiger"]["remote_total_tickets"] = vtiger_remote_count
        if vtiger_remote_count is not None:
            sources["vtiger"]["diff"] = vtiger_remote_count - sources["vtiger"]["local_total_tickets"]
            sources["vtiger"]["is_synced"] = sources["vtiger"]["diff"] == 0
        elif vtiger.last_query_error:
            sources["vtiger"]["error"] = vtiger.last_query_error.get("message") or str(vtiger.last_query_error)
    except Exception as e:
        logger.warning("⚠️ vTiger archived status check failed: %s", e)
        sources["vtiger"]["error"] = str(e)

    overall_synced = all(sources[key].get("is_synced") is True for key in source_keys)

    return {
        "checked_at": datetime.utcnow().isoformat(),
        "overall_synced": overall_synced,
        "sources": sources,
    }


@router.get("/archived/simply/modules", tags=["Archived Tickets"])
async def list_simply_modules(current_user: dict = Depends(sync_admin_access)):
    """
    List available Simply-CRM modules (debug helper).
    """
    try:
        async with SimplyCRMService() as service:
            modules = await service.list_types()
            return {"modules": modules}
    except Exception as e:
        logger.error(f"❌ Failed to list Simply-CRM modules: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/archived/simply/ticket", tags=["Archived Tickets"])
async def fetch_simply_ticket(
    ticket_number: Optional[str] = Query(None, description="Ticket number, e.g. TT934"),
    external_id: Optional[str] = Query(None, description="VTiger record ID, e.g. 17x1234"),
    current_user: dict = Depends(sync_admin_access)
):
    """
    Fetch a single HelpDesk ticket from Simply-CRM by ticket number or record id.
    """
    if not ticket_number and not external_id:
        raise HTTPException(status_code=400, detail="Provide ticket_number or external_id")

    try:
        async with SimplyCRMService() as service:
            module_name = getattr(settings, "SIMPLYCRM_TICKET_MODULE", "HelpDesk")

            if external_id:
                record = await service.retrieve(external_id)
                return {"module": module_name, "records": [record] if record else []}

            sanitized = _escape_simply_value(ticket_number or "")
            fields = ["ticket_no", "ticketnumber", "ticket_number"]
            for field in fields:
                query = f"SELECT * FROM {module_name} WHERE {field} = '{sanitized}';"
                records = await service.query(query)
                if records:
                    return {"module": module_name, "match_field": field, "records": records}

            return {"module": module_name, "records": []}

    except Exception as e:
        logger.error(f"❌ Failed to fetch Simply-CRM ticket: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/archived/simply/record", tags=["Archived Tickets"])
async def fetch_simply_record(
    record_id: str = Query(..., description="VTiger record ID, e.g. 11x2601"),
    module: Optional[str] = Query(None, description="Optional module name for context"),
    current_user: dict = Depends(sync_admin_access)
):
    """
    Fetch a single record from Simply-CRM by record id.
    """
    try:
        async with SimplyCRMService() as service:
            record = await service.retrieve(record_id)
            return {"module": module, "record": record}
    except Exception as e:
        logger.error(f"❌ Failed to fetch Simply-CRM record: {e}")
        raise HTTPException(status_code=500, detail=str(e))
