import os
import numpy as np
import pandas as pd
import logging
import json
from requests.auth import HTTPBasicAuth

import commonfunctions.core
import commonfunctions.xmltodict


class QualysClient():
    locallogger = logging.getLogger("QualysClient")
    locallogger.setLevel(logging.DEBUG)
    

    def __init__(self, qualysUserName, qualysPassword, proxies):
        self.qualysAuth =  HTTPBasicAuth(qualysUserName, qualysPassword)
        self.proxies = proxies

        self.base_url = 'https://qualysapi.qualys.com/api/2.0/fo/'
        self.knowledge_base_url = self.base_url + 'knowledge_base/vuln/'
        self.search_list_url = self.base_url + 'qid/search_list/static/'
        self.vm_option_profile_url = self.base_url + 'subscription/option_profile/vm/'
        self.vm_scan_url = self.base_url + 'scan/'
        self.report_url = self.base_url + 'report/'
        self.scan_report_template_url = self.report_url + 'template/scan/'

        self.headers =  {
            "X-Requested-With" : "Assurance-Analytics",
            "Content-type": "text/html; charset=utf-8",
        }


    def getSearchListDf(self,params = {}, echo_response = False):
        _params = {
            'action' : 'list', #Required
            **params
        }
        response, _ = commonfunctions.core.getResponseFromURL(url=self.search_list_url, method='GET', headers=self.headers, auth=self.qualysAuth,  proxies=self.proxies, data=None, json=None, params=_params, verify=False, timeout=600, session=None, maxRetry=3, loggingPrefix=self.locallogger.name, responseEncoding='utf-8')
        if echo_response: self.locallogger.info(response.text)
        xml_dict = commonfunctions.xmltodict.parse(response.text)
        search_list_df_raw = pd.json_normalize(xml_dict['STATIC_SEARCH_LIST_OUTPUT']['RESPONSE']['STATIC_LISTS']['STATIC_LIST'])
        return search_list_df_raw

    def getOptionProfileListDf(self,params = {}, echo_response = False):
        _params = {
            'action' : 'list', #Required
            **params
        }
        response, _ = commonfunctions.core.getResponseFromURL(url=self.vm_option_profile_url, method='GET', headers=self.headers, auth=self.qualysAuth,  proxies=self.proxies, data=None, json=None, params=_params, verify=False, timeout=600, session=None, maxRetry=3, loggingPrefix=self.locallogger.name, responseEncoding='utf-8')
        if echo_response: self.locallogger.info(response.text)
        
        xml_dict = commonfunctions.xmltodict.parse(response.text)
        option_profile_df_raw = pd.json_normalize(xml_dict['OPTION_PROFILES']['OPTION_PROFILE'])
        return option_profile_df_raw


    def getScanListDf(self, params = {}, echo_response = False):
        _params = {
            'action' : 'list', #Required
            'type' : 'API',
            **params
        }

        response, _ = commonfunctions.core.getResponseFromURL(url=self.vm_scan_url, method='GET', headers=self.headers, auth=self.qualysAuth,  proxies=self.proxies, data=None, json=None, params=_params, verify=False, timeout=600, session=None, maxRetry=3, loggingPrefix=self.locallogger.name, responseEncoding='utf-8')
        if echo_response: self.locallogger.info(response.text)
        xml_dict = commonfunctions.xmltodict.parse(response.text)
        scan_list_df = pd.json_normalize(xml_dict['SCAN_LIST_OUTPUT']['RESPONSE']['SCAN_LIST']['SCAN'])
        return scan_list_df


    def getKnowledebaseDf(self, params = {}, echo_response = False):
        _params = {
            'action' : 'list', #Required
            **params
        }
        response, _ = commonfunctions.core.getResponseFromURL(url=self.knowledge_base_url, method='GET', headers=self.headers, auth=self.qualysAuth,  proxies=self.proxies, data=None, json=None, params=_params, verify=False, timeout=600, session=None, maxRetry=3, loggingPrefix=self.locallogger.name, responseEncoding='utf-8')
        if echo_response: self.locallogger.info(response.text)
        xml_dict = commonfunctions.xmltodict.parse(response.text)
        vul_list_df_raw = pd.json_normalize(xml_dict['KNOWLEDGE_BASE_VULN_LIST_OUTPUT']['RESPONSE']['VULN_LIST']['VULN'])
        return vul_list_df_raw

    def getReport(self, report_id, params ={}, echo_response = False):
        _params = {
            'action' : 'fetch', #Required
            'id' : report_id, 
            **params
        }
        
        response, _ = commonfunctions.core.getResponseFromURL(url=self.report_url, method='GET', headers=self.headers, auth=self.qualysAuth,  proxies=self.proxies, data=None, json=None, params=_params, verify=False, timeout=600, session=None, maxRetry=3, loggingPrefix=self.locallogger.name, responseEncoding='utf-8')
        if echo_response: self.locallogger.info(response.text)
        return response.text

    def createSearchList(self, title, qids, params = {}, echo_response = False):
        _params = {
            'action' : 'create', #Required
            'title' : title,
            'qids' : qids,
            **params
        }
        response, _ = commonfunctions.core.getResponseFromURL(url=self.search_list_url, method='POST', headers=self.headers, auth=self.qualysAuth,  proxies=self.proxies, data=None, json=None, params=_params, verify=False, timeout=600, session=None, maxRetry=3, loggingPrefix=self.locallogger.name, responseEncoding='utf-8')
        if echo_response: self.locallogger.info(response.text)
        xml_dict = commonfunctions.xmltodict.parse(response.text)
        return  xml_dict['SIMPLE_RETURN']['RESPONSE']['ITEM_LIST']['ITEM']['VALUE']
 
    def createOptionProfile(self, title, params, echo_response = False):
        _params = {
            'action' : 'create', #Required
            'title' : title,
            **params
        }

        response, _ = commonfunctions.core.getResponseFromURL(url=self.vm_option_profile_url, method='POST', headers=self.headers, auth=self.qualysAuth,  proxies=self.proxies, data=None, json=None, params=_params, verify=False, timeout=600, session=None, maxRetry=3, loggingPrefix=self.locallogger.name, responseEncoding='utf-8')
        if echo_response: self.locallogger.info(response.text)
        xml_dict = commonfunctions.xmltodict.parse(response.text)
        return xml_dict['SIMPLE_RETURN']['RESPONSE']['ITEM_LIST']['ITEM']['VALUE']

    def updateReportTemplate(self, report_template, template_id , params = {}, echo_response = False):

        _params = {
            'action' : 'update', #Required
            'report_format' : 'xml',
            'template_id' : template_id,
            **params
        }

        _custom_headers = {
            "X-Requested-With" : "Assurance-Analytics",
            "Content-type": "text/xml",
        }

        response, _ = commonfunctions.core.getResponseFromURL(url=self.scan_report_template_url, method='POST', headers=_custom_headers, auth=self.qualysAuth,  proxies=self.proxies, data=report_template, json=None, params=_params, verify=False, timeout=600, session=None, maxRetry=3, loggingPrefix=self.locallogger.name, responseEncoding='utf-8')
        if echo_response: self.locallogger.info(response.text)


    def launchScan(self, scan_title, params, echo_response = False):
        _params = {
            'action' : 'launch', #Required
            'scan_title' : scan_title,
            **params
        }
        response, _ = commonfunctions.core.getResponseFromURL(url=self.vm_scan_url, method='POST', headers=self.headers, auth=self.qualysAuth,  proxies=self.proxies, data=None, json=None, params=_params, verify=False, timeout=600, session=None, maxRetry=3, loggingPrefix=self.locallogger.name, responseEncoding='utf-8')
        if echo_response: self.locallogger.info(response.text)
        xml_dict = commonfunctions.xmltodict.parse(response.text)

        return xml_dict['SIMPLE_RETURN']['RESPONSE']['ITEM_LIST']['ITEM'][1]['VALUE']

    def launchReport(self,params , echo_response = False):
        _params = {
            'action' : 'launch', #Required
            **params
        } 

        response, _ = commonfunctions.core.getResponseFromURL(url=self.report_url, method='POST', headers=self.headers, auth=self.qualysAuth,  proxies=self.proxies, data=None, json=None, params=_params, verify=False, timeout=600, session=None, maxRetry=3, loggingPrefix=self.locallogger.name, responseEncoding='utf-8')
        if echo_response: self.locallogger.info(response.text)
        xml_dict = commonfunctions.xmltodict.parse(response.text)

        self.locallogger.info(response.text)
        return xml_dict['SIMPLE_RETURN']['RESPONSE']['ITEM_LIST']['ITEM']['VALUE']




    def deleteSearchList(self, ids_str, params={}, echo_response = False):
        _params = {
            'action' : 'delete', #Required
            'id' : ids_str, #Required
            **params
        }
        response, _ = commonfunctions.core.getResponseFromURL(url=self.search_list_url, method='POST', headers=self.headers, auth=self.qualysAuth,  proxies=self.proxies, data=None, json=None, params=_params, verify=False, timeout=600, session=None, maxRetry=3, loggingPrefix=self.locallogger.name, responseEncoding='utf-8')
        if echo_response: self.locallogger.info(response.text)

    def deleteOptioinProfiles(self,ids_str,params={}, echo_response = False):
        _params = {
            'action' : 'delete', #Required
            'id' : ids_str,
            **params
        }
        response, _ = commonfunctions.core.getResponseFromURL(url=self.vm_option_profile_url, method='POST', headers=self.headers, auth=self.qualysAuth,  proxies=self.proxies, data=None, json=None, params=_params, verify=False, timeout=600, session=None, maxRetry=3, loggingPrefix=self.locallogger.name, responseEncoding='utf-8')
        if echo_response: self.locallogger.info(response.text)



    def clearExistingItems(self, filter_str = "(Test)"):
        search_list_df_raw = self.getSearchListDf()
        # filter_str = "(Test)"
        target_search_list_items = search_list_df_raw[search_list_df_raw.TITLE.str.contains(filter_str, regex=False)]
        target_search_list_ids = target_search_list_items.ID.to_list()
        # print(target_search_list_ids)
        self.locallogger.info(f'Clearing Seachlist and Option Profile of which name containing keyword "{filter_str}".')
        for i in range(len(target_search_list_ids)):
            self.deleteSearchList(target_search_list_ids[i], echo_response=True)

        option_profile_df_raw = self.getOptionProfileDf()
        target_option_profile_items = option_profile_df_raw[option_profile_df_raw['BASIC_INFO.GROUP_NAME'].str.contains(filter_str, regex=False)]
        target_option_profile_ids = target_option_profile_items['BASIC_INFO.ID'].to_list()
        for i in range(len(target_option_profile_ids)):
            self.deleteOptioinProfiles(target_option_profile_ids[i], echo_response=True)

        # print(target_option_profile_ids)


    def customAPICall(self, url, request, headers, params=None, data=None):
        _params = params
        response, outcome = commonfunctions.core.getResponseFromURL(url=url, method=request, headers=headers, auth=self.qualysAuth,  proxies=self.proxies, data=data, params=_params, verify=False, timeout=600, session=None, maxRetry=3, loggingPrefix=self.locallogger.name, responseEncoding='utf-8')
        return response, outcome

