import requests
import urllib3
import os
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# STG -> 200.57.183.158:19443
BASE_URL = os.getenv('CLOUDIAN_API_URL','https://200.57.183.158:19443')
USER = os.getenv('CLOUDIAN_USER','')
PASS = os.getenv('CLOUDIAN_PASS','')
REQUEST_LIMIT = os.getenv('CLOUDIAN_REQUEST_LIMIT',100)
RATING_PLAN = os.getenv('CLOUDIAN_DEFAULT_RT',"Default-RP")
VERIFY_SSL = os.getenv("VERIFY_SSL", "False") == "True"
PROXY_STRING = os.getenv('CLOUDIAN_PROXY_STRING',"")

# DICT ELEMENTS FLAGS
ERROR = 'error'
MSG = 'msg'
DATA = 'data'

# RESPONSE CODES DICT
COMMON_CODES = {
    401 : 'credenciales incorrectas'
    }
GROUP_CUSTOM_CODES = COMMON_CODES | {
    409 : 'el grupo existe.'
    }
USER_CUSTOM_CODES = COMMON_CODES | {
    409 : 'el usuario existe.'
    }
S3_CUSTOM_CODES = COMMON_CODES | {
    403 : 'número maximo de credenciales s3 alcanzado'
    }
DELETE_CODES = COMMON_CODES | {
    409 : 'el grupo no esta vacio.'
}

class Consumer:
    def __init__(self,url:str=BASE_URL, usr:str=USER, password:str=PASS, rq:int=REQUEST_LIMIT):
        self.url = url
        self.usr = usr
        self.password = password
        self.request_limit = rq

    def createSesion(self,verifySSL:bool=True, proxyString:str=''):
        response={}
        url=self.url
        usr=self.usr
        password=self.password
        if url and usr and password:
            self.session = requests.Session()
            self.session.auth = (usr, password)
            self.session.verify=verifySSL
            if proxyString:
                self.session.proxies = { "http":proxyString, "https":proxyString}
            response[MSG]='sesion inicializada.'
        else:
            response[ERROR]='no existen credenciales.'
        return response

    def get(self,method:str,data:dict={},elementOfset:str=""):
        res = {}
        try:
            session = self.session
            if elementOfset: data['limit'] = self.request_limit
            response = session.get(url=f'{self.url}/{method}',params=data)
            return_data=[]
            if response.status_code != 200:
                res[ERROR] = f'error en la petición "{method}".'
                res[MSG] = response.status_code
            else:
                while len(response.json()) >= self.request_limit:
                    return_data += response.json()[:self.request_limit]
                    if elementOfset: data['offset'] = response.json()[-1][elementOfset]
                    response = session.get(url=f'{self.url}/{method}',params=data)
                return_data += response.json()
                res[DATA] = return_data
                res[MSG] = 'ok'
        except requests.exceptions.ConnectionError as e:
            res[ERROR] = f'server inalcanzable "{method}".'
        return res
    
    def put(self,method:str,data:dict={},asPayload:bool=True):
        res = {}
        session = self.session
        headers = {"Content-Type": "application/json"}
        try:
            response = session.put(url=f'{self.url}/{method}',json=data,headers=headers) if asPayload else session.put(url=f'{self.url}/{method}',params=data)
            if response.status_code != 200:
                res[ERROR] = f'error en la petición "{method}".'
                res[MSG] = response.status_code
            else:
                res[MSG] = 'petición realizada con exito.'
                if response.text: res[DATA]=response.json()
        except requests.exceptions.ConnectionError as e:
            res[ERROR] = f'server inalcanzable "{method}".'
        return res

    def delete(self,method:str,data:dict={}):
        res = {}
        session = self.session
        try:
            response = session.delete(url=f'{self.url}/{method}',params=data)
            if response.status_code != 200:
                res[ERROR] = f'error en la petición "{method}".'
                res[MSG] = response.status_code
            else:
                res[MSG] = 'petición realizada con exito.'
        except requests.exceptions.ConnectionError as e:
            res[ERROR] = f'server inalcanzable "{method}".'
        return res

    def post(self,method:str,data:dict={}):
        res = {}
        session = self.session
        try:
            response = session.post(url=f'{self.url}/{method}',params=data)
            if response.status_code != 200:
                res[ERROR] = f'error en la petición "{method}".'
                res[MSG] = response.status_code
            else:
                res[MSG] = 'petición realizada con exito.'
        except requests.exceptions.ConnectionError as e:
            print(e)
            res[ERROR] = f'server inalcanzable "{method}".'
        return res

