import json
import whatap.net as net
from whatap.value.map_value import MapValue
from whatap.value.text_value import TextValue
from whatap.value.decimal_value import DecimalValue
from whatap.value.double_value import DoubleValue
from whatap.value.boolean_value import BooleanValue
from whatap.value.list_value import ListValue
import whatap.agent.conf.configure as configure
import whatap.osinfo.stack_dump as stack_dump
import whatap.util.logging_util as logging_util
import whatap.value as value
import threading, os, time
import whatap.agent.control.handle as handle
import whatap.ext.simplepluginWrapper as simpleplugin
start = False

def initControlHandler():
    global start
    if not start:
        start = True

        t = threading.Thread(target=runControl)
        t.setDaemon(True)
        t.start()

def runControl():
    while True:
        try:
            __runControl()
            time.sleep(0.001)
        except Exception, e:
            logging_util.logStack(e)
            time.sleep(0.1)

def __runControl():
    import whatap.net.receiver as receiver
    import whatap.agent.data.data_pack as data_pack
    import whatap.agent.conf.configure as conf
    import whatap.agent.secure.security_master as secu
    pcode = secu.GetSecurityMaster().pcode
    oid = secu.GetSecurityMaster().oid

    while True:
        #logging_util.debug("control handler trying to receive param pack") 
        p = receiver.getReceiver().get()
        #logging_util.debug("p.id:", p.id)
        #logging_util.debug( 'control_handler.runControl step -1')
        if p.id == net.PARAM_GET_ENV:
            r = p.toResponse()
            r.pcode = pcode
            r.oid = oid
            m = MapValue()
            for k, v in os.environ.items():
                m.putString(k,v)
            r.put('env', m)

            data_pack.SendSecure(r)
        elif p.id == net.PARAM_SET_CONFIG:
            #logging_util.debug('control_handler.runControl step -2')
            c = p.getMap("config")
            #logging_util.debug('control_handler.runControl step -3')
            if c:
                m = {}
                for k in c.keys():
                    m[k] = c.get(k).toObject()
                    #logging_util.debug('param_set_config {}={}'.format(k, m[k]))   
                #logging_util.debug('control_handler.runControl step -4')
                conf.SetValues(m)
            r = p.toResponse()
            r.pcode = pcode
            r.oid = oid
            data_pack.SendSecure(p)

        elif p.id == net.PARAM_CONFIGURE_GET:
            r = p.toResponse()
            r.pcode = pcode
            r.oid = oid

            def h2(k, v):
                r.put(k, TextValue(value=v))
            m = configure.GetConfig().populateAllKeyValues(h2)

            data_pack.SendSecure(p)

        elif p.id == net.PARAM_THREAD_LIST:

            r = p.toResponse()
            r.pcode = pcode
            r.oid = oid
            mv = stack_dump.getAll()

            p.put("thread_list", mv)
            data_pack.SendSecure(p)

        elif p.id == net.PARAM_THREAD_DETAIL:
            r = p.toResponse()
            r.pcode = pcode
            r.oid = oid
            threadid = p.get("thread_id").toObject()
            m = stack_dump.getThreadDetail(threadid)
            p.put(str(threadid), m)
            data_pack.SendSecure(p)
        elif p.id == net.PARAM_AGENT_LOG_LIST:
            r = p.toResponse()
            r.pcode = pcode
            r.oid = oid
            mv = MapValue()
            mv.put("default", DecimalValue(value=1024))
            p.put("files", mv)
            data_pack.SendSecure(p)
        elif p.id == net.PARAM_AGENT_LOG_READ:
            r = p.toResponse()
            r.pcode = pcode
            r.oid = oid
            p.put("before", DecimalValue(value=0))
            p.put("next", DecimalValue(value=-1))
            p.putString("text", logging_util.getAllLogs())
            data_pack.SendSecure(p)
        elif p.id == net.PARAM_REMOTE_CMD:
            cmd = p.getString("cmd")
            logging_util.debug("control handler remote cmd :{}".format(cmd))
            p = p.toResponse()
            p.pcode = pcode
            p.oid = oid
            if cmd == "list":
                m = handle.getCommandList()
                p.put("result", m)
            elif cmd == "execute" :
                scriptSlot = p.getString("script")
                options = p.get("args")
            
                m = handle.execute(scriptSlot, options)
                p.put("result", m)
                logging_util.debug("remote cmd scriptSlot:{} options:{}".format(scriptSlot, ",".join([options.getString(i) for i in range(options.size())]))) 
                                
            elif cmd == "getInput":
                inputfile = p.getString("inputFile")
                m = MapValue()
                p.put("result", m)
                try:
                    inputText = simpleplugin.getInput(inputfile)
                    m.putString("inputToml", inputText)
                    m.put("success", BooleanValue(True))
                except Exception, e:
                    logging_util.debug('control handler getInput error:{}'.format(e))
                    logging_util.debug(logging_util.getStackTrace())
                    m.putString("error", str(e))
                    m.put("success", BooleanValue(False))
                    
            elif cmd == "setInput":
                inputToml = p.getString("inputToml")
                inputfile = p.getString("inputFile")

                m = MapValue()
                p.put("result", m)
                try:
                    simpleplugin.setInput(inputfile, inputToml)
                    m.put("success", BooleanValue(True))
                except Exception, e:
                    logging_util.debug('control handler setInput error:{}'.format(e))
                    logging_util.debug(logging_util.getStackTrace())
                    m.putString("error", str(e))
                    m.put("success", BooleanValue(False))
                
            elif cmd == "getAllInput":
                m = MapValue()
                p.put("result", m)
                
                try:
                    inputs = simpleplugin.getAllInput()
                    allInputs = m.newList("inputs")
                    for input in inputs:
                        allInputs.addString(input)
                
                    m.put("success", BooleanValue(True))
                except Exception, e:
                    logging_util.debug("getAllInput error: {}".format(e))
                    logging_util.debug(logging_util.getStackTrace())
                    m.putString("error", str(e))
                    m.put("success", BooleanValue(False))
                
            elif cmd == "deleteInput":
                m = MapValue()
                p.put("result", m)
                inputfiles = p.get("inputFileList")
                for i in range(1,inputfiles.size()):
                    inputFile = inputfiles.getString(i)
                    try:
                        simpleplugin.deleteInput(inputFile)
                        m.put(inputFile, BooleanValue(True))
                    except Exception, e:
                        logging_util.debug("deleteInput error: {}".format(e))
                        m.put(inputFile, BooleanValue(False))
            data_pack.SendSecure(p)

        # Script Manager Handlers
        elif p.id == net.PARAM_SCRIPT_MANAGER_REQUEST:
            _handle_script_manager_request(p, pcode, oid, data_pack)

        elif p.id == net.PARAM_SCRIPT_MANAGER_EXEC:
            _handle_script_manager_exec(p, pcode, oid, data_pack)

        elif p.id == net.PARAM_SCRIPT_MANAGER_STATUS:
            _handle_script_manager_status(p, pcode, oid, data_pack)

        elif p.id == net.PARAM_SCRIPT_MANAGER_FILEREAD:
            _handle_script_manager_fileread(p, pcode, oid, data_pack)

        elif p.id == net.PARAM_SCRIPT_MANAGER_FILESAVE:
            logging_util.info("control filesave")
            _handle_script_manager_filesave(p, pcode, oid, data_pack)
            logging_util.info("control filesave end")

        elif p.id == net.PARAM_SCRIPT_MANAGER_FILELIST:
            _handle_script_manager_filelist(p, pcode, oid, data_pack)

        elif p.id == net.PARAM_SCRIPT_MANAGER_FILEDELETE:
            _handle_script_manager_filedelete(p, pcode, oid, data_pack)

        elif p.id == net.PARAM_SCRIPT_MANAGER_PATCH:
            _handle_script_manager_patch(p, pcode, oid, data_pack)

        elif p.id == net.PARAM_SCRIPT_MANAGER_PATCH_STATUS:
            _handle_script_manager_patch_status(p, pcode, oid, data_pack)


