# Copyright (C) 2005-2008, 2010 by Canonical Ltd
#   Authors: Robert Collins <robert.collins@canonical.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

from unittest import TestLoader

from .... import config, tests
from ....bzr.bzrdir import BzrDir
from ....tests import TestCaseInTempDir
from ..emailer import EmailSender


def test_suite():
    return TestLoader().loadTestsFromName(__name__)


sample_config = (
    b"[DEFAULT]\n"
    b"post_commit_to=demo@example.com\n"
    b"post_commit_sender=Sample <foo@example.com>\n"
    b"revision_mail_headers=X-Cheese: to the rescue!\n"
)

unconfigured_config = b"[DEFAULT]\n" b"email=Robert <foo@example.com>\n"

sender_configured_config = (
    b"[DEFAULT]\n" b"post_commit_sender=Sample <foo@example.com>\n"
)

to_configured_config = b"[DEFAULT]\n" b"post_commit_to=Sample <foo@example.com>\n"

multiple_to_configured_config = (
    b"[DEFAULT]\n"
    b"post_commit_sender=Sender <from@example.com>\n"
    b"post_commit_to=Sample <foo@example.com>, Other <baz@bar.com>\n"
)

customized_mail_config = (
    b"[DEFAULT]\n"
    b"post_commit_to=demo@example.com\n"
    b"post_commit_sender=Sample <foo@example.com>\n"
    b"post_commit_subject=[commit] $message\n"
    b"post_commit_body='''$committer has committed "
    b"revision 1 at $url.\n\n'''\n"
)

push_config = (
    b"[DEFAULT]\n" b"post_commit_to=demo@example.com\n" b"post_commit_push_pull=True\n"
)

with_url_config = (
    b"[DEFAULT]\n"
    b"post_commit_url=http://some.fake/url/\n"
    b"post_commit_to=demo@example.com\n"
    b"post_commit_sender=Sample <foo@example.com>\n"
)

# FIXME: this should not use a literal log, rather grab one from breezy.log
sample_log = (
    "------------------------------------------------------------\n"
    "revno: 1\n"
    "revision-id: %s\n"
    "committer: Sample <john@example.com>\n"
    "branch nick: work\n"
    "timestamp: Thu 1970-01-01 00:00:01 +0000\n"
    "message:\n"
    "  foo bar baz\n"
    "  fuzzy\n"
    "  wuzzy\n"
)


