import logging
import json
import requests
from datetime import datetime
import os
import time
import traceback
import subprocess
import math

class pyCreatia:
    #Constants
    CREATIA_AUTH_ISSUE = 'auth/issue/'
    CREATIA_AUTH_REVOKE = 'auth/revoke/'

    CREATIA_NETWORK_ADD = 'internal/organisation/_creatia_id/network'
    CREATIA_NETWORK_CLEAN = 'internal/network/_prefix_id'
    CREATIA_NETWORK_AS = 'internal/network/as'

    CREATIA_ORGANISATION_IDENTITIES = 'internal/organisation'
    CREATIA_ORGANISATION_NETWORKS = 'internal/organisation/_creatia_id/network'
    CREATIA_ORGANISATION_CREATE = 'internal/organisation'

    CREATIA_USER_CREATE = 'internal/organisation/_creatia_id/user'

    def __init__(self, endpoint: str, identify_token: str, secret_token: str):
        self.creatia_logger = logging.getLogger("creatia_api")

        self.identify_token = identify_token
        self.secret_token = secret_token

        self.creatia_bearer_token = None
        self.creatia_bearer_expires = 0

        self.endpoint = endpoint

    def is_token_expired(self):
        return (float(datetime.now().timestamp()) > self.creatia_bearer_expires)

    def creatia_authenticate(self) -> bool:
        self.creatia_logger.info("Authenicating")

        if self.is_token_expired():
            self.creatia_logger.info("Expired or empty token")
            response = requests.post(self.endpoint + self.CREATIA_AUTH_ISSUE, headers=self.creatia_construct_headers(), data={
                'identifier': self.identify_token,
                'token': self.secret_token
            })

            if response.ok:
                self.creatia_logger.info("Valid authentication attempt")
                self.creatia_bearer_token = response.json()["bearer"]
                self.creatia_bearer_expires = response.json()["expiry"]
                return True

            self.creatia_logger.error(f"Failed authetication - {response.status_code} - {response.text}")
            raise Exception("Failed authetication")

    def creatia_construct_headers(self) -> dict:
        headers = {
            'User-Agent': f'pyCreatia-Goliath'
        }

        if self.creatia_bearer_token is not None and not self.is_token_expired():
            headers['Authorization'] = 'Bearer ' + self.creatia_bearer_token

        return headers

    def creatia_revoke(self) -> bool:
        self.creatia_authenticate()
        self.creatia_logger.info("Revoking")

        if self.creatia_bearer_token is not None:
            response = requests.post(self.endpoint + self.CREATIA_AUTH_REVOKE, headers=self.creatia_construct_headers())

            if response.status_code == 200:
                self.creatia_logger.info("Token revoked")

                self.creatia_bearer_token = None
                self.creatia_bearer_expires = 0
                return True

        self.creatia_logger.error(f"Failed token revoke - {response.status_code} - {response.text}")
        raise Exception("Failed token revoke")

    def creatia_get(self, endpoint: str, in_params: dict) -> dict:
        self.creatia_authenticate()
        self.creatia_logger.debug(f"GET REQUEST: {endpoint} - {in_params}")
        response = requests.get(self.endpoint + endpoint, params=in_params, headers=self.creatia_construct_headers())

        if response.status_code == 200:
            self.creatia_logger.debug(f"GET RESPONSE: {response.status_code} - {response.text}")
            return response.json()

        raise Exception("GET invalid", response.status_code, response.text)

    def creatia_post(self, endpoint: str, payload: dict, files: dict = {}) -> dict:
        self.creatia_authenticate()
        response = requests.post(self.endpoint + endpoint, json=payload, headers=self.creatia_construct_headers(), files=files)

        if response.status_code == 200:
            return response.json()

        raise Exception("POST invalid", response.status_code, response.text)

    def creatia_patch(self, endpoint: str, payload: dict) -> dict:
        self.creatia_authenticate()
        response = requests.patch(self.endpoint + endpoint, json=payload, headers=self.creatia_construct_headers())

        if response.status_code == 200:
            return response.json()

        raise Exception("PATCH invalid", response.status_code, response.text)

    def organisation_external_identities(self, application_key: str, application_id: str = None) -> dict:
        self.creatia_authenticate()

        payload = {'application': application_key} if application_id is None else {'application': application_key, 'identity': application_id}

        return self.creatia_get(self.CREATIA_ORGANISATION_IDENTITIES, payload)

    def organisation_networks(self, creatia_id: int) -> dict:
        self.creatia_authenticate()

        return self.creatia_get(self.CREATIA_ORGANISATION_NETWORKS.replace("_creatia_id", f"{creatia_id}"), {})

    def network_add(self, creatia_id: int, subnet: str, name: str, prefix_only: bool, children_allowed: bool) -> dict:
        self.creatia_authenticate()

        return self.creatia_post(self.CREATIA_NETWORK_ADD.replace("_creatia_id", f"{creatia_id}"), {
            'subnet': subnet,
            'name': name,
            'prefix_only': prefix_only,
            'children_allowed': children_allowed
        })

    def network_clean(self, prefix_id: int) -> dict:
        self.creatia_authenticate()

        return self.creatia_post(self.CREATIA_NETWORK_CLEAN.replace("_prefix_id", f"{prefix_id}"), {})

    def organisation_add(self, name: str, identifiers: list, subtenant: bool) -> dict:
        self.creatia_authenticate()

        return self.creatia_post(self.CREATIA_ORGANISATION_CREATE, {
            'name': name,
            'identifiers': identifiers,
            'subtenant': subtenant
        })

    def user_add(self, creatia_id: int, email: str, name: str) -> dict:
        self.creatia_authenticate()

        return self.creatia_post(self.CREATIA_USER_CREATE.replace("_creatia_id", f"{creatia_id}"), {
            'name': name,
            'email': email
        })

    def network_as(self) -> dict:
        self.creatia_authenticate()

        return self.creatia_get(self.CREATIA_NETWORK_AS, {})


api = pyCreatia('https://spicy-creatia.securelayer.com/api/', 'DIkkQZyXHCkMLpqS7VnceWp12Oemq0oM', '74gnyS2Zht9Kb30aLWfew9rYTTa4FkJQ')


#print(api.organisation_add('Jesse Test Pty Ltd 123', [{'key': 'development','identity': '1'}], True))
#print(api.user_add(194, 'jessec+123456@globalsecurelayer.com', 'Jesse Cunningham 2'))
#print(api.organisation_external_identities('development'))
#print(api.organisation_networks(194))
#print(api.network_add(194, '103.1.213.13/32', 'Devbox 2', 1, 1))
#print(api.network_clean(30860))

print(api.network_as())