def _handle_script_manager_request(p, pcode, oid, data_pack):
    import whatap.agent.cron.cronjob as cronjob
    import whatap.agent.cron.script_manager as sm
    from whatap.value.list_value import ListValue

    logging_util.debug("PARAM_SCRIPT_MANAGER_REQUEST pack received")
    p = p.toResponse()
    p.pcode = pcode
    p.oid = oid

    try:
        data_map = p.getMap("data")
        if data_map is None:
            logging_util.error("PARAM_SCRIPT_MANAGER_REQUEST data map nil")
            p.putString("result", "Error")
            p.putString("errorCode", sm.ERR_SM_REQUEST_GENERAL)
            p.putString("errorMessage", "data is nil")
            data_pack.SendSecure(p)
            return

        method = data_map.getText("method")
        logging_util.debug("ScriptManager method: {}".format(method))

        if method == sm.METHOD_ACTIVATE:
            job_list_str = data_map.getText(sm.COLUMN_JOB_LIST)
            try:
                new_cfg = json.loads(job_list_str)
                cronjob.save_script_file(new_cfg)
                cronjob.save_cron_config(new_cfg)
                p.putString("result", "OK")
            except Exception, e:
                logging_util.error("PARAM_SCRIPT_MANAGER_REQUEST activate error: {}".format(e))
                p.putString("result", "Error")
                p.putString("errorCode", sm.ERR_SM_ACTIVATE_JSON)
                p.putString("errorMessage", str(e))

        elif method == sm.METHOD_DEACTIVATE or method == sm.METHOD_DELETE:
            job_list = data_map.get(sm.COLUMN_JOB_LIST)
            if job_list is None:
                p.putString("result", "Error")
                p.putString("errorCode", sm.ERR_SM_DEACTIVATE_NO_JOBLIST)
                p.putString("errorMessage", "jobList is nil")
            else:
                job_ids = []
                if isinstance(job_list, ListValue):
                    for i in range(job_list.size()):
                        job_ids.append(job_list.getString(i))
                try:
                    cronjob.delete_cron_config(job_ids)
                    p.putString("result", "OK")
                except Exception, e:
                    logging_util.error("PARAM_SCRIPT_MANAGER_REQUEST delete error: {}".format(e))
                    p.putString("result", "Error")
                    p.putString("errorCode", sm.ERR_SM_REQUEST_GENERAL)
                    p.putString("errorMessage", str(e))

        else:
            p.putString("result", "Error")
            p.putString("errorCode", sm.ERR_SM_UNKNOWN_REQ_METHOD)
            p.putString("errorMessage", "unknown method: {}".format(method))

    except Exception, e:
        logging_util.error("PARAM_SCRIPT_MANAGER_REQUEST error: {}".format(e))
        p.putString("result", "Error")
        p.putString("errorCode", sm.ERR_SM_REQUEST_GENERAL)
        p.putString("errorMessage", str(e))

    logging_util.debug("PARAM_SCRIPT_MANAGER_REQUEST return pack")
    data_pack.SendSecure(p)


