#!/usr/bin/env python

##############################################################################
# Copyright (c) 2018 HUAWEI TECHNOLOGIES CO.,LTD and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################

import click
import os
import yaml
import json
import collections
from src.step.step_manager import TestStepManager
from src.conductor_processor.workflow import WorkflowFile
from conductorclient.run_new_workflow import WorkflowMgr

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
CONDUCTOR_SERVER_ADDR = "http://conductor_conductor-server_1:8080"
STORE_TASK_PATH = "/tmp/generate_task.json"
STORE_WF_PATH = "/tmp/generate_workflow.json"


@click.command()
@click.option("--filepath", help="file path of test case")
def cmdParse(filepath):
    parse(filepath)


def parse(filepath):
    filePrefix, fileName = os.path.split(filepath)
    print '------------ start to parse the test case:' + \
        '%s ----------------' % fileName
    with open(filepath) as f:
        yaml_file = yaml.load(f)
        parseTestcase(yaml_file['schema'], fileName)

    workflowId = runWorkFlow()
    print '------------------- parse executes end -------------------------'

    return workflowId


def parseTestcase(schema, tcName='testcase0'):
    if schema is None:
        return parseLog(False, reason='schema not found.')
    steps = schema['steps']
    if steps is None:
        return parseLog(False, reason='steps is invalid.')
    flows = schema['flows']
    if flows is None:
        return parseLog(False, reasion='flows is invalid.')
    # steps is a list, step is dict. no json here.
    # steps = sorted(steps, sortById)

    # load context
    contextDict = {}
    contextDir = os.path.join(BASE_DIR, "env", "context", "context.yaml")
    with open(contextDir, "r") as f:
        contextDict = yaml.load(f)
    #
    testStepMgr = TestStepManager(contextDict)

    stepObjArr = []
    for step in steps:
        if 'args' not in step:
            step['args'] = {}
        # type and action can be extended, default couple is 'test' & 'start'.
        if 'type' not in step:
            step['type'] = 'test'
            step['action'] = 'start'

        stepObj = testStepMgr.getStepObj(
            step['type'], step['id'], step['name'], step['service'],
            step['action'], step['args'])
        stepObjArr.append(stepObj)

    # generate workflow by 'flow' and 'step'
    tcName = os.path.splitext(tcName)[0]
    wfFileObj = WorkflowFile(tcName)
    workflowDict, taskMetaList = wfFileObj.generateMetaData(flows, stepObjArr)

    with open(STORE_TASK_PATH, 'w') as f:
        f.write(json.dumps({'task_group': taskMetaList}, indent=True))
    with open(STORE_WF_PATH, 'w') as f:
        f.write(json.dumps(workflowDict, indent=True))


def parseWebTestcase(webTestcase):
    print 'parseWebTestcase----------------------------'

    stepList = webTestcase['stepList']
    mainOrdersList = webTestcase['mainOrdersList']
    subflowList = webTestcase['subflowList']

    parseData = collections.OrderedDict()
    parseData['schema'] = collections.OrderedDict()
    parseData['schema']['steps'] = []
    parseData['schema']['flows'] = []

    parseStepList = parseData['schema']['steps']
    parseFlowList = parseData['schema']['flows']
    stepIndexDict = {}
    # parse stepList
    for index in range(len(stepList)):
        stepItem = stepList[index]
        parseStep = collections.OrderedDict()

        parseStep['id'] = index + 1
        parseStep['name'] = stepItem['name']
        parseStep['service'] = collections.OrderedDict()
        parseStep['service']['name'] = stepItem['service']
        parseStep['service']['interface'] = stepItem['action']
        parseStep['action'] = 'start'
        parseStep['args'] = {}
        for paramItem in stepItem['params']:
            parseStep['args'][paramItem['key']] = transParamString(
                paramItem['value'])

        parseStepList.append(parseStep)
        stepIndexDict[parseStep['name']] = parseStep['id']
    # parse flows
    # parse mainflow
    print stepIndexDict
    typeDict = {1: 'normal', 2: 'switch', 3: 'parallel'}
    mainFlow = collections.OrderedDict()
    mainFlow['name'] = 'main'
    mainFlow['orders'] = []
    mainFlow['orders'] = parseOrderList(
        mainOrdersList, stepIndexDict, typeDict)
    parseFlowList.append(mainFlow)

    # parse subflow
    for subflowItem in subflowList:
        replaceSubflow = collections.OrderedDict()
        replaceSubflow['name'] = subflowItem['name']
        replaceSubflow['orders'] = parseOrderList(
            subflowItem['orderList'], stepIndexDict, typeDict)
        parseFlowList.append(replaceSubflow)

    print 'END parseWebTestcase----------------------------'
    return parseData


