import os
import re

import testdata

from stockton import cli
from stockton.path import Filepath, Dirpath
from stockton.concur.formats.postfix import Main, SMTPd, Master
from stockton.interface import Postfix, Spam, Razor, Pyzor, DKIM, SRS, SMTP, DCC

from . import TestCase as BaseTestCase, Stockton


class TestCase(BaseTestCase):
#     def setUp(self):
#         super(TestCase, self).setUp()

    @classmethod
    def setup_env(cls):
        p = Postfix()
        if not p.main_f.exists():
            s = Stockton("prepare")
            r = s.run()

    def setup_domain(self, domain):
        s = Stockton("configure-recv")
        r = s.run("--mailserver=mail.example.com")

        s = Stockton("add-domain")
        r = s.run("{} --proxy-email=dest@dest.com".format(domain))


class PrepareTest(TestCase):
    @classmethod
    def setup_env(cls):
        cli.purge("postfix")

    def test_run(self):
        p = Postfix()
        d = p.config_d
        self.assertFalse(d.exists())

        s = Stockton("prepare")
        r = s.run()

        f = p.main_f
        self.assertTrue(f.exists())


class ConfigureRecvTest(TestCase):
    @classmethod
    def setup_env(cls):
        super(ConfigureRecvTest, cls).setup_env()

        s = Stockton("configure-recv")
        r = s.run("--mailserver=mail.example.com")

    def test_recv(self):
        p = Postfix()

        # make some changes to main
        m = p.main()
        m.update(
            ("myhostname", "foobar.com")
        )
        m.save()

        f = p.main_f
        self.assertTrue(f.contains("foobar.com"))

        # re-run
        s = Stockton("configure-recv")
        r = s.run("--mailserver=mail.example.com")
        f = p.main_f
        self.assertFalse(f.contains("foobar.com"))
        self.assertTrue(p.is_running())


class ConfigureSendTest(TestCase):
    def test_send(self):
        p = Postfix()
        s = Stockton("configure-send")
        r = s.run("--mailserver=mail.example.com")
        self.assertTrue(p.is_running())


class ConfigureDKIMTest(TestCase):
    @classmethod
    def setup_env(cls):
        dk = DKIM()
        dk.uninstall()
        super(ConfigureDKIMTest, cls).setup_env()

    def test_dkim(self):
        #self.test_recv() # we need a configured for receive postfix
        self.setup_domain("example.com")

        s = Stockton("configure-dkim")
        r = s.run()

        dk = DKIM()
        p = Postfix()
        self.assertTrue(dk.config_d.exists())
        self.assertEqual(1, dk.keytable_f.lc())
        self.assertEqual(1, dk.signingtable_f.lc())
        self.assertTrue("*.example.com" in dk.trustedhosts_f.lines())
        self.assertTrue(dk.is_running())
        self.assertTrue(p.is_running())


class ConfigureSRSTest(TestCase):
    def test_srs(self):
        s = Stockton("configure-srs")
        r = s.run("")

        sr = SRS()
        self.assertTrue(sr.is_running())


class ConfigureSpamLibsTest(TestCase):
    def test_dcc(self):
        sp = Spam()
        sp.uninstall()

        dcc = DCC()
        dcc.uninstall()
        self.assertFalse(dcc.exists())

        self.setup_domain("example.com")

        with self.assertRaises(RuntimeError):
            s = Stockton("configure-dcc")
            r = s.run()

        s = Stockton("lockdown-spam")
        r = s.run()

        s = Stockton("configure-dcc")
        r = s.run()

        self.assertTrue("DCC_CHECK" in dcc.test())

    def test_pyzor(self):
        sp = Spam()
        sp.uninstall()
        p = Pyzor()
        p.uninstall()
        self.assertFalse(p.exists())

        self.setup_domain("example.com")

        with self.assertRaises(RuntimeError):
            s = Stockton("configure-pyzor")
            r = s.run()

        s = Stockton("lockdown-spam")
        r = s.run()

        s = Stockton("configure-pyzor")
        r = s.run()

        self.assertTrue("pyzor is available" in p.test())

    def test_razor(self):
        sp = Spam()
        sp.uninstall()
        ra = Razor()
        ra.uninstall()
        self.assertFalse(ra.exists())

        self.setup_domain("example.com")

        with self.assertRaises(RuntimeError):
            s = Stockton("configure-razor")
            r = s.run()

        s = Stockton("lockdown-spam")
        r = s.run()

        s = Stockton("configure-razor")
        r = s.run()

        self.assertTrue("Found razorhome: {}".format(ra.home_d) in ra.test())