def _handle_script_manager_exec(p, pcode, oid, data_pack):
    import whatap.agent.cron.script_manager as sm
    
    logging_util.debug("PARAM_SCRIPT_MANAGER_EXEC pack received")
    p = p.toResponse()
    p.pcode = pcode
    p.oid = oid

    try:
        data_map = p.getMap("data")
        if data_map is None:
            logging_util.error("PARAM_SCRIPT_MANAGER_EXEC data map nil")
            p.putString("result", "Error")
            p.putString("errorCode", sm.ERR_SM_REQUEST_GENERAL)
            p.putString("errorMessage", "data is nil")
            data_pack.SendSecure(p)
            return

        command_map = {
            sm.COLUMN_METHOD: data_map.getText(sm.COLUMN_METHOD),
            sm.COLUMN_EXECUTE_UUID: data_map.getText(sm.COLUMN_EXECUTE_UUID),
            sm.COLUMN_CONTENT: data_map.getText(sm.COLUMN_CONTENT)
        }

        script_status = sm.execute_script(command_map)

        p.putString("result", "OK")

        if script_status:
            response = MapValue()
            command = MapValue()

            command.putString("status", script_status.get('status', ''))
            command.put("startTime", DecimalValue(script_status.get('start_time', 0)))
            command.put("endTime", DecimalValue(script_status.get('end_time', 0)))
            command.putString("stdout", script_status.get('stdout', ''))
            command.putString("stderr", script_status.get('stderr', ''))
            command.put("exitCode", DecimalValue(script_status.get('exit_code', 0)))

            queue = MapValue()
            q_status = script_status.get('queue_status', {})
            queue.put("currentSize", DecimalValue(q_status.get('current', 0)))
            queue.put("totalSize", DecimalValue(q_status.get('total', 0)))

            response.put("command", command)
            response.put("queue", queue)
            p.put("response", response)

    except sm.ScriptManagerError, e:
        logging_util.error("PARAM_SCRIPT_MANAGER_EXEC error: {}".format(e))
        p.putString("result", "Error")
        p.putString("errorCode", e.error_code())
        p.putString("errorMessage", str(e))
    except Exception, e:
        logging_util.error("PARAM_SCRIPT_MANAGER_EXEC error: {}".format(e))
        p.putString("result", "Error")
        p.putString("errorCode", sm.ERR_SM_EXEC_GENERAL)
        p.putString("errorMessage", str(e))

    logging_util.debug("PARAM_SCRIPT_MANAGER_EXEC return pack")
    data_pack.SendSecure(p)


