from typing import Optional
from datetime import datetime

from django.db.transaction import atomic

from helpdesk.repositories.ticket_repository import TicketRepository
from helpdesk.models import Ticket, TicketStatus
from django.contrib.auth import get_user_model

class TicketNotFoundError(Exception):
    """Raised when the requested Ticket is not found."""
    pass

class NotificationService:
    """Placeholder สำหรับ NotificationService"""
    def send_assignment_notification(self, ticket_id, user_id):
        pass

class TicketService:
    def __init__(self, ticket_repo: Optional[TicketRepository] = None, notification_service=None):
        # รองรับ DI ในการทดสอบ — หากไม่ส่งมา จะสร้าง repo ใหม่สำหรับ production
        self.ticket_repo = ticket_repo or TicketRepository()
        self.notification_service = notification_service

    @atomic
    def update_ticket_on_new_message(
            self,
            ticket_id: int,
            content: str,
            timestamp: datetime,
            sender_is_agent: bool,
    ) -> Ticket:
        """
        อัปเดต Ticket เมื่อมีข้อความใหม่เข้ามาใน Chat
        - ถ้าไม่พบ Ticket จะโยน TicketNotFoundError
        """
        ticket = self.ticket_repo.get_ticket_by_id(ticket_id)

        if ticket is None:
            raise TicketNotFoundError(f"Ticket with id={ticket_id} not found.")

        update_data = {
            "last_message_at": timestamp,
            "last_message_content": content,
            "is_read": True if sender_is_agent else False,
        }

        return self.ticket_repo.update_ticket(ticket, update_data)

    def update_ticket_status(self, ticket_id: int, new_status: str) -> Ticket:
        """อัปเดตสถานะของ Ticket และตรวจสอบ Business Rule"""
        ticket = self.ticket_repo.get_ticket_by_id(ticket_id)
        if ticket is None:
            raise TicketNotFoundError(f"Ticket with id={ticket_id} not found.")

        # Business Rule ห้ามเปลี่ยนจาก CLOSED -> OPEN
        if ticket.status == TicketStatus.CLOSED and new_status == TicketStatus.OPEN:
            raise ValueError("Cannot transition from CLOSED to OPEN")

        # ตรวจสอบว่า new_status เป็นค่าที่ถูกต้องใน enum หรือไม่ (เผื่อกรณี View ส่ง string ที่ผิดมา)
        if new_status not in TicketStatus:
            raise ValueError(f"Invalid status value: {new_status}")

        updated_ticket = self.ticket_repo.update_ticket(ticket, {"status": new_status})

        # คืน updated_ticket
        return updated_ticket

    def get_inbox_summary(self):
        return self.ticket_repo.get_unified_inbox_list()

    def get_ticket_detail(self, ticket_id: int) -> Ticket:
        ticket = self.ticket_repo.get_ticket_by_id(ticket_id)
        if ticket is None:
            raise TicketNotFoundError(f"Ticket with id={ticket_id} not found.")
        return ticket

    def get_ticket_ref(self, ticket_id: int) -> dict:
        ticket = self.ticket_repo.get_ticket_by_id(ticket_id)
        return {
            "id": ticket.id,
            "last_message_at": ticket.last_message_at,
            "last_message_content": ticket.last_message_content,
            "is_read": ticket.is_read
        }

    @atomic
    def assign_ticket_to_user(self, ticket_id: int, assignee_id: int):
        """
        มอบหมาย Ticket ให้กับผู้ใช้ (Agent) และเรียก NotificationService ถ้ามี
        """
        ticket = self.ticket_repo.get_ticket_by_id(ticket_id)
        if not ticket:
            raise TicketNotFoundError(f"Ticket with id={ticket_id} not found.")

        # ดึง User Object จาก ID
        User = get_user_model()
        try:
            assigned_user = User.objects.get(pk=assignee_id)
        except User.DoesNotExist:
            raise ValueError("User not found")

        updated_ticket = self.ticket_repo.update_ticket(ticket, {"assigned_to": assigned_user})

        if self.notification_service:
            self.notification_service.send_assignment_notification(
                ticket_id=updated_ticket.id,
                user_id=assigned_user.id
            )

        # คืน updated_ticket
        return updated_ticket

ticket_service = TicketService()