class DomainTest(TestCase):
#     @classmethod
#     def setup_env(cls):
#         dk = DKIM()
#         dk.uninstall()
#         super(DomainTest, cls).setup_env()

    def test_delete_domain(self):
        self.setup_domain("example.com")
        s = Stockton("configure_dkim")
        r = s.run()

        s = Stockton("add-domain")
        r = s.run("todelete.com --proxy-email=todelete@dest.com")

        s = Stockton("delete-domain")
        r = s.run("todelete.com")

        # verify
        af = Filepath("/etc/postfix/virtual/addresses/todelete.com")
        self.assertFalse(af.exists())

        df = Filepath("/etc/postfix/virtual/domains")
        self.assertFalse(df.contains("todelete.com"))

        opendkim_d = Dirpath("/etc/opendkim")
        signingtable_f = Filepath(opendkim_d, "SigningTable")
        self.assertFalse(signingtable_f.contains("todelete.com"))

        trustedhosts_f = Filepath(opendkim_d, "TrustedHosts")
        self.assertFalse(trustedhosts_f.contains("todelete.com"))

    def test_add_domain_proxy_file_smtp(self):
        self.setup_domain("example.com")
        s = Stockton("configure-send")
        r = s.run("--mailserver=mail.example.com")

        proxy_addresses = testdata.create_file("foo.com", [
            "one@foo.com                foo@dest1.com",
            "two@foo.com                foo@dest2.com",
            "three@foo.com              foo@dest3.com",
            "",
        ])

        s = Stockton("add-domain")
        r = s.run("foo.com --proxy-file={} --smtp-password=12345".format(proxy_addresses))

        cli.package("cyrus-clients-2.4") # for smtptest
        r = cli.run(
            "echo QUIT | smtptest -a smtp@foo.com -w 12345 -t /etc/postfix/certs/mail.example.com.pem -p 587 localhost",
            capture_output=True
        )
        self.assertRegexpMatches(r, "235[^A]+Authentication\s+successful")

    def test_update_domain_proxy(self):
        # to make sure this works completely, we completely remove postfix
        p = Postfix()
        p.reset()

        f = testdata.create_file("foo.com", [
            "one@foo.com                foo@dest.com",
            "",
        ])

        s = Stockton("add-domain")
        r = s.run("{} --proxy-file={}".format(f.name, f))
        m = Main(prototype_path=Main.dest_path)
        proxy_path = re.search("^hash:([/a-z]+?foo\.com)$", m["virtual_alias_maps"].val, re.M).group(1)
        proxy_path_f = Filepath(proxy_path)
        self.assertTrue("one@foo.com" in proxy_path_f.contents())

        s = Stockton("update-domain-proxy")
        f = testdata.create_file("foo.com", [
            "two@bar.com                bar@dest.com",
            "",
        ])
        r = s.run("{} --proxy-file={}".format(f.name, f))
        m = Main(prototype_path=Main.dest_path)
        proxy_path = re.search("^hash:([/a-z]+?foo\.com)$", m["virtual_alias_maps"].val, re.M).group(1)
        proxy_path_f = Filepath(proxy_path)
        self.assertFalse("one@foo.com" in proxy_path_f.contents())
        self.assertTrue("two@bar.com" in proxy_path_f.contents())

    def test_add_domain_proxy_file_no_smtp(self):

        # to make sure this works completely, we completely remove postfix
        p = Postfix()
        p.reset()

        s = Stockton("add-domain")
        proxy_domains = testdata.create_files({
            "foo.com": "\n".join([
                "one@foo.com                foo@dest.com",
                "two@foo.com                foo@dest.com",
                "three@foo.com              foo@dest.com",
                "",
            ]),
            "bar.com": "\n".join([
                "one@bar.com                bar@dest.com",
                "two@bar.com                bar@dest.com",
                "three@bar.com              bar@dest.com",
                "",
            ]),
        })

        for f in proxy_domains:
            r = s.run("{} --proxy-file={}".format(f.name, f))

        contents = "\n".join([
            "@foo.com                   foo@dest.com",
            "",
        ])
        f = testdata.create_file("foo.com", contents)

        r = s.run("{} --proxy-file={}".format(f.name, f))
        foo = Filepath("/etc/postfix/virtual/addresses/foo.com")
        self.assertEqual(contents, foo.contents())

        # Let's check structure because I was having a lot of problems with getting
        # the structure...

        # domains file should have 2 domains in it
        domains_f = Filepath("/etc/postfix/virtual/domains")
        self.assertEqual(2, domains_f.lc())
        self.assertTrue(domains_f.contains("^foo\.com$"))

        m = Main(prototype_path=Main.dest_path)
        # we can't guarrantee foo, bar order so we match one line at a time
        self.assertTrue(re.search("^hash:[/a-z]+?(bar|foo)\.com,", m["virtual_alias_maps"].val, re.M))
        self.assertTrue(re.search("^\s+hash:[/a-z]+?(bar|foo)\.com$", m["virtual_alias_maps"].val, re.M))

    def test_add_domains(self):
        s = Stockton("add-domains")

        # a file with different domains
        proxy_domains = testdata.create_files({
            "foo2.com.txt": [
                "one@foo3.com                foo@dest.com",
                "one@bar3.com                bar@dest.com",
                "",
            ],
        })
        with self.assertRaises(RuntimeError):
            r = s.run("--proxy-domains={}".format(proxy_domains))

        # 2 files with the same domain
        proxy_domains = testdata.create_files({
            "foo2.com.txt": [
                "one@foo2.com                foo@dest.com",
                "",
            ],
            "bar2.com": [
                "one@foo2.com                bar@dest.com",
                "",
            ],
        })
        with self.assertRaises(RuntimeError):
            r = s.run("--proxy-domains={}".format(proxy_domains))

        proxy_domains = testdata.create_files({
            "foo.com.txt": [
                "one@foo.com                foo@dest.com",
                "two@foo.com                foo@dest.com",
                "three@foo.com              foo@dest.com",
                "",
            ],
            "bar.com": [
                "one@bar.com                bar@dest.com",
                "two@bar.com                bar@dest.com",
                "three@bar.com              bar@dest.com",
                "",
            ],
            "che.org.txt": [
                "@che.org                   che@dest.com",
                "",
            ],
        })

        r = s.run("--proxy-domains={} --smtp-password=1234".format(proxy_domains))
        self.assertTrue("Adding domain foo.com" in r)
        self.assertTrue("Adding domain bar.com" in r)
        self.assertTrue("Adding domain che.org" in r)

    def test_add_domain_domain(self):
        """pass in the domain and the proxy"""
        s = Stockton("configure-dkim")
        r = s.run()

        p = Postfix()
        dk = DKIM()

        s = Stockton("add-domain")
        one_f = p.address("one.com")
        two_f = p.address("two.com")
        domains_f = p.domains_f

        r = s.run("one.com --proxy-email=one@dest.com")
        self.assertTrue(one_f.contains("^@one.com"))
        self.assertTrue(domains_f.contains("one.com"))

        r = s.run("two.com --proxy-email=two@dest.com")
        self.assertTrue(one_f.contains("^@one.com"))
        self.assertTrue(domains_f.contains("one.com"))
        self.assertTrue(two_f.contains("two@dest.com"))
        self.assertTrue(domains_f.contains("two.com"))

        opendkim_d = dk.config_d
        keytable_f = dk.keytable_f
        signingtable_f = dk.signingtable_f
        trustedhosts_f = dk.trustedhosts_f

        self.assertTrue(keytable_f.contains("one.com"))
        self.assertTrue(signingtable_f.contains("one.com"))
        self.assertTrue(keytable_f.contains("two.com"))
        self.assertTrue(signingtable_f.contains("two.com"))
        self.assertTrue("*.one.com" in trustedhosts_f.lines())
        self.assertTrue("*.two.com" in trustedhosts_f.lines())

    def test_add_domain_smtp(self):
        s = Stockton("configure-send")
        r = s.run("--mailserver=mail.example.com")

        s = Stockton("add-domain")
        r = s.run("one.com --proxy-email=one@dest.com --smtp-password=1234")

        cli.package("cyrus-clients-2.4") # for smtptest
        r = cli.run(
            "echo QUIT | smtptest -a smtp@one.com -w 1234 -t /etc/postfix/certs/mail.example.com.pem -p 587 localhost"
        )
        self.assertRegexpMatches(r, "235[^A]+Authentication\s+successful")

        r = cli.run(
            "echo QUIT | smtptest -a smtp@one.com -w 9876 -t /etc/postfix/certs/mail.example.com.pem -p 587 localhost"
        )
        self.assertRegexpMatches(r, "535[^E]+Error:\s+authentication\s+failed")

        r = cli.run(
            "echo QUIT | smtptest -a foo@one.com -w 1234 -t /etc/postfix/certs/mail.example.com.pem -p 587 localhost"
        )
        self.assertRegexpMatches(r, "535[^E]+Error:\s+authentication\s+failed")




