"""Toolkit for the workspace domain (email, calendar, cloud drive)."""

from typing import List, Dict

from murmur.domains.workspace.data_model import (
    WorkspaceDB,
    WorkspaceEmail,
    WorkspaceCalendarEvent,
    WorkspaceFile,
)
from murmur.environment.toolkit import ToolKitBase, ToolType, is_tool


class WorkspaceTools(ToolKitBase):
    """Subset of workspace tools modeled after agentdojo workspace suite."""

    db: WorkspaceDB

    def __init__(self, db: WorkspaceDB) -> None:
        super().__init__(db)

    # Email tools
    @is_tool(ToolType.READ)
    def get_unread_emails(self) -> List[WorkspaceEmail]:
        """Get the list of unread emails.

        Returns:
            List of unread emails.
        """
        return [email for email in self.db.inbox.emails.values() if not email.read]

    @is_tool(ToolType.WRITE)
    def send_email(self, recipients: List[str], subject: str, body: str) -> WorkspaceEmail:
        """Send an email to the recipients.

        Args:
            recipients: List of recipients.
            subject: Subject of the email.
            body: Body of the email.

        Returns:
            The sent email.
        """
        next_id = str(max([int(k) for k in self.db.inbox.emails.keys()] + [0]) + 1)
        email = WorkspaceEmail(id=next_id, sender=self.db.inbox.account_email, recipients=recipients, subject=subject, body=body, read=True)
        self.db.inbox.emails[next_id] = email
        return email

    @is_tool(ToolType.READ)
    def search_emails(self, query: str, sender: str | None = None) -> List[WorkspaceEmail]:
        """Search for emails body or subject for a given sender.

        Args:
            query: Query to search for.
            sender: Sender of the emails.

        Returns:
            List of emails matching the query.
        """
        emails = list(self.db.inbox.emails.values())
        if sender is not None:
            emails = [e for e in emails if e.sender == sender]
        matches = [e for e in emails if query.lower() in e.subject.lower() or query.lower() in e.body.lower()]
        if len(matches) == 0:
            raise ValueError("No emails found. Try with a different query.")
        return matches

    @is_tool(ToolType.WRITE)
    def delete_email(self, email_id: str) -> None:
        """Delete an email.

        Args:
            email_id: ID of the email to delete.
        """
        if email_id not in self.db.inbox.emails:
            raise ValueError(f"Email with ID '{email_id}' not found.")
        self.db.inbox.trash[email_id] = self.db.inbox.emails[email_id]
        del self.db.inbox.emails[email_id]

    @is_tool(ToolType.READ)
    def get_sent_emails(self) -> List[WorkspaceEmail]:
        """Get the list of sent emails.

        Returns:
            List of sent emails.
        """
        return [e for e in self.db.inbox.emails.values() if e.sender == self.db.inbox.account_email]

    @is_tool(ToolType.READ)
    def get_received_emails(self) -> List[WorkspaceEmail]:
        """Get the list of received emails.

        Returns:
            List of received emails.
        """
        return [e for e in self.db.inbox.emails.values() if self.db.inbox.account_email in e.recipients]

    @is_tool(ToolType.READ)
    def get_draft_emails(self) -> List[WorkspaceEmail]:
        """Get the list of draft emails.

        Returns:
            List of draft emails.
        """
        return []

    @is_tool(ToolType.READ)
    def search_contacts_by_name(self, query: str) -> List[Dict[str, str]]:
        """Search for contacts by name.

        Args:
            query: Query to search for.

        Returns:
            List of contacts matching the query.
        """
        # Derive contacts from emails
        contacts: Dict[str, str] = {}
        def add(email: str):
            if email not in contacts:
                name = email.split('@')[0].replace('.', ' ').title()
                contacts[email] = name
        for e in self.db.inbox.emails.values():
            add(e.sender)
            for r in e.recipients:
                add(r)
        matches: List[Dict[str, str]] = [{"email": k, "name": v} for k, v in contacts.items() if query.lower() in v.lower()]
        if not matches:
            raise ValueError(f"Contact with name '{query}' not found.")
        return matches

    @is_tool(ToolType.READ)
    def search_contacts_by_email(self, query: str) -> List[Dict[str, str]]:
        """Search for contacts by email.

        Args:
            query: Query to search for.

        Returns:
            List of contacts matching the query.
        """
        contacts: Dict[str, str] = {}
        def add(email: str):
            if email not in contacts:
                name = email.split('@')[0].replace('.', ' ').title()
                contacts[email] = name
        for e in self.db.inbox.emails.values():
            add(e.sender)
            for r in e.recipients:
                add(r)
        matches: List[Dict[str, str]] = [{"email": k, "name": v} for k, v in contacts.items() if query.lower() in k.lower()]
        if not matches:
            raise ValueError(f"Contact with email '{query}' not found.")
        return matches

    # Calendar tools
    @is_tool(ToolType.READ)
    def get_day_calendar_events(self, day: str) -> List[WorkspaceCalendarEvent]:
        """Get the list of calendar events for a given day.

        Args:
            day: Day to get events for.

        Returns:
            List of calendar events for the day.
        """
        return [e for e in self.db.calendar.events.values() if e.start_time.startswith(day)]

    @is_tool(ToolType.READ)
    def get_current_day(self) -> str:
        """Get the current day.

        Returns:
            The current day.
        """
        return self.db.calendar.current_day

    @is_tool(ToolType.READ)
    def search_calendar_events(self, query: str, date: str | None = None) -> List[WorkspaceCalendarEvent]:
        """Search for calendar events by title or description.

        Args:
            query: Query to search for.
            date: Date to get events for. (YYYY-MM-DD)

        Returns:
            List of calendar events matching the query.
        """
        events = self.db.calendar.events.values()
        if date is not None:
            events = [e for e in events if e.start_time.startswith(date)]
        matches = [e for e in events if (e.title and query.lower() in e.title.lower()) or (e.description and query.lower() in e.description.lower())]
        if len(matches) == 0:
            raise ValueError("No events found. Try with a different query.")
        return matches

    @is_tool(ToolType.WRITE)
    def create_calendar_event(
        self,
        title: str,
        start_time: str,
        end_time: str,
        description: str = "",
        participants: List[str] | None = None,
        location: str | None = None,
    ) -> WorkspaceCalendarEvent:
        """Create a calendar event.

        Args:
            title: Title of the event.
            start_time: Start time of the event. (YYYY-MM-DD HH:MM)
            end_time: End time of the event. (YYYY-MM-DD HH:MM)
            description: Description of the event.
            participants: List of participants.
            location: Location of the event.

        Returns:
            The created calendar event.
        """
        if participants is None:
            participants = []
        next_id = str(max([int(k) for k in self.db.calendar.events.keys()] + [0]) + 1)
        event = WorkspaceCalendarEvent(
            id=next_id,
            title=title,
            description=description,
            start_time=start_time,
            end_time=end_time,
            location=location,
            participants=participants,
        )
        self.db.calendar.events[next_id] = event
        return event

    @is_tool(ToolType.WRITE)
    def reschedule_calendar_event(self, event_id: str, new_start_time: str, new_end_time: str | None = None) -> WorkspaceCalendarEvent:
        """Reschedule a calendar event.

        Args:
            event_id: ID of the event to reschedule.
            new_start_time: New start time of the event. (YYYY-MM-DD HH:MM)
            new_end_time: New end time of the event. (YYYY-MM-DD HH:MM)

        Returns:
            The rescheduled calendar event.
        """
        if event_id not in self.db.calendar.events:
            raise ValueError(f"Event with ID '{event_id}' not found.")
        ev = self.db.calendar.events[event_id]
        ev.start_time = new_start_time
        if new_end_time is not None:
            ev.end_time = new_end_time
        return ev

    @is_tool(ToolType.WRITE)
    def add_calendar_event_participants(self, event_id: str, participants: List[str]) -> WorkspaceCalendarEvent:
        """Add participants to a calendar event.

        Args:
            event_id: ID of the event to add participants to.
            participants: List of participants to add.

        Returns:
            The calendar event with the added participants.
        """
        if event_id not in self.db.calendar.events:
            raise ValueError(f"Event with ID '{event_id}' not found.")
        ev = self.db.calendar.events[event_id]
        for p in participants:
            if p not in ev.participants:
                ev.participants.append(p)
        return ev

    @is_tool(ToolType.WRITE)
    def cancel_calendar_event(self, event_id: str) -> str:
        """Cancel a calendar event.

        Args:
            event_id: ID of the event to cancel.

        Returns:
            The canceled calendar event.
        """
        if event_id not in self.db.calendar.events:
            raise ValueError(f"Event with ID '{event_id}' not found.")
        # We don't model status enum here; just acknowledge
        return f"Event with ID {event_id} has been canceled and participants have been notified."

    # Drive tools
    @is_tool(ToolType.READ)
    def list_files(self) -> List[WorkspaceFile]:
        """List all files in the cloud drive.

        Returns:
            List of files in the cloud drive.
        """
        return list(self.db.drive.files.values())

    @is_tool(ToolType.WRITE)
    def create_file(self, filename: str, content: str) -> WorkspaceFile:
        """Create a file in the cloud drive.

        Args:
            filename: Name of the file.
            content: Content of the file.

        Returns:
            The created file.
        """
        next_id = str(max([int(k) for k in self.db.drive.files.keys()] + [0]) + 1)
        file = WorkspaceFile(id=next_id, filename=filename, content=content, size=len(content))
        self.db.drive.files[next_id] = file
        return file

    @is_tool(ToolType.WRITE)
    def delete_file(self, file_id: str) -> None:
        """Delete a file in the cloud drive.

        Args:
            file_id: ID of the file to delete.
        """
        if file_id not in self.db.drive.files:
            raise ValueError(f"File with ID '{file_id}' not found.")
        del self.db.drive.files[file_id]

    @is_tool(ToolType.READ)
    def get_file_by_id(self, file_id: str) -> WorkspaceFile:
        """Get a file by its ID.

        Args:
            file_id: ID of the file to get.

        Returns:
            The file.
        """
        if file_id not in self.db.drive.files:
            raise ValueError(f"File with ID '{file_id}' not found.")
        return self.db.drive.files[file_id]

    @is_tool(ToolType.WRITE)
    def share_file(self, file_id: str, email: str, permission: str) -> WorkspaceFile:
        """Share a file with an email.

        Args:
            file_id: ID of the file to share.
            email: Email to share the file with.
            permission: Permission level "r" for read, "rw" for read and write, "w" for write.

        Returns:
            The shared file.
        """
        file = self.get_file_by_id(file_id)
        file.shared_with[email] = permission
        return file

    @is_tool(ToolType.WRITE)
    def append_to_file(self, file_id: str, content: str) -> WorkspaceFile:
        """Append to a file.

        Args:
            file_id: ID of the file to append to.
            content: Content to append to the file.

        Returns:
            The file with the appended content.
        """
        file = self.get_file_by_id(file_id)
        file.content += content
        return file

    @is_tool(ToolType.READ)
    def search_files_by_filename(self, filename: str) -> List[WorkspaceFile]:
        """Search for files by name of the file.

        Args:
            filename: Filename to search for.

        Returns:
            List of files matching the filename.
        """
        return [f for f in self.db.drive.files.values() if filename.lower() in f.filename.lower()]

    @is_tool(ToolType.READ)
    def search_files(self, query: str) -> List[WorkspaceFile]:
        """Search in a file using some keywords.

        Args:
            query: Query to search for.

        Returns:
            List of files matching the query.
        """
        matches = [f for f in self.db.drive.files.values() if query.lower() in f.content.lower()]
        if len(matches) == 0:
            raise ValueError(f"No files found with the given query ('{query}'). Try a different search term.")
        return matches