def _handle_script_manager_status(p, pcode, oid, data_pack):
    import whatap.agent.cron.script_manager as sm
    import whatap.agent.cron.cronjob as cronjob
    from whatap.value.list_value import ListValue

    logging_util.debug("PARAM_SCRIPT_MANAGER_STATUS pack received")
    p = p.toResponse()
    p.pcode = pcode
    p.oid = oid

    try:
        running_status = sm.get_running_script_status()
        cron_status = cronjob.get_cronjob_status()

        p.putString("result", "OK")

        queue = MapValue()
        execute = MapValue()

        q_status = running_status.get('queue_status', {})
        queue.put("currentSize", DecimalValue(q_status.get('current', 0)))
        queue.put("totalSize", DecimalValue(q_status.get('total', 0)))

        execute.putString("executeStatus", running_status.get('status', sm.STATUS_NOTFOUND))
        execute.putString("executeUuid", running_status.get('uuid', ''))
        execute.put("executeTime", DecimalValue(running_status.get('start_time', 0)))

        schedules = ListValue()
        for job in cron_status:
            job_map = MapValue()
            job_map.putString("id", job.get('id', ''))

            trigger = MapValue()
            if job.get('scheduleType') == 'cron':
                trigger.putString("cron", job.get('schedule', ''))
            elif job.get('scheduleType') == 'interval':
                try:
                    trigger.put("interval", DecimalValue(int(job.get('schedule', 0))))
                except:
                    trigger.put("interval", DecimalValue(0))

            job_map.put("trigger", trigger)
            job_map.put("lastExecuteTime", DecimalValue(job.get('lastExecuteStartTime', 0)))
            job_map.putString("lastExecuteStatus", job.get('lastExecuteStatus', ''))
            schedules.addValue(job_map)

        response = MapValue()
        response.put("queue", queue)
        response.put("execute", execute)
        response.put("schedules", schedules)
        p.put("response", response)

    except sm.ScriptManagerError, e:
        logging_util.error("PARAM_SCRIPT_MANAGER_STATUS error: {}".format(e))
        p.putString("result", "Error")
        p.putString("errorCode", e.error_code())
        p.putString("errorMessage", str(e))
    except Exception, e:
        logging_util.error("PARAM_SCRIPT_MANAGER_STATUS error: {}".format(e))
        p.putString("result", "Error")
        p.putString("errorCode", sm.ERR_SM_STATUS_GENERAL)
        p.putString("errorMessage", str(e))

    logging_util.debug("PARAM_SCRIPT_MANAGER_STATUS return pack")
    data_pack.SendSecure(p)