# parse orderlist from web edition to server edition
def parseOrderList(orderList, stepIndexDict, typeDict):
    replaceList = []
    for orderItem in orderList:
        replaceOrder = collections.OrderedDict()
        orderType = typeDict[orderItem['type']]
        replaceOrder['type'] = orderType
        if orderType == 'normal':
            stepId = stepIndexDict[orderItem['step']]
            replaceOrder['step'] = stepId
        elif orderType == 'switch':
            replaceOrder['value'] = orderItem['value']
            replaceOrder['cases'] = collections.OrderedDict()
            for caseItem in orderItem['cases']:
                caseValue = caseItem['value']
                caseOrderType = caseItem['orderType']
                caseOrderValue = caseItem['orderValue']
                if caseOrderType == "step":
                    orderInCase = collections.OrderedDict()
                    orderInCase['type'] = 'normal'
                    orderInCase['step'] = stepIndexDict[caseOrderValue]
                    replaceOrder['cases'][caseValue] = [orderInCase]
                else:
                    replaceOrder['cases'][caseValue] = caseOrderValue
        else:
            replaceOrder['parallel'] = collections.OrderedDict()
            pIndex = 1
            for branchItem in orderItem['branches']:
                pKey = 'p' + str(pIndex)
                branchOrderType = branchItem['orderType']
                branchOrderValue = branchItem['orderValue']
                if branchOrderType == "step":
                    replaceBranchItem = collections.OrderedDict()
                    replaceBranchItem['type'] = 'normal'
                    replaceBranchItem['step'] = stepIndexDict[branchOrderValue]
                    replaceOrder['parallel'][pKey] = [replaceBranchItem]
                else:
                    replaceOrder['parallel'][pKey] = branchOrderValue
                pIndex += 1
        replaceList.append(replaceOrder)
    return replaceList


def transParamString(val):
    if type(val) != str:
        return val
    if '.' not in val:
        if val.isdigit():
            return int(val)
    try:
        f = float(val)
        return f
    except ValueError:
        return val


def getWebTestcase(originTcDict):
    print "getWebTestcase----------------------------------"
    webTcDict = {
        "stepList": [],
        "mainOrdersList": [],
        "subflowList": []
    }
    stepList = webTcDict['stepList']
    subflowList = webTcDict['subflowList']
    if originTcDict is None:
        return webTcDict
    originContent = originTcDict['schema']
    originSteps = originContent['steps']
    stepIndexDict = {}
    # transform steps to stepList
    for stepItem in originSteps:
        replaceStep = {}
        replaceStep['name'] = stepItem['name']
        replaceStep['service'] = stepItem['service']['name']
        replaceStep['action'] = stepItem['service']['interface']
        replaceStep['params'] = []
        if 'args' in stepItem:
            for (key, value) in stepItem['args'].items():
                replaceParam = {}
                replaceParam['key'] = key
                replaceParam['value'] = value
                replaceStep['params'].append(replaceParam)
        stepList.append(replaceStep)
        stepIndexDict[stepItem['id']] = stepItem['name']

    # transform main flow
    originFlows = originContent['flows']
    originMainflow = {}
    for flowIndex in range(len(originFlows)):
        flowItem = originFlows[flowIndex]
        if flowItem['name'] == 'main':
            originMainflow = flowItem
            originFlows.pop(flowIndex)
            break
    typeDict = {'normal': 1, 'switch': 2, 'parallel': 3}
    webTcDict['mainOrdersList'] = getOrderList(
        originMainflow['orders'], stepIndexDict, typeDict)

    # transform subflows
    for originSubflow in originFlows:
        replaceSubflow = {}
        replaceSubflow['name'] = originSubflow['name']
        replaceSubflow['orderList'] = getOrderList(
            originSubflow['orders'], stepIndexDict, typeDict)
        subflowList.append(replaceSubflow)

    # return web edition of testcase
    print "END getWebTestcase----------------------------------"
    return webTcDict


def getOrderList(originOrderList, stepIndexDict, typeDict):
    replaceOrderList = []
    for orderItem in originOrderList:
        replaceOrderItem = {}
        orderType = orderItem['type']
        replaceOrderItem['type'] = typeDict[orderType]
        if orderType == 'normal':
            stepName = stepIndexDict[orderItem['step']]
            replaceOrderItem['step'] = stepName
        elif orderType == 'switch':
            replaceOrderItem['value'] = orderItem['value']
            replaceOrderItem['cases'] = []
            for (caseValue, ordersInCase) in orderItem['cases'].items():
                replaceCase = {}
                replaceCase['value'] = caseValue
                if type(ordersInCase) == list:
                    replaceCase['orderType'] = 'step'
                    caseStepName = stepIndexDict[ordersInCase[0]['step']]
                    replaceCase['orderValue'] = caseStepName
                else:
                    replaceCase['orderType'] = 'flow'
                    replaceCase['orderValue'] = ordersInCase
                replaceOrderItem['cases'].append(replaceCase)
        else:
            replaceOrderItem['branches'] = []
            for paraIndex in orderItem['parallel']:
                paraItem = orderItem['parallel'][paraIndex]
                replaceBranch = {}
                if type(paraItem) == list:
                    replaceBranch['orderType'] = 'step'
                    branchStepName = stepIndexDict[paraItem[0]['step']]
                    replaceBranch['orderValue'] = branchStepName
                else:
                    replaceBranch['orderType'] = 'flow'
                    replaceBranch['orderValue'] = paraItem
                replaceOrderItem['branches'].append(replaceBranch)
        replaceOrderList.append(replaceOrderItem)

    return replaceOrderList


def runWorkFlow():
    wfMgr = WorkflowMgr(CONDUCTOR_SERVER_ADDR)
    wfMgr.setTaskDefFromFile(STORE_TASK_PATH)
    wfMgr.setWorkflowFromFile(STORE_WF_PATH)
    inputParam = {'input': 'fake'}
    workflowId = wfMgr.startWorkflow(inputParam)
    return workflowId


def parseLog(flag, **msg):
    return {'result': flag, 'message': msg}


if __name__ == "__main__":
    cmdParse()