#!/usr/bin/env python
# encoding: utf-8
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

import json

from flask import request

from pyoauth2.provider import ResourceAuthorization
from pyoauth2.provider import ResourceProvider
from pyoauth2.provider import AuthorizationProvider


from gstack import db
from gstack.services import requester
from gstack.models.client import Client
from gstack.models.accesstoken import AccessToken
from gstack.models.refreshtoken import RefreshToken


class CloudstackAuthorizationProvider(AuthorizationProvider):

    def validate_client_id(self, client_id):
        return client_id is not None

    def validate_client_secret(self, client_id, client_secret):
        command = 'listCapabilities'
        args = {}
        cloudstack_response = requester.make_request(
            command,
            args,
            client_id,
            client_secret
        )

        if cloudstack_response:
            existing_client = Client.query.get(client_id)

            if existing_client is not None:
                existing_client.client_secret = client_secret
            else:
                client = Client(
                    client_id,
                    client_secret
                )
                db.session.add(client)

            db.session.commit()
            return True
        else:
            return False

    def validate_redirect_uri(self, client_id, redirect_uri):
        return True

    def validate_access(self):
        return True

    def validate_scope(self, client_id, scope):
        return True

    def persist_authorization_code(self, client_id, code, scope):
        return

    def persist_token_information(self, client_id, scope, access_token, token_type,
                                  expires_in, refresh_token, id_token, data):
        client = Client.query.get(client_id)
        if client is not None:
            existing_access_token = AccessToken.query.filter_by(
                client_id=client_id).first()
            existing_refresh_token = RefreshToken.query.filter_by(
                client_id=client_id).first()

            if existing_access_token is not None:
                existing_access_token.access_token = access_token
                existing_access_token.expires_in = expires_in
            else:
                db.session.add(
                    AccessToken(
                        access_token, client_id, expires_in, id_token, json.dumps(
                            data)
                    )
                )

            if existing_refresh_token is not None:
                existing_refresh_token.refresh_token = refresh_token
                existing_refresh_token.data = json.dumps(data)
            else:
                db.session.add(
                    RefreshToken(refresh_token, client_id, id_token, json.dumps(data)))

            db.session.commit()
            return True
        else:
            return False

    def from_authorization_code(self, client_id, code, scope):
        return {
            'client_id': client_id,
            'code': code,
            'scope': scope,
        }

    def from_refresh_token(self, client_id, refresh_token, scope):
        found_refresh_token = RefreshToken.query.get(refresh_token)
        if found_refresh_token is not None and found_refresh_token.data != 'false':
            data = json.loads(found_refresh_token.data)
            if (scope == '' or scope == data.get('scope')) and client_id == data.get('client_id'):
                return data
        else:
            return False

    def discard_authorization_code(self, client_id, code):
        return None

    def discard_refresh_token(self, client_id, refresh_token):
        found_refresh_token = RefreshToken.query.get(refresh_token)
        if found_refresh_token is not None:
            db.session.delete(found_refresh_token)
            db.session.commit()


class CloudstackResourceAuthorization(ResourceAuthorization):
    client_secret = None


class CloudstackResourceProvider(ResourceProvider):

    @property
    def authorization_class(self):
        return CloudstackResourceAuthorization

    def get_authorization_header(self):
        return request.headers.get('Authorization')

    def validate_access_token(self, access_token, authorization):
        found_access_token = AccessToken.query.get(access_token)
        if found_access_token is not None and found_access_token.data != 'false':
            access_token_data = json.loads(found_access_token.data)
            client = Client.query.get(access_token_data.get('client_id'))

            authorization.is_valid = True
            authorization.client_id = access_token_data.get('client_id')
            authorization.expires_in = access_token_data.get('expires_in')
            authorization.client_secret = client.client_secret