class CloudianConsumer(Consumer):
    def __init__(self,usr:str=USER, password:str=PASS,verifySSL:bool=VERIFY_SSL, proxyString:str=PROXY_STRING):
        Consumer.__init__(self,usr=usr,password=password)
        self.createSesion(verifySSL=verifySSL, proxyString=proxyString)
    
    def createGroup(self,groupId:str, groupName:str, ratingPlanId:str=RATING_PLAN):
        # Estructura por defecto de la petición
        groupData = {
            "active" : "true",
            "groupId" : groupId,
            "groupName" : groupName,
            "ldapEnabled" : False,
            "s3endpointshttp" : ["ALL"],
            "s3endpointshttps" : ["ALL"],
            "s3websiteendpoints" : ["ALL"]
        }

        # Crear el Grupo
        group = self.put(method='group',data=groupData)
        if ERROR in group:
            group[ERROR] = (f'No se pudo crear el grupo, {GROUP_CUSTOM_CODES[group[MSG]]}'
                if group[MSG] in GROUP_CUSTOM_CODES else f'No se pudo crear el grupo, {group[ERROR]}')
            group[MSG]=str(group[MSG])
            return group

        # Asignar el Rating Plan
        rp = self.post(method='group/ratingPlanId', data={'groupId':groupId,'ratingPlanId':ratingPlanId})
        if ERROR in rp:
            rp[ERROR] = (f'No fue posible asignar rating plan, {COMMON_CODES[rp[MSG]]}'
                if rp[MSG] in COMMON_CODES else f'No fue posible asignar rating plan, {rp[ERROR]}')
            rp[MSG]=str(rp[MSG])
            return rp
        rp.update({MSG:'creación de grupo ok.'})
        return rp

    def createUser(self,groupId:str, userId:str, fullName:str='',isGroupAdmin:bool=False):
        # Estructura por defecto de la petición
        userData = {
            "active" : "true",
            "groupId" : groupId,
            "userId" : userId,
            "fullName" : fullName,
            "userType" : "GroupAdmin" if isGroupAdmin else "User"
        }

        # Crear el usuario
        user = self.put(method='user',data=userData)
        if ERROR in user:
            user[ERROR] = (f'No se pudo crear el usuario, {USER_CUSTOM_CODES[user[MSG]]}'
                if user[MSG] in USER_CUSTOM_CODES else f'No se pudo crear el usuario, {user[ERROR]}')
            user[MSG]=str(user[MSG])
            return user
        user.update({MSG:'creación de usuario ok.'})
        return user

    def createS3Access(self,groupId:str, userId:str):
        # Estructura por defecto de la petición
        userData = {
            "groupId" : groupId,
            "userId" : userId,
        }

        # Crear el Acceso S3
        s3Access = self.put(method='user/credentials',data=userData,asPayload=False)
        if ERROR in s3Access:
            s3Access[ERROR] = (f'No se pudo crear el usuario, {S3_CUSTOM_CODES[s3Access[MSG]]}'
                if s3Access[MSG] in S3_CUSTOM_CODES else f'No se pudo crear el usuario, {s3Access[ERROR]}')
            s3Access[MSG]=str(s3Access[MSG])
            return s3Access
        s3Access.update({MSG:'creación del acceso s3 ok.'})
        return s3Access
    
    def listGroups(self,prefix:str=''): #Prefijo para el groupId
        # Estructura por defecto de la petición
        searchData = {
            "prefix" : prefix 
        }

        # Buscar Grupos
        groups = self.get(method='group/list',data=searchData, elementOfset="groupId")
        if ERROR in groups:
            groups[ERROR] = (f'error al listar los grupos, {COMMON_CODES[groups[MSG]]}'
                if groups[MSG] in COMMON_CODES else f'error al listar los grupos, {groups[ERROR]}')
            groups[MSG]=str(groups[MSG])
            return groups
        groups.update({MSG:f'{len(groups[DATA])} grupos encontrados.'})
        return groups
    
    def listUserByGroup(self,groupId:str,onlyActives:bool=True):
        # Estructura por defecto de la petición
        searchData = {
            'userType' : 'all',
            'userStatus' : 'active' if onlyActives else 'all',
            'groupId' : groupId
        }

        # Buscar Usuarios
        users = self.get(method='user/list',data=searchData, elementOfset="userId")
        if ERROR in users:
            users[ERROR] = (f'error al listar los usuarios, {COMMON_CODES[users[MSG]]}'
                if users[MSG] in COMMON_CODES else f'error al listar los usuarios, {users[ERROR]}')
            users[MSG]=str(users[MSG])
            return users
        users.update({MSG:f'{len(users[DATA])} usuarios encontrados.'})
        return users

    def listS3AccessByUserGroup(self,groupId:str,userId:str,isRootAccountOnly:bool=False):
        # Estructura por defecto de la petición
        searchData = {
            'groupId' : groupId,
            'userId' : userId,
            'isRootAccountOnly' : isRootAccountOnly
        }

        # Buscar Accesos S3
        s3Access = self.get(method='user/credentials/list',data=searchData)
        if ERROR in s3Access:
            s3Access[ERROR] = (f'error al listar los accesos s3, {COMMON_CODES[s3Access[MSG]]}'
                if s3Access[MSG] in COMMON_CODES else f'error al listar los accesos s3, {s3Access[ERROR]}')
            s3Access[MSG]=str(s3Access[MSG])
            return s3Access
        s3Access.update({MSG:f'{len(s3Access[DATA])} accesos s3 encontrados.'})
        return s3Access

    def deleteUser(self,groupId:str,userId:str):
        # Estructura por defecto de la petición
        deleteData = {
            'groupId' : groupId,
            'userId' : userId,
        }

        # Borrar Usuario
        response = self.delete(method='user',data=deleteData)
        if ERROR in response:
            response[ERROR] = (f'error al borrar usuario, {COMMON_CODES[response[MSG]]}'
                if response[MSG] in COMMON_CODES else f'error al borrar usuario, {response[ERROR]}')
            response[MSG]=str(response[MSG])
            return response
        response.update({MSG:f'usuario "{userId}" del grupo "{groupId}" borrado.'})
        return response

    def deleteGroup(self,groupId:str):
        # Estructura por defecto de la petición
        deleteData = {
            'groupId' : groupId,
        }

        # Borrar Usuario
        response = self.delete(method='group',data=deleteData)
        if ERROR in response:
            response[ERROR] = (f'error al borrar grupo, {DELETE_CODES[response[MSG]]}'
                if response[MSG] in DELETE_CODES else f'error al borrar grupo, {response[ERROR]}')
            response[MSG]=str(response[MSG])
            return response
        response.update({MSG:f'grupo "{groupId}" borrado.'})
        return response

    # No es necesario ya que al borrar usuario se eliminan todos los accesos S3.
    def deleteS3Access(self,accessKey:str):
        # Estructura por defecto de la petición
        deleteData = {
            'accessKey' : accessKey,
        }

        # Borrar Usuario
        response = self.delete(method='user/credentials',data=deleteData)
        if ERROR in response:
            response[ERROR] = (f'error al borrar acceso s3, {DELETE_CODES[response[MSG]]}'
                if response[MSG] in DELETE_CODES else f'error al borrar acceso s3, {response[ERROR]}')
            response[MSG]=str(response[MSG])
            return response
        response.update({MSG:f'acceso s3 "{accessKey}" borrado.'})
        return response