import hashlib
import time
from http import HTTPStatus
from datetime import datetime, timedelta
from django.contrib.contenttypes.models import ContentType
from django.core.files.uploadedfile import UploadedFile
from django.db import connection, models, transaction
from django.db.models import Q
from rest_framework.views import APIView
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed,ValidationError,PermissionDenied
from rest_framework.permissions import IsAuthenticated
from django.utils import timezone
from api.models.access_token import AccessToken
from rest_framework.response import Response

from api.models.audit_trail import AuditTrail
from api.models.incoming_api_admin import IncomingApiAdmin
from api.request_auth import validation_payload
from api.utils import get_ip, parse_boolean, throw_validation_error, get_exception_detail, log_critical_error
from django.conf import settings
from api.translation import __
from api.admin_permission_conf import PERMISSIONS,NO_PERMISSION
from api.views.main_base import APIBase


class TokenAuthentication(BaseAuthentication):
    def authenticate(self, request):
        auth_header = request.headers.get('Authorization')
        if not auth_header or not auth_header.startswith("Token "):
            raise AuthenticationFailed(__("validation.invalid",field=__("attr.token")))

        token_key = auth_header.split(" ")[1]
        try:
            token = AccessToken.objects.select_related('user').get(token=token_key)
        except AccessToken.DoesNotExist:
            raise AuthenticationFailed(__("validation.invalid",field=__("attr.token")))

        if token.expires_at and token.expires_at <= timezone.now():
            token.delete()
            raise AuthenticationFailed(__("validation.token_expired"))

        return (token.user, token)
class AdminApiBase(APIBase):
    authentication_classes = [TokenAuthentication]

    def finalize_response(self, request, response, *args, **kwargs):
        if 200 <= response.status_code < 300:
            if not self.exclude_token_extend:
                token = getattr(request, "auth", None)
                if isinstance(token, AccessToken):
                    token.expires_at = timezone.now() + timedelta(hours=1)
                    token.save(update_fields=["expires_at"])

        return super().finalize_response(request, response, *args, **kwargs)

    enable_log = True
    exclude_token_extend = False
    # for listing log, no need store the outgoing data of the list
    api_log_skip_outgoing_data = False
    audit_type=AuditTrail.TYPE['admin']
    incoming_api_table_name = IncomingApiAdmin

    url_name_ignore_basic_validation_and_enable_log = [
        "get_timestamp"
    ]

    def initial(self, request, *args, **kwargs):
        super().initial(request, *args, **kwargs)
        from api.services.diagnostics import (
            current_diagnostic_identifiers,
            record_admin_log_id,
            record_admin_request,
            sanitize_diagnostic_value,
        )

        self.api_log_id = None
        url_name = request.resolver_match.view_name or ""
        if url_name not in self.url_name_ignore_basic_validation_and_enable_log:
            if getattr(self, 'enable_log', True):
                route_name = request.resolver_match.view_name
                user = request.user

                if route_name not in NO_PERMISSION:
                    if not user.has_perm(route_name):
                        raise PermissionDenied(__('validation.no_permission'))

                incoming_data = sanitize_diagnostic_value(request.data.copy())
                request_id, correlation_id = current_diagnostic_identifiers()
                request_path = request.path
                request_ip = get_ip(request)
                record_admin_request(
                    user_id=getattr(user, "id", None),
                    incoming_params=incoming_data,
                    path=request_path,
                    ip=request_ip,
                )

                self.api_log_id = self.incoming_api_table_name.insert_log(
                    user_id=getattr(user, "id", None),
                    request_data=incoming_data,
                    url=request_path,
                    ip=request_ip,
                    request_id=request_id,
                    correlation_id=correlation_id,
                )
                record_admin_log_id(self.api_log_id)

            errors = self.basic_validation(
                validation_payload(
                    request.data,
                    request.headers,
                    request.content_type,
                )
            )
            if errors:
                scode = HTTPStatus.BAD_REQUEST
                self.incoming_api_table_name.update_log(self.api_log_id, errors, scode=scode)
                raise throw_validation_error(code=scode, error=errors)

    def basic_validation(self, data: dict) -> bool:
        if "timestamp" not in data:
            return __("validation.required", field=__("attr.timestamp"))

        if "signature" not in data:
            return __("validation.required", field=__("attr.signature"))

        #validate timestamp
        request_timestamp = int(data["timestamp"])
        now = int(time.time())
        min_time = now - settings.API_TIMEOUT_MAX
        max_time = now - settings.API_TIMEOUT_MIN
        if request_timestamp > max_time or request_timestamp < min_time:
            return __("validation.invalid",field=__("attr.timestamp"))

        #validate signature
        request_sign = data.get('signature')

        self_sign = self.sign(data, settings.API_SECRET_KEY)
        if request_sign != self_sign:
            return __("validation.invalid",field=__("signature"))

    def api_response(self, data=None, code=200, errors=None, error=None):
        from api.services.diagnostics import record_admin_response

        response = {
            'code': code,
        }
        if errors or error:
            response['error'] = error
            response['errors'] = errors
        else:
            data = data.copy()

            remove_fields = ['password']
            for field in remove_fields:
                data.pop(field, None)

            response['data'] = data

        if getattr(self, 'enable_log', True) and self.api_log_id:
            outgoing_data = response.copy()
            # only success response can skip outgoing_data
            if self.api_log_skip_outgoing_data and code==200:
                outgoing_data = None
            self.incoming_api_table_name.update_log(self.api_log_id, outgoing_data, scode=code)

        record_admin_response(outgoing_params=response, status_code=code)

        return Response(response, status=code)