def _handle_script_manager_fileread(p, pcode, oid, data_pack):
    import whatap.agent.cron.script_manager as sm
    import whatap.agent.cron.cronjob as cronjob
    
    logging_util.debug("PARAM_SCRIPT_MANAGER_FILEREAD pack received")
    p = p.toResponse()
    p.pcode = pcode
    p.oid = oid

    try:
        data_map = p.getMap("data")
        file_name = data_map.getText("file")
        file_type = data_map.getText("type")
        pos = data_map.getLong("pos")
        length = data_map.getLong("length")
        direction = data_map.getText("direction") or "forward"

        if length > 8000:
            length = 8000

        if file_type == "conf":
            file_name = cronjob.get_cron_config_path(file_name)

        text, start_pos, end_pos, file_size, err = sm.read_file(file_name, pos, length, direction)

        if err:
            p.putString("result", "Error")
            p.putString("errorCode", sm.ERR_SM_FILE_READ_ERROR)
            p.putString("errorMessage", str(err))
        else:
            p.putString("result", "OK")
            response = MapValue()
            response.put("startPosition", DecimalValue(start_pos))
            response.put("endPosition", DecimalValue(end_pos))
            response.putString("text", text)
            response.put("fileSize", DecimalValue(file_size))
            p.put("response", response)

    except Exception, e:
        logging_util.error("PARAM_SCRIPT_MANAGER_FILEREAD error: {}".format(e))
        p.putString("result", "Error")
        p.putString("errorCode", sm.ERR_SM_FILE_READ_ERROR)
        p.putString("errorMessage", str(e))

    logging_util.debug("PARAM_SCRIPT_MANAGER_FILEREAD return pack")
    data_pack.SendSecure(p)


def _handle_script_manager_filesave(p, pcode, oid, data_pack):
    import whatap.agent.control.agent_patch as ap

    logging_util.info("PARAM_SCRIPT_MANAGER_FILESAVE pack received")
    p = p.toResponse()
    p.pcode = pcode
    p.oid = oid

    try:
        data_map = p.getMap("data")
        if data_map is None:
            p.putString("result", "Error")
            p.putString("errorCode", ap.ERR_SM_FILE_SAVE_ERROR)
            p.putString("errorMessage", "data is nil")
            data_pack.SendSecure(p)
            return

        file_path = data_map.getText("file")
        file_name = os.path.basename(file_path) if file_path else ""
        pos = data_map.getLong("pos")
        blob_data = data_map.get("data")
        size = data_map.getLong("size")
        crc = data_map.getLong("crc")

        if file_name and blob_data is not None and hasattr(blob_data, 'value'):
            logging_util.info("PARAM_SCRIPT_MANAGER_FILESAVE chunk: file={}, pos={}, dataLen={}".format(
                file_name, pos, len(blob_data.value)))
            try:
                ap.package_save(file_name, pos, blob_data.value)
                logging_util.info("PARAM_SCRIPT_MANAGER_FILESAVE chunk save ok : file={}, pos={}, dataLen={}".format(
                    file_name, pos, len(blob_data.value)))
                p.putString("result", "OK")
            except ap.AgentPatchError, e:
                p.putString("result", "Error")
                p.putString("errorCode", e.error_code())
                p.putString("errorMessage", str(e))

        elif crc != 0 and size != 0:
            logging_util.info("PARAM_SCRIPT_MANAGER_FILESAVEDONE: file={}, size={}, crc={}".format(
                file_name, size, crc))
            try:
                ap.package_save_done(file_name, size, crc)
                p.putString("result", "OK")
            except ap.AgentPatchError, e:
                p.putString("result", "Error")
                p.putString("errorCode", e.error_code())
                p.putString("errorMessage", str(e))

        else:
            p.putString("result", "Error")
            p.putString("errorCode", ap.ERR_SM_FILE_SAVE_ERROR)
            p.putString("errorMessage", "Unknown request")

    except Exception, e:
        logging_util.error("PARAM_SCRIPT_MANAGER_FILESAVE error: {}".format(e))
        p.putString("result", "Error")
        p.putString("errorCode", ap.ERR_SM_FILE_SAVE_ERROR)
        p.putString("errorMessage", str(e))

    logging_util.info("PARAM_SCRIPT_MANAGER_FILESAVE return pack")
    data_pack.SendHide(p)


