#!/usr/bin/python3

from ansible.module_utils.basic import AnsibleModule
import requests
import json


class InfluxdbAuth:
    def __init__(self, api_url, api_token, org, user, permissions):
        self.api_url = api_url
        self.headers = {'Authorization': 'Token {}'.format(api_token)}
        self.org = org
        self.user = user
        self.permissions = permissions

    def exists(self):
        url = '{}/api/v2/authorizations'.format(self.api_url)
        r = requests.get(
            url,
            headers=self.headers,
            params={'org': self.org, 'user': self.user}
        )

        if r.status_code == 404:
            return False
        elif r.status_code == 200:
            data = json.loads(r.text)
            for auth in data['authorizations']:
                if self.check_permissions(auth['permissions']):
                    self.id = auth['id']
                    self.status = auth['status']
                    self.description = auth['description']
                    return True
            return False

        raise Exception(
            'Influxdb',
            'Bad status code {}: {}'.format(r.status_code, r.text)
        )

    def check_permissions(self, permissions):
        for pa in self.permissions:
            find = False
            for pb in permissions:
                if pa['action'] != pb['action']:
                    continue
                if pa['resource']['type'] != pb['resource']['type']:
                    continue
                if 'org' in pa['resource'] and 'org' in pb['resource']:
                    if pa['resource']['org'] != pb['resource']['org']:
                        continue
                if 'name' in pa['resource'] and 'name' in pb['resource']:
                    if pa['resource']['name'] != pb['resource']['name']:
                        continue
                find = True
                break

            if not find:
                return False
        return True

    def transform_permissions(self):
        permissions = list()
        for perm in self.permissions:
            org = None
            if 'org' in perm['resource'] and 'orgID' not in perm['resource']:
                org = perm['resource']['org']
                perm['resource']['orgID'] = self.get_orgid(org)
            if 'name' in perm['resource'] and 'id' not in perm['resource']:
                if perm['resource']['type'] == 'buckets':
                    if org is None:
                        org = self.org
                    perm['resource']['id'] = self.get_bucketid(
                            perm['resource']['name'], org)
            permissions.append(perm)

        return permissions

    def has_changed(self, status, description):
        if self.status != status:
            return True
        if self.description != description:
            return True
        return False

    def create(self, status, description):
        url = '{}/api/v2/authorizations'.format(self.api_url)
        r = requests.post(
            url,
            headers=self.headers,
            json={
                'userID': self.get_userid(self.user, self.org),
                'orgID': self.get_orgid(self.org),
                'status': status,
                'description': description,
                'permissions': self.transform_permissions()
            }
        )

        if r.status_code != 201:
            raise Exception(
                'Influxdb',
                'Bad status code {}: {}'.format(r.status_code, r.text)
            )

    def delete(self):
        url = '{}/api/v2/authorizations/{}'.format(self.api_url, self.id)
        r = requests.delete(url, headers=self.headers)

        if r.status_code != 204:
            raise Exception(
                'Influxdb',
                'Bad status code {}: {}'.format(r.status_code, r.text)
            )

    def update(self, status, description):
        url = '{}/api/v2/authorizations/{}'.format(self.api_url, self.id)
        r = requests.patch(
            url,
            headers=self.headers,
            json={
                'description': description,
                'status': status
            }
        )

        if r.status_code != 200:
            raise Exception(
                'Influxdb',
                'Bad status code {}: {}'.format(r.status_code, r.text)
            )

    def get_bucketid(self, bucket_name, org):
        url = '{}/api/v2/buckets'.format(self.api_url)
        r = requests.get(
            url,
            headers=self.headers,
            params={'name': bucket_name, 'org': org}
         )

        if r.status_code != 200:
            raise Exception(
                'Influxdb',
                'Bad status code {}: {}'.format(r.status_code, r.text)
            )

        data = json.loads(r.text)
        for bucket in data['buckets']:
            if bucket['name'] == bucket_name:
                return bucket['id']

        raise Exception('Influxdb', 'Don\'t get the bucketID')

    def get_orgid(self, org_name):
        url = '{}/api/v2/orgs'.format(self.api_url)
        r = requests.get(url, headers=self.headers, params={'org': org_name})

        if r.status_code != 200:
            raise Exception(
                'Influxdb',
                'Bad status code {}: {}'.format(r.status_code, r.text)
            )

        data = json.loads(r.text)
        for org in data['orgs']:
            if org['name'] == org_name:
                return org['id']

        raise Exception('Influxdb', 'Don\'t get the orgID')

    def get_userid(self, user_name, org):
        url = '{}/api/v2/users'.format(self.api_url)
        r = requests.get(
            url,
            headers=self.headers,
            params={'name': user_name, 'org': org}
        )

        if r.status_code != 200:
            raise Exception(
                'Influxdb',
                'Bad status code {}: {}'.format(r.status_code, r.text)
            )

        data = json.loads(r.text)
        for user in data['users']:
            if user['name'] == user_name:
                return user['id']

        raise Exception('Influxdb', 'Don\'t get the userID')


def main():
    fields = {
        'user':        {'type': 'str', 'required': True},
        'org':         {'type': 'str', 'required': True},
        'permissions': {'type': 'list', 'required': True},
        'status':      {'type': 'str',
                        'default': 'active',
                        'choice': ['active', 'inactive']},
        'description': {'type': 'str', 'default': ''},
        'api_url':     {'type': 'str', 'default': 'http://127.0.0.1:8086'},
        'api_token':   {'type': 'str', 'required': True},
        'state':       {'type': 'str',
                        'default': 'present',
                        'choice': ['present', 'absent']},
    }
    module = AnsibleModule(argument_spec=fields)

    auth = InfluxdbAuth(
        module.params['api_url'],
        module.params['api_token'],
        module.params['org'],
        module.params['user'],
        module.params['permissions'],
    )
    changed = False

    if auth.exists():
        if module.params['state'] == 'absent':
            auth.delete()
            changed = True
        elif auth.has_changed(module.params['status'],
                              module.params['description']):
            auth.update(module.params['status'], module.params['description'])
            changed = True
    elif module.params['state'] == 'present':
        auth.create(module.params['status'], module.params['description'])
        changed = True

    module.exit_json(changed=changed)


if __name__ == '__main__':
    main()