#         cli.package("cyrus-clients-2.4") # for smtptest
#
#         s = Stockton("configure-send")
#         arg_str = "example.com --mailserver=mail.example.com --smtp-password=1234"
#         r = s.run(arg_str)
# 
#         r = cli.run(
#             "echo QUIT | smtptest -a smtp@example.com -w 1234 -t /etc/postfix/certs/mail.example.com.pem -p 587 localhost",
#             capture_output=True
#         )
#         self.assertRegexpMatches(r, "235[^A]+Authentication\s+successful")
# 
#         r = cli.run(
#             "echo QUIT | smtptest -a smtp@example.com -w 9876 -t /etc/postfix/certs/mail.example.com.pem -p 587 localhost",
#             capture_output=True
#         )
#         self.assertRegexpMatches(r, "535[^E]+Error:\s+authentication\s+failed")
# 
#         r = cli.run(
#             "echo QUIT | smtptest -a foo@example.com -w 1234 -t /etc/postfix/certs/mail.example.com.pem -p 587 localhost",
#             capture_output=True
#         )
#         self.assertRegexpMatches(r, "535[^E]+Error:\s+authentication\s+failed")
# 
# #         r = cli.run(
# #             "echo QUIT | smtptest -a smtp -w 1234 -t /etc/postfix/certs/example.com.pem -p 587 localhost",
# #             capture_output=True
# #         )
# #         self.assertRegexpMatches(r, "535[^E]+Error:\s+authentication\s+failed")