def _handle_script_manager_filelist(p, pcode, oid, data_pack):
    import whatap.agent.control.agent_patch as ap
    from whatap.value.list_value import ListValue

    logging_util.debug("PARAM_SCRIPT_MANAGER_FILELIST pack received")
    p = p.toResponse()
    p.pcode = pcode
    p.oid = oid

    try:
        files, err = ap.package_list()

        if err:
            p.putString("result", "Error")
            p.putString("errorCode", ap.ERR_SM_FILE_LIST_ERROR)
            p.putString("errorMessage", str(err))
        else:
            p.putString("result", "OK")
            response = MapValue()
            file_lv = ListValue()
            for f in files:
                file_lv.addString(f)
            response.put("fileList", file_lv)
            p.put("response", response)

    except Exception, e:
        logging_util.error("PARAM_SCRIPT_MANAGER_FILELIST error: {}".format(e))
        p.putString("result", "Error")
        p.putString("errorCode", ap.ERR_SM_FILE_LIST_ERROR)
        p.putString("errorMessage", str(e))

    logging_util.debug("PARAM_SCRIPT_MANAGER_FILELIST return pack")
    data_pack.SendHide(p)


def _handle_script_manager_filedelete(p, pcode, oid, data_pack):
    import whatap.agent.control.agent_patch as ap

    logging_util.debug("PARAM_SCRIPT_MANAGER_FILEDELETE pack received")
    p = p.toResponse()
    p.pcode = pcode
    p.oid = oid

    try:
        data_map = p.getMap("data")
        file_path = data_map.getText("file")
        file_name = os.path.basename(file_path) if file_path else ""

        err = ap.package_delete(file_name)

        if err:
            p.putString("result", "Error")
            p.putString("errorCode", ap.ERR_SM_FILE_DELETE_ERROR)
            p.putString("errorMessage", str(err))
        else:
            p.putString("result", "OK")

    except Exception, e:
        logging_util.error("PARAM_SCRIPT_MANAGER_FILEDELETE error: {}".format(e))
        p.putString("result", "Error")
        p.putString("errorCode", ap.ERR_SM_FILE_DELETE_ERROR)
        p.putString("errorMessage", str(e))

    logging_util.debug("PARAM_SCRIPT_MANAGER_FILEDELETE return pack")
    data_pack.SendHide(p)


def _handle_script_manager_patch(p, pcode, oid, data_pack):
    import whatap.agent.control.agent_patch as ap

    logging_util.debug("PARAM_SCRIPT_MANAGER_PATCH pack received")
    p = p.toResponse()
    p.pcode = pcode
    p.oid = oid

    try:
        data_map = p.getMap("data")
        path = data_map.getText("file")
        file_name = os.path.basename(path) if path else ""

        ap.agent_patch(file_name)
        p.putString("result", "OK")

    except ap.AgentPatchError, e:
        p.putString("result", "Error")
        p.putString("errorCode", e.error_code())
        p.putString("errorMessage", str(e))
    except Exception, e:
        logging_util.error("PARAM_SCRIPT_MANAGER_PATCH error: {}".format(e))
        p.putString("result", "Error")
        p.putString("errorCode", ap.ERR_SM_AGENT_PATCH_GENERAL)
        p.putString("errorMessage", str(e))

    logging_util.debug("PARAM_SCRIPT_MANAGER_PATCH return pack")
    data_pack.SendSecure(p)


def _handle_script_manager_patch_status(p, pcode, oid, data_pack):
    import whatap.agent.control.agent_patch as ap
    from whatap.agent.counter.counter_manager import AGENT_VERSION

    logging_util.debug("PARAM_SCRIPT_MANAGER_PATCH_STATUS pack received")
    p = p.toResponse()
    p.pcode = pcode
    p.oid = oid

    try:
        status = ap.agent_patch_status()
        p.putString("result", "OK")

        if status:
            response = MapValue()
            response.putString("status", status.status)
            response.putString("patchVersion", status.patch_version)
            response.putString("currentVersion", AGENT_VERSION)
            response.putString("patchTime", status.start_time)
            response.putString("errorMessage", status.error_message)
            p.put("response", response)

    except ap.AgentPatchError, e:
        p.putString("result", "Error")
        p.putString("errorCode", e.error_code())
        p.putString("errorMessage", str(e))
    except Exception, e:
        logging_util.error("PARAM_SCRIPT_MANAGER_PATCH_STATUS error: {}".format(e))
        p.putString("result", "Error")
        p.putString("errorCode", ap.ERR_SM_AGENT_PATCH_GENERAL)
        p.putString("errorMessage", str(e))

    logging_util.debug("PARAM_SCRIPT_MANAGER_PATCH_STATUS return pack")
    data_pack.SendSecure(p)

