#!/usr/bin/env python3

import copy
import os
import subprocess
import sys
import unittest
import shutil
import io

FIXTURES = os.path.join(os.getcwd(), "tests/fixtures")
WORKSPACE = os.path.abspath(os.getenv("WORKSPACE", "/tmp"))
FAKE_BIN = os.path.join(WORKSPACE, "bin")
FAKE_PATH = "%s:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin" % FAKE_BIN
FAKE_CMD_LIST = os.path.join(WORKSPACE, "cmd_list")
FAKE_RUN_DIR = os.path.join(WORKSPACE, "run/kamailio")

FAKE_KAMCMD_PS = """
25474    main process - attendant
25476   udp receiver child=0 sock=127.0.0.1:5062
25477   slow timer
25478   timer
25479   secondary timer
25480   TIMER NH
25481   ctl handler
25482   Dialog Clean Timer
25483   MI FIFO
25484   tcp receiver (generic) child=0
25485   tcp main process
"""

FAKE_KAMCMD_CMD = """
WORKSPACE="%s"
if [ "$1" = "-s" ] ; then
  socket=$(echo $2|sed s#${WORKSPACE}##)
  set -- "$1" "${socket}" "$3"
fi
echo kamcmd $* >> %s
if [ "$3" = "ps" ] ; then
cat <<EOF
%sEOF
fi
"""


def executeAndReturnOutput(command, env):
    p = subprocess.Popen(
        command,
        encoding="utf-8",
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        env=env,
    )
    stdoutdata, stderrdata = p.communicate()
    print(stdoutdata, file=sys.stdout)
    print(stderrdata, file=sys.stderr)
    return p.returncode, stdoutdata, stderrdata


command = [
    "env",
    "PATH=%s" % FAKE_PATH,
    "/bin/bash",
    "./ngcp-kamcmd",
]


def create_prog(filename, command):
    """Create test program.

    :param unicode filename: destination filename
    :param unicode command: command to write to test program
    """
    with io.open(filename, "w", encoding="utf-8") as fp:
        fp.write("#!/bin/bash\n%s\n" % (command,))
        os.fchmod(fp.fileno(), 0o755)


class TestCommandLine(unittest.TestCase):

    def _create_kamcmd(self):
        create_prog(
            os.path.join(FAKE_BIN, "kamcmd"),
            FAKE_KAMCMD_CMD % (WORKSPACE, FAKE_CMD_LIST, FAKE_KAMCMD_PS),
        )
        create_prog(
            os.path.join(FAKE_BIN, "kill"),
            "echo kill $* >> %s" % FAKE_CMD_LIST,
        )

    def _create_socket(self, name):
        filename = os.path.join(self.env["RUN_DIR"], name)
        with io.open(filename, "w") as fp:
            fp.write("fake socket")

    def checkCmd(self, f):
        res = executeAndReturnOutput(["diff", "-uN", FAKE_CMD_LIST, f], None)
        self.assertEqual(res[0], 0, res[1])

    def setUp(self):
        self.command = copy.deepcopy(command)
        self.env = {
            "SHARE_DIR": "kamcmd",
            "RUN_DIR": FAKE_RUN_DIR,
        }
        if not os.path.exists(FAKE_BIN):
            os.makedirs(FAKE_BIN)
        if not os.path.exists(FAKE_RUN_DIR):
            os.makedirs(FAKE_RUN_DIR)
        self._create_socket("kamailio.proxy.binrpc.sock")

    def tearDown(self):
        shutil.rmtree(WORKSPACE)

    def testNoPrograms(self):
        res = executeAndReturnOutput(self.command, self.env)
        self.assertNotEqual(res[0], 0)
        self.assertRegex(res[2], "error: kamcmd not found")

    def testNoTarget(self):
        create_prog(os.path.join(FAKE_BIN, "kamcmd"), "true")
        res = executeAndReturnOutput(self.command, self.env)
        self.assertNotEqual(res[0], 0)
        self.assertRegex(res[2], "Usage: ")

    def testOk(self):
        self._create_kamcmd()
        self.command.append("proxy")
        res = executeAndReturnOutput(self.command, self.env)
        self.assertEqual(res[0], 0)
        self.checkCmd(os.path.join(FIXTURES, "kamcmd_ok"))

    def testOkCmd(self):
        self._create_kamcmd()
        self.command.append("proxy")
        self.command.append("whatever")
        res = executeAndReturnOutput(self.command, self.env)
        self.assertEqual(res[0], 0)
        self.checkCmd(os.path.join(FIXTURES, "kamcmd_ok_cmd"))

    def testMemDBG(self):
        self._create_kamcmd()
        self.command.append("proxy")
        self.command.append("memdbg")
        res = executeAndReturnOutput(self.command, self.env)
        self.assertEqual(res[0], 0)
        self.checkCmd(os.path.join(FIXTURES, "kamcmd_memdbg"))
        self.assertEqual(res[1], FAKE_KAMCMD_PS)

    def testMemDBGPid(self):
        self._create_kamcmd()
        self.command.append("proxy")
        self.command.append("memdbg")
        self.command.append("25476")
        res = executeAndReturnOutput(self.command, self.env)
        self.assertEqual(res[0], 0)
        self.checkCmd(os.path.join(FIXTURES, "kamcmd_memdbg_pid"))

    def testMemDBGPidKO(self):
        self._create_kamcmd()
        self.command.append("proxy")
        self.command.append("memdbg")
        self.command.append("2547")
        res = executeAndReturnOutput(self.command, self.env)
        self.assertEqual(res[0], 1)
        self.checkCmd(os.path.join(FIXTURES, "kamcmd_memdbg"))

    def testSocketKO(self):
        self._create_kamcmd()
        self.command.append("lb")
        res = executeAndReturnOutput(self.command, self.env)
        self.assertEqual(res[0], 1)

    def testSocketInstanceOk(self):
        self._create_kamcmd()
        self.command.append("lb-A")
        self._create_socket("kamailio.lb.A.binrpc.sock")
        res = executeAndReturnOutput(self.command, self.env)
        self.assertEqual(res[0], 0)
