"""
In-Game Mail system

Evennia Contribution - grungies1138 2016

A simple Brandymail style @mail system that uses the Msg class from Evennia Core.

Installation:
    import CmdMail from this module (from evennia.contrib.mail import CmdMail),
    and add into the default Account or Character command set (self.add(CmdMail)).

"""

import re
from evennia import ObjectDB, AccountDB
from evennia import default_cmds
from evennia.utils import create, evtable, make_iter
from evennia.comms.models import Msg


_HEAD_CHAR = "|015-|n"
_SUB_HEAD_CHAR = "-"
_WIDTH = 78


class CmdMail(default_cmds.MuxCommand):
    """
    Commands that allow either IC or OOC communications

    Usage:
        @mail       - Displays all the mail an account has in their mailbox

        @mail <#>   - Displays a specific message

        @mail <accounts>=<subject>/<message>
                - Sends a message to the comma separated list of accounts.

        @mail/delete <#> - Deletes a specific message

        @mail/forward <account list>=<#>[/<Message>]
                - Forwards an existing message to the specified list of accounts,
                  original message is delivered with optional Message prepended.

        @mail/reply <#>=<message>
                - Replies to a message #.  Prepends message to the original
                  message text.
    Switches:
        delete  - deletes a message
        forward - forward a received message to another object with an optional message attached.
        reply   - Replies to a received message, appending the original message to the bottom.

    Examples:
        @mail 2
        @mail Griatch=New mail/Hey man, I am sending you a message!
        @mail/delete 6
        @mail/forward feend78 Griatch=4/You guys should read this.
        @mail/reply 9=Thanks for the info!

    """
    key = "@mail"
    aliases = ["mail"]
    lock = "cmd:all()"
    help_category = "General"

    def search_targets(self, namelist):
        """
        Search a list of targets of the same type as caller.

        Args:
            caller (Object or Account): The type of object to search.
            namelist (list): List of strings for objects to search for.

        Returns:
            targetlist (list): List of matches, if any.

        """
        nameregex = r"|".join(r"^%s$" % re.escape(name) for name in make_iter(namelist))
        if hasattr(self.caller, "account") and self.caller.account:
            matches = list(ObjectDB.objects.filter(db_key__iregex=nameregex))
        else:
            matches = list(AccountDB.objects.filter(username__iregex=nameregex))
        return matches

    def get_all_mail(self):
        """
        Returns a list of all the messages where the caller is a recipient.

        Returns:
            messages (list): list of Msg objects.

        """
        # mail_messages = Msg.objects.get_by_tag(category="mail")
        # messages = []
        try:
            account = self.caller.account
        except AttributeError:
            account = self.caller
        messages = Msg.objects.get_by_tag(category="mail").filter(db_receivers_accounts=account)
        return messages

    def send_mail(self, recipients, subject, message, caller):
        """
        Function for sending new mail.  Also useful for sending notifications from objects or systems.

        Args:
            recipients (list): list of Account or character objects to receive the newly created mails.
            subject (str): The header or subject of the message to be delivered.
            message (str): The body of the message being sent.
            caller (obj): The object (or Account or Character) that is sending the message.

        """
        for recipient in recipients:
            recipient.msg("You have received a new @mail from %s" % caller)
            new_message = create.create_message(self.caller, message, receivers=recipient, header=subject)
            new_message.tags.add("U", category="mail")

        if recipients:
            caller.msg("You sent your message.")
            return
        else:
            caller.msg("No valid accounts found.  Cannot send message.")
            return

    def func(self):
        subject = ""
        body = ""

        if self.switches or self.args:
            if "delete" in self.switches:
                try:
                    if not self.lhs:
                        self.caller.msg("No Message ID given.  Unable to delete.")
                        return
                    else:
                        all_mail = self.get_all_mail()
                        mind_max = max(0, all_mail.count() - 1)
                        mind = max(0, min(mind_max, int(self.lhs) - 1))
                        if all_mail[mind]:
                            all_mail[mind].delete()
                            self.caller.msg("Message %s deleted" % self.lhs)
                        else:
                            raise IndexError
                except IndexError:
                    self.caller.msg("That message does not exist.")
                except ValueError:
                    self.caller.msg("Usage: @mail/delete <message ID>")
            elif "forward" in self.switches:
                try:
                    if not self.rhs:
                        self.caller.msg("Cannot forward a message without an account list.  Please try again.")
                        return
                    elif not self.lhs:
                        self.caller.msg("You must define a message to forward.")
                        return
                    else:
                        all_mail = self.get_all_mail()
                        mind_max = max(0, all_mail.count() - 1)
                        if "/" in self.rhs:
                            message_number, message = self.rhs.split("/", 1)
                            mind = max(0, min(mind_max, int(message_number) - 1))

                            if all_mail[mind]:
                                old_message = all_mail[mind]

                                self.send_mail(self.search_targets(self.lhslist), "FWD: " + old_message.header,
                                               message + "\n---- Original Message ----\n" + old_message.message,
                                               self.caller)
                                self.caller.msg("Message forwarded.")
                            else:
                                raise IndexError
                        else:
                            mind = max(0, min(mind_max, int(self.rhs) - 1))
                            if all_mail[mind]:
                                old_message = all_mail[mind]
                                self.send_mail(self.search_targets(self.lhslist), "FWD: " + old_message.header,
                                               "\n---- Original Message ----\n" + old_message.message, self.caller)
                                self.caller.msg("Message forwarded.")
                                old_message.tags.remove("u", category="mail")
                                old_message.tags.add("f", category="mail")
                            else:
                                raise IndexError
                except IndexError:
                    self.caller.msg("Message does not exixt.")
                except ValueError:
                    self.caller.msg("Usage: @mail/forward <account list>=<#>[/<Message>]")
            elif "reply" in self.switches:
                try:
                    if not self.rhs:
                        self.caller.msg("You must define a message to reply to.")
                        return
                    elif not self.lhs:
                        self.caller.msg("You must supply a reply message")
                        return
                    else:
                        all_mail = self.get_all_mail()
                        mind_max = max(0, all_mail.count() - 1)
                        mind = max(0, min(mind_max, int(self.lhs) - 1))
                        if all_mail[mind]:
                            old_message = all_mail[mind]
                            self.send_mail(old_message.senders, "RE: " + old_message.header,
                                           self.rhs + "\n---- Original Message ----\n" + old_message.message, self.caller)
                            old_message.tags.remove("u", category="mail")
                            old_message.tags.add("r", category="mail")
                            return
                        else:
                            raise IndexError
                except IndexError:
                    self.caller.msg("Message does not exist.")
                except ValueError:
                    self.caller.msg("Usage: @mail/reply <#>=<message>")
            else:
                # normal send
                if self.rhs:
                    if "/" in self.rhs:
                        subject, body = self.rhs.split("/", 1)
                    else:
                        body = self.rhs
                    self.send_mail(self.search_targets(self.lhslist), subject, body, self.caller)
                else:
                    all_mail = self.get_all_mail()
                    mind_max = max(0, all_mail.count() - 1)
                    try:
                        mind = max(0, min(mind_max, int(self.lhs) - 1))
                        message = all_mail[mind]
                    except (ValueError, IndexError):
                        self.caller.msg("'%s' is not a valid mail id." % self.lhs)
                        return

                    messageForm = []
                    if message:
                        messageForm.append(_HEAD_CHAR * _WIDTH)
                        messageForm.append("|wFrom:|n %s" % (message.senders[0].key))
                        messageForm.append("|wSent:|n %s" % message.db_date_created.strftime("%m/%d/%Y %H:%M:%S"))
                        messageForm.append("|wSubject:|n %s" % message.header)
                        messageForm.append(_SUB_HEAD_CHAR * _WIDTH)
                        messageForm.append(message.message)
                        messageForm.append(_HEAD_CHAR * _WIDTH)
                    self.caller.msg("\n".join(messageForm))
                    message.tags.remove("u", category="mail")
                    message.tags.add("o", category="mail")

        else:
            messages = self.get_all_mail()

            if messages:
                table = evtable.EvTable("|wID:|n", "|wFrom:|n", "|wSubject:|n", "|wDate:|n", "|wSta:|n",
                                        table=None, border="header", header_line_char=_SUB_HEAD_CHAR, width=_WIDTH)
                index = 1
                for message in messages:
                    table.add_row(index, message.senders[0], message.header,
                                  message.db_date_created.strftime("%m/%d/%Y"),
                                  str(message.db_tags.last().db_key.upper()))
                    index += 1

                table.reformat_column(0, width=6)
                table.reformat_column(1, width=18)
                table.reformat_column(2, width=34)
                table.reformat_column(3, width=13)
                table.reformat_column(4, width=7)

                self.caller.msg(_HEAD_CHAR * _WIDTH)
                self.caller.msg(unicode(table))
                self.caller.msg(_HEAD_CHAR * _WIDTH)
            else:
                self.caller.msg("There are no messages in your inbox.")
