""" unit tests for Transformation Clients
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

# pylint: disable=protected-access,missing-docstring,invalid-name

import six
import unittest
import json
import mock

from DIRAC.RequestManagementSystem.Client.Request import Request
from DIRAC.TransformationSystem.Client.TaskManager import TaskBase, RequestTasks
from DIRAC.TransformationSystem.Client.Transformation import Transformation
from DIRAC.TransformationSystem.Client.Utilities import PluginUtilities


class reqValFake_C(object):
  def validate(self, opsInput):
    for ops in opsInput:
      if not len(ops):
        return {'OK': False}
      for f in ops:
        try:
          if not f.LFN:
            return {'OK': False}
        except BaseException:
          return {'OK': False}
    return {'OK': True}


reqValFake = reqValFake_C()


class ClientsTestCase(unittest.TestCase):
  """ Base class for the clients test cases
  """

  def setUp(self):
    self.mockTransClient = mock.MagicMock()
    self.mockTransClient.setTaskStatusAndWmsID.return_value = {'OK': True}

    self.mockReqClient = mock.MagicMock()

    self.taskBase = TaskBase(transClient=self.mockTransClient)
    self.pu = PluginUtilities(transClient=self.mockTransClient)

    self.requestTasks = RequestTasks(transClient=self.mockTransClient,
                                     requestClient=self.mockReqClient,
                                     requestValidator=reqValFake)
    self.transformation = Transformation()

    self.maxDiff = None

  def tearDown(self):
    pass


class TaskBaseSuccess(ClientsTestCase):

  def test_updateDBAfterTaskSubmission(self):
    res = self.taskBase.updateDBAfterTaskSubmission({})
    self.assertEqual(res['OK'], True)


class PluginUtilitiesSuccess(ClientsTestCase):

  def test_groupByReplicas(self):
    res = self.pu.groupByReplicas({'/this/is/at.1': ['SE1'],
                                   '/this/is/at.12': ['SE1', 'SE2'],
                                   '/this/is/at.2': ['SE2'],
                                   '/this/is/at_123': ['SE1', 'SE2', 'SE3'],
                                   '/this/is/at_23': ['SE2', 'SE3'],
                                   '/this/is/at_4': ['SE4']},
                                  'Flush')
    self.assertTrue(res['OK'])
    self.assertEqual(res['Value'], [('SE1', ['/this/is/at.1']),
                                    ('SE1,SE2', ['/this/is/at.12']),
                                    ('SE1,SE2,SE3', ['/this/is/at_123']),
                                    ('SE2', ['/this/is/at.2']),
                                    ('SE2,SE3', ['/this/is/at_23']),
                                    ('SE4', ['/this/is/at_4'])])

    res = self.pu.groupByReplicas({'/this/is/at.123': ['SE1', 'SE2', 'SE3'],
                                   '/this/is/at.12': ['SE1', 'SE2'],
                                   '/this/is/at.134': ['SE1', 'SE3', 'SE4']},
                                  'Flush')
    self.assertTrue(res['OK'])
    self.assertEqual(res['Value'], [
        ('SE1,SE2', ['/this/is/at.12']),
        ('SE1,SE2,SE3', ['/this/is/at.123']),
        ('SE1,SE3,SE4', ['/this/is/at.134'])])


class RequestTasksSuccess(ClientsTestCase):

  def test_prepareTranformationTasks(self):

    # No tasks in input
    taskDict = {}
    res = self.requestTasks.prepareTransformationTasks('', taskDict, 'owner', 'ownerGroup', '/bih/boh/DN')
    self.assertTrue(res['OK'])
    self.assertEqual(len(taskDict), 0)

    # 3 tasks, 1 task not OK (in second transformation)
    taskDict = {123: {'TransformationID': 2,
                      'TargetSE': 'SE3', 'b3': 'bb3', 'InputData': ''}}
    res = self.requestTasks.prepareTransformationTasks(
        '', taskDict, 'owner', 'ownerGroup', '/bih/boh/DN')
    self.assertTrue(res['OK'])
    # We should "lose" one of the task in the preparation
    self.assertEqual(len(taskDict), 0)

    taskDict = {1: {'TransformationID': 1, 'TargetSE': 'SE1', 'b1': 'bb1', 'Site': 'MySite',
                    'InputData': ['/this/is/a1.lfn', '/this/is/a2.lfn']},
                2: {'TransformationID': 1, 'TargetSE': 'SE2', 'b2': 'bb2',
                    'InputData': "/this/is/a1.lfn;/this/is/a2.lfn"},
                3: {'TransformationID': 2, 'TargetSE': 'SE3', 'b3': 'bb3', 'InputData': ''}}

    res = self.requestTasks.prepareTransformationTasks('', taskDict, 'owner', 'ownerGroup', '/bih/boh/DN')
    self.assertTrue(res['OK'])
    # We should "lose" one of the task in the preparation
    self.assertEqual(len(taskDict), 2)
    for task in res['Value'].values():
      self.assertTrue(isinstance(task['TaskObject'], Request))
      self.assertEqual(task['TaskObject'][0].Type, 'ReplicateAndRegister')
      try:
        self.assertEqual(task['TaskObject'][0][0].LFN, '/this/is/a1.lfn')
      except IndexError:
        self.assertEqual(task['TaskObject'][0].Status, 'Waiting')
      try:
        self.assertEqual(task['TaskObject'][0][1].LFN, '/this/is/a2.lfn')
      except IndexError:
        self.assertEqual(task['TaskObject'][0].Status, 'Waiting')

    # # test another (single) OperationType
    res = self.requestTasks.prepareTransformationTasks('someType;LogUpload', taskDict,
                                                       'owner', 'ownerGroup', '/bih/boh/DN')
    self.assertTrue(res['OK'])
    # We should "lose" one of the task in the preparation
    self.assertEqual(len(taskDict), 2)
    for task in res['Value'].values():
      self.assertTrue(isinstance(task['TaskObject'], Request))
      self.assertEqual(task['TaskObject'][0].Type, 'LogUpload')

    # ## Multiple operations
    transBody = [("ReplicateAndRegister", {"SourceSE": "FOO-SRM", "TargetSE": "BAR-SRM"}),
                 ("RemoveReplica", {"TargetSE": "FOO-SRM"}), ]
    jsonBody = json.dumps(transBody)

    taskDict = {1: {'TransformationID': 1, 'TargetSE': 'SE1', 'b1': 'bb1', 'Site': 'MySite',
                    'InputData': ['/this/is/a1.lfn', '/this/is/a2.lfn']},
                2: {'TransformationID': 1, 'TargetSE': 'SE2', 'b2': 'bb2',
                    'InputData': "/this/is/a1.lfn;/this/is/a2.lfn"},
                3: {'TransformationID': 2, 'TargetSE': 'SE3', 'b3': 'bb3',
                    'InputData': ''}}

    res = self.requestTasks.prepareTransformationTasks(jsonBody, taskDict, 'owner', 'ownerGroup', '/bih/boh/DN')
    self.assertTrue(res['OK'])
    # We should "lose" one of the task in the preparation
    self.assertEqual(len(taskDict), 2)
    for task in res['Value'].values():
      self.assertTrue(isinstance(task['TaskObject'], Request))
      self.assertEqual(task['TaskObject'][0].Type, 'ReplicateAndRegister')
      self.assertEqual(task['TaskObject'][1].Type, 'RemoveReplica')
      try:
        self.assertEqual(task['TaskObject'][0][0].LFN, '/this/is/a1.lfn')
        self.assertEqual(task['TaskObject'][1][0].LFN, '/this/is/a1.lfn')
      except IndexError:
        self.assertEqual(task['TaskObject'][0].Status, 'Waiting')
        self.assertEqual(task['TaskObject'][1].Status, 'Waiting')
      try:
        self.assertEqual(task['TaskObject'][0][1].LFN, '/this/is/a2.lfn')
        self.assertEqual(task['TaskObject'][1][1].LFN, '/this/is/a2.lfn')
      except IndexError:
        self.assertEqual(task['TaskObject'][0].Status, 'Waiting')
        self.assertEqual(task['TaskObject'][1].Status, 'Waiting')

      self.assertEqual(task['TaskObject'][0].SourceSE, 'FOO-SRM')
      self.assertEqual(task['TaskObject'][0].TargetSE, 'BAR-SRM')
      self.assertEqual(task['TaskObject'][1].TargetSE, 'FOO-SRM')


class TransformationSuccess(ClientsTestCase):

  def test_setGet(self):

    res = self.transformation.setTransformationName('TestTName')
    self.assertTrue(res['OK'])
    description = 'Test transformation description'
    res = self.transformation.setDescription(description)
    longDescription = 'Test transformation long description'
    res = self.transformation.setLongDescription(longDescription)
    self.assertTrue(res['OK'])
    res = self.transformation.setType('MCSimulation')
    self.assertTrue(res['OK'])
    res = self.transformation.setPlugin('aPlugin')
    self.assertTrue(res['OK'])

    # # Test DataOperation Body

    res = self.transformation.setBody("")
    self.assertTrue(res['OK'])
    self.assertEqual(self.transformation.paramValues["Body"], "")

    res = self.transformation.setBody("_requestType;RemoveReplica")
    self.assertTrue(res['OK'])
    self.assertEqual(self.transformation.paramValues["Body"], "_requestType;RemoveReplica")

    # #Json will turn tuples to lists and strings to unicode
    transBody = [[u"ReplicateAndRegister", {u"SourceSE": u"FOO-SRM", u"TargetSE": u"BAR-SRM"}],
                 [u"RemoveReplica", {u"TargetSE": u"FOO-SRM"}],
                 ]
    res = self.transformation.setBody(transBody)
    self.assertTrue(res['OK'])

    self.assertEqual(self.transformation.paramValues["Body"], json.dumps(transBody))

    # # This is not true if any of the keys or values are not strings, e.g., integers
    self.assertEqual(json.loads(self.transformation.paramValues["Body"]), transBody)

    with self.assertRaisesRegexp(TypeError, "Expected list"):
      self.transformation.setBody({"ReplicateAndRegister": {"foo": "bar"}})
    with self.assertRaisesRegexp(TypeError, "Expected tuple"):
      self.transformation.setBody(["ReplicateAndRegister", "RemoveReplica"])
    with self.assertRaisesRegexp(TypeError, "Expected 2-tuple"):
      self.transformation.setBody([("ReplicateAndRegister", "RemoveReplica", "LogUpload")])
    with self.assertRaisesRegexp(TypeError, "Expected string"):
      self.transformation.setBody([(123, "Parameter:Value")])
    with self.assertRaisesRegexp(TypeError, "Expected dictionary"):
      self.transformation.setBody([("ReplicateAndRegister", "parameter=foo")])
    with self.assertRaisesRegexp(TypeError, "Expected string"):
      self.transformation.setBody([("ReplicateAndRegister", {123: "foo"})])
    with self.assertRaisesRegexp(ValueError, "Unknown attribute"):
      self.transformation.setBody([("ReplicateAndRegister", {"Request": Request()})])
    with self.assertRaisesRegexp(TypeError, "Cannot encode"):
      self.transformation.setBody([("ReplicateAndRegister", {"Arguments": Request()})])

  def test_SetGetReset(self):
    """ Testing of the set, get and reset methods.

          set*()
          get*()
          setTargetSE()
          setSourceSE()
          getTargetSE()
          getSourceSE()
          reset()
        Ensures that after a reset all parameters are returned to their defaults
    """

    res = self.transformation.getParameters()
    self.assertTrue(res['OK'])
    defaultParams = res['Value'].copy()
    for parameterName, defaultValue in res['Value'].items():
      if isinstance(defaultValue, six.string_types):
        testValue = 'TestValue'
      else:
        testValue = 99999
      # # set*

      setterName = 'set%s' % parameterName
      self.assertTrue(hasattr(self.transformation, setterName))
      setter = getattr(self.transformation, setterName)
      self.assertTrue(callable(setter))
      res = setter(testValue)
      self.assertTrue(res['OK'])
      # # get*
      getterName = "get%s" % parameterName
      self.assertTrue(hasattr(self.transformation, getterName))
      getter = getattr(self.transformation, getterName)
      self.assertTrue(callable(getter))
      res = getter()
      self.assertTrue(res['OK'])
      self.assertTrue(res['Value'], testValue)

    res = self.transformation.reset()
    self.assertTrue(res['OK'])
    res = self.transformation.getParameters()
    self.assertTrue(res['OK'])
    for parameterName, resetValue in res['Value'].items():
      self.assertEqual(resetValue, defaultParams[parameterName])
    self.assertRaises(AttributeError, self.transformation.getTargetSE)
    self.assertRaises(AttributeError, self.transformation.getSourceSE)


if __name__ == '__main__':
  suite = unittest.defaultTestLoader.loadTestsFromTestCase(ClientsTestCase)
  suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TaskBaseSuccess))
  suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(PluginUtilitiesSuccess))
  suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(RequestTasksSuccess))
  suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TransformationSuccess))
  testResult = unittest.TextTestRunner(verbosity=2).run(suite)