class TestGetTo(TestCaseInTempDir):
    def test_body(self):
        sender, revid = self.get_sender()
        self.assertEqual(
            "At {}\n\n{}".format(sender.url(), sample_log % revid.decode("utf-8")),
            sender.body(),
        )

    def test_custom_body(self):
        sender, revid = self.get_sender(customized_mail_config)
        self.assertEqual(
            "%s has committed revision 1 at %s.\n\n%s"
            % (
                sender.revision.committer,
                sender.url(),
                sample_log % revid.decode("utf-8"),
            ),
            sender.body(),
        )

    def test_command_line(self):
        sender, revid = self.get_sender()
        self.assertEqual(
            ["mail", "-s", sender.subject(), "-a", "From: " + sender.from_address()]
            + sender.to(),
            sender._command_line(),
        )

    def test_to(self):
        sender, revid = self.get_sender()
        self.assertEqual(["demo@example.com"], sender.to())

    def test_from(self):
        sender, revid = self.get_sender()
        self.assertEqual("Sample <foo@example.com>", sender.from_address())

    def test_from_default(self):
        sender, revid = self.get_sender(unconfigured_config)
        self.assertEqual("Robert <foo@example.com>", sender.from_address())

    def test_should_send(self):
        sender, revid = self.get_sender()
        self.assertEqual(True, sender.should_send())

    def test_should_not_send(self):
        sender, revid = self.get_sender(unconfigured_config)
        self.assertEqual(False, sender.should_send())

    def test_should_not_send_sender_configured(self):
        sender, revid = self.get_sender(sender_configured_config)
        self.assertEqual(False, sender.should_send())

    def test_should_not_send_to_configured(self):
        sender, revid = self.get_sender(to_configured_config)
        self.assertEqual(True, sender.should_send())

    def test_send_to_multiple(self):
        sender, revid = self.get_sender(multiple_to_configured_config)
        self.assertEqual(
            ["Sample <foo@example.com>", "Other <baz@bar.com>"], sender.to()
        )
        self.assertEqual(
            ["Sample <foo@example.com>", "Other <baz@bar.com>"],
            sender._command_line()[-2:],
        )

    def test_url_set(self):
        sender, revid = self.get_sender(with_url_config)
        self.assertEqual(sender.url(), "http://some.fake/url/")

    def test_public_url_set(self):
        config = b"[DEFAULT]\n" b"public_branch=http://the.publication/location/\n"
        sender, revid = self.get_sender(config)
        self.assertEqual(sender.url(), "http://the.publication/location/")

    def test_url_precedence(self):
        config = (
            b"[DEFAULT]\n"
            b"post_commit_url=http://some.fake/url/\n"
            b"public_branch=http://the.publication/location/\n"
        )
        sender, revid = self.get_sender(config)
        self.assertEqual(sender.url(), "http://some.fake/url/")

    def test_url_unset(self):
        sender, revid = self.get_sender()
        self.assertEqual(sender.url(), sender.branch.base)

    def test_subject(self):
        sender, revid = self.get_sender()
        self.assertEqual(
            "Rev 1: foo bar baz in %s" % sender.branch.base, sender.subject()
        )

    def test_custom_subject(self):
        sender, revid = self.get_sender(customized_mail_config)
        self.assertEqual(
            "[commit] %s" % sender.revision.get_summary(), sender.subject()
        )

    def test_diff_filename(self):
        sender, revid = self.get_sender()
        self.assertEqual("patch-1.diff", sender.diff_filename())

    def test_headers(self):
        sender, revid = self.get_sender()
        self.assertEqual({"X-Cheese": "to the rescue!"}, sender.extra_headers())

    def get_sender(self, text=sample_config):
        my_config = config.MemoryStack(text)
        self.branch = BzrDir.create_branch_convenience(".")
        tree = self.branch.controldir.open_workingtree()
        revid = tree.commit(
            "foo bar baz\nfuzzy\nwuzzy",
            allow_pointless=True,
            timestamp=1,
            timezone=0,
            committer="Sample <john@example.com>",
        )
        sender = EmailSender(self.branch, revid, my_config)
        # This is usually only done after the EmailSender has locked the branch
        # and repository during send(), however, for testing, we need to do it
        # earlier, since send() is not called.
        sender._setup_revision_and_revno()
        return sender, revid


class TestEmailerWithLocal(tests.TestCaseWithTransport):
    """Test that Emailer will use a local branch if supplied."""

    def test_local_has_revision(self):
        master_tree = self.make_branch_and_tree("master")
        self.build_tree(["master/a"])
        master_tree.add("a")
        master_tree.commit("a")

        child_tree = master_tree.controldir.sprout("child").open_workingtree()
        child_tree.branch.bind(master_tree.branch)

        self.build_tree(["child/b"])
        child_tree.add(["b"])
        revision_id = child_tree.commit("b")

        sender = EmailSender(
            master_tree.branch,
            revision_id,
            master_tree.branch.get_config(),
            local_branch=child_tree.branch,
        )

        # Make sure we are using the 'local_branch' repository, and not the
        # remote one.
        self.assertIs(child_tree.branch.repository, sender.repository)

    def test_local_missing_revision(self):
        master_tree = self.make_branch_and_tree("master")
        self.build_tree(["master/a"])
        master_tree.add("a")
        master_tree.commit("a")

        child_tree = master_tree.controldir.sprout("child").open_workingtree()
        child_tree.branch.bind(master_tree.branch)

        self.build_tree(["master/c"])
        master_tree.add(["c"])
        revision_id = master_tree.commit("c")

        self.assertFalse(child_tree.branch.repository.has_revision(revision_id))
        sender = EmailSender(
            master_tree.branch,
            revision_id,
            master_tree.branch.get_config(),
            local_branch=child_tree.branch,
        )
        # We should be using the master repository here, because the child
        # repository doesn't contain the revision.
        self.assertIs(master_tree.branch.repository, sender.repository)
