from urllib.parse import urlparse

from flask import request
import jwt

from utilities.schema_loader import SchemaLoad
from db.query import QueryBuilder, QueryExecution
from helper_functions import set_data
from logger_module.package_logger import print_log
from key_resolver import KeyResolver
from db.database_operations import DatabaseOperations


class AuthenticationSchema:
    auth_schema_path = 'api_wrapper/authentication/authentication_schema.json'

    def auth(self, request):
        try:
            print_log(f'Hostname: {request.headers.get("Host")}')
            host_name = request.headers.get("Host").split(".")[0]
            if host_name == 'mconnectuat-1':
                host_name = 'mconnectuat'
            # host_name = 'mconnectuat'
            status = False
            result = []
            auth_type = 'basic'

            # Load schemas
            schema_loader = SchemaLoad()
            auth_schema = schema_loader.read_schema(self.auth_schema_path)
            host_list = auth_schema.get('domain', {}).keys()

            # Validate host
            if host_name not in host_list:
                return False, self.authentication_response(auth_schema, auth_type, host_name)

            from routes import Routes
            from app import app_name
            routes = Routes().get_routes_schema(app_name)
            route_config = routes.get(request.path, {})

            # Check if authentication is required
            if not route_config.get('auth'):
                return True, self.get_details(result, host_name)

            supported_auth = route_config.get('auth')
            check_ip_whitelisting = route_config.get('ip_check_enabled', False)
            auth_details = auth_schema.get('domain', {}).get(host_name, {})
            auth_list = auth_details.get('authentication', [])
            ip_whitelisting = auth_details.get('IPwhitelisting')

            # print_log('Host list verified')

            for auth in auth_list:
                auth_type = auth.get('type')

                # Basic Authentication
                if auth_type in supported_auth and auth_type == 'basic' and auth.get('mode') == 'database':
                    stored_key = auth.get('storedKey')
                    if stored_key:
                        query_type, table_name, columns, clause = (
                            stored_key.get('queryType'),
                            stored_key.get('table'),
                            stored_key.get('columns'),
                            stored_key.get('clause'),
                        )
                        statement, params = QueryBuilder().create_query(query_type, table_name, columns, clause=clause)
                        status, result = QueryExecution.fetch_result(statement, params)
                        if status and result:
                            if check_ip_whitelisting and not auth.get('IPwhitelisting', True):
                                check_ip_whitelisting = False
                            # print_log(result)
                            break

                # JWT Authentication
                elif auth_type in supported_auth and auth_type == 'jwt':
                    try:
                        token = request.headers.get(auth.get('key', ''), '').split(' ')[-1]
                        decode_key = KeyResolver().resolve_key(auth.get('decodeKey'), None)
                        algorithm = auth.get('algorithm')
                        data = jwt.decode(token, decode_key, algorithms=algorithm)

                        if data:
                            set_data('token', data)
                            verify_key = auth.get('verifyKey', {})
                            query_type, table_name, columns, clause = (
                                verify_key.get('queryType'),
                                verify_key.get('table'),
                                verify_key.get('columns'),
                                verify_key.get('clause'),
                            )
                            statement, params = QueryBuilder().create_query(query_type, table_name, columns,
                                                                            clause=clause)
                            status, result = QueryExecution.fetch_result(statement, params)
                            if status and result:
                                if check_ip_whitelisting and not auth.get('IPwhitelisting', True):
                                    check_ip_whitelisting = False
                                # print_log(result)
                                break
                    except jwt.ExpiredSignatureError:
                        return False, self.authentication_response(auth_schema, auth_type, host_name)
                    except jwt.InvalidTokenError:
                        return False, self.authentication_response(auth_schema, auth_type, host_name)

            if status and result:
                if ip_whitelisting and check_ip_whitelisting:
                    client_ip = (
                        request.headers.getlist("X-Forwarded-For")[0]
                        if request.headers.getlist("X-Forwarded-For")
                        else request.remote_addr
                    )
                    print_log(f'IP validation started: {client_ip}')
                    if self.check_ip_whitelist(auth_details.get('IPwhitelisting'), client_ip,
                                               result.get('merchant_id')):
                        print_log("Authorization success")
                        return True, self.get_details(result, host_name)
                else:
                    return True, self.get_details(result, host_name)

            return False, self.authentication_response(auth_schema, auth_type, host_name)
        except Exception as ex:
            print_log(str(ex))

    def get_details(self, data, domain):
        url = request.url
        parsed_url = urlparse(url)
        endpoint = parsed_url.path
        method = request.method
        self.request_prep(method)
        if data:
            merchant_id = data.get('merchant_id')
            merchant_query = "select * from merchant where id = %s"
            merchant_params = (merchant_id,)
            status, merchant_result = QueryExecution.fetch_result(merchant_query, merchant_params)
            set_data('merchant_details', merchant_result)
            # print_log(merchant_result)
            return {'merchant_id': merchant_id, 'merchant_name': merchant_result.get('merchant_name'),
                    'category_id': merchant_result.get('category_id'),
                    'domain': domain, 'endpoint': endpoint, 'method': method.lower()}
        return {'domain': domain, 'endpoint': endpoint, 'method': method.lower(), 'source': request.headers.get('source')}

    @staticmethod
    def authentication_response(json_schema, type, host_name):
        response = json_schema.get('domain').get(host_name).get('response')
        invalid_auth_response = response.get(type).get('content')
        return invalid_auth_response

    @staticmethod
    def request_prep(method):
        if method == 'GET':
            args = request.args
            keys = args.keys()
            request_body = {}
            for i in keys:
                req_b = {i: args.get(i)}
                request_body.update(req_b)
            request.get_json = request_body
        else:
            request.get_json = request.get_json()
        set_data('request', request.get_json)

    @staticmethod
    def check_ip_whitelist(details_dict, request_ip, merchant_id):
        # if request_ip == '127.0.0.1':
        #     return True
        status, result = DatabaseOperations().execute(details_dict, None)
        if result:
            ips = result.get('ips')
            if ips:
                ip_list = ips.split(',')
                # print_log(ip_list)
                if request_ip in ip_list:
                    return True
        return False