class LockdownTest(TestCase):
    def test_all(self):
        s = Stockton("lockdown")
        r = s.run("--mailserver=mail.example.com")

        p = Postfix()
        sp = Spam()
        self.assertTrue(p.is_running())
        self.assertTrue(p.helo_f.exists())
        self.assertTrue(sp.is_running())

    def test_spam(self):
        s = Stockton("lockdown-spam")
        r = s.run()

        sp = Spam()
        self.assertTrue(sp.is_running())

    def test_postfix(self):
        self.setup_domain("example.com")

        s = Stockton("lockdown-postfix")
        r = s.run("--mailserver=mail.example.com")

        p = Postfix()
        self.assertTrue(p.is_running())
        self.assertTrue(p.helo_f.exists())


class InstallationTest(TestCase):
    @classmethod
    def setup_env(cls):
        #super(ConfigureDKIMTest, cls).setup_env()
        pass

    def test_idempotence(self):
        self.test_uninstall()
        self.test_install()

        p = Postfix()
        dk = DKIM()
        sp = Spam()
        sm = SMTP()

        main_hash = p.main_f.checksum
        master_hash = p.master_f.checksum
        dk_hash = dk.config_f.checksum
        sp_config_hash = sp.config_f.checksum
        sp_local_hash = sp.local_f.checksum
        sm_config_hash = sm.config_f.checksum

        self.test_install()

        # just to make sure there is no caching issues
        p = Postfix()
        dk = DKIM()
        sp = Spam()
        sm = SMTP()

        self.assertEqual(main_hash, p.main_f.checksum)
        self.assertEqual(master_hash, p.master_f.checksum)
        self.assertEqual(dk_hash, dk.config_f.checksum)
        self.assertEqual(sp_config_hash, sp.config_f.checksum)
        self.assertEqual(sp_local_hash, sp.local_f.checksum)
        self.assertEqual(sm_config_hash, sm.config_f.checksum)

    def test_uninstall(self):
        s = Stockton("uninstall")
        r = s.run()

        p = Postfix()
        self.assertFalse(p.is_running())
        self.assertFalse(p.exists())

        dk = DKIM()
        self.assertFalse(dk.is_running())
        self.assertFalse(dk.exists())

        sr = SRS()
        self.assertFalse(sr.is_running())

        sp = Spam()
        self.assertFalse(sp.is_running())
        self.assertFalse(sp.exists())

        sm = SMTP()
        self.assertFalse(sm.exists())

    def test_install_proxy_domains(self):
        s = Stockton("install")
        proxy_domains = testdata.create_files({
            "pd1.com.txt": [
                "one@pd1.com                foo@dest.com",
                "",
            ],
            "pd2.com": [
                "one@pd2.com                bar@dest.com",
                "",
            ],
        })

        r = s.run("--mailserver={} --proxy-domains={} --smtp-password=1234".format(
            "mail.example.com",
            proxy_domains
        ))
        # if there was no error than yay, it worked

    def test_install(self):
        s = Stockton("install")
        arg_str = " ".join([
            "--mailserver=mail.example.com",
            "--smtp-password=1234",
            "--proxy-email=d@dest.com",
            "front.com"
        ])
        r = s.run(arg_str)

        p = Postfix()
        self.assertTrue(p.is_running())

        dk = DKIM()
        self.assertTrue(dk.is_running())

        sr = SRS()
        self.assertTrue(sr.is_running())

        sp = Spam()
        self.assertTrue(sp.is_running())

