import traceback
from django.utils.timezone import now
from django.http import JsonResponse
from rest_framework import status
from apps.pos_device.models import Sessions, DeviceVersion
from apps.authentication.models import Organization


class POSDeviceException(Exception):
    """Custom Exception for POS Validation"""
    def __init__(self, message, code=400):
        self.message = message
        self.code = code
        super().__init__(message)


def get_client_ip(request):
    forwarded = request.META.get('HTTP_X_FORWARDED_FOR')
    return forwarded.split(',')[0] if forwarded else request.META.get('REMOTE_ADDR')


class PosDeviceValidationMiddleware:
    REQUIRED_HEADERS = [
        'device-mac', 'device-serial', 'device-name',
        'device-sdk', 'device-provider', 'device-version',
        'device-vname', 'device-lng', 'device-lot'  # noqa
    ]

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        try:
            if request.path.startswith("/pos_device/pos/"):
                self.validate_request(request)

            return self.get_response(request)
        except POSDeviceException as e:
            return JsonResponse({'message': e.message}, status=e.code)

        # for response 500 errors in json format
        except Exception as e:
            if request.path.startswith('/pos_device/pos/'):
                return JsonResponse(
                    {"message": str(e), "traceback": traceback.format_exc()},
                    status=500
                )
            raise

    def validate_request(self, request):
        headers = request.headers
        data = {key: headers.get(key) for key in self.REQUIRED_HEADERS}

        missing = [key for key, value in data.items() if not value]
        if missing:
            raise POSDeviceException(f'پارامترهای ارسالی ناقص هستند: {", ".join(missing)}')  # noqa

        provider_name = data['device-provider']
        organization = Organization.objects.filter(en_name=provider_name).first()  # noqa
        if not organization:
            raise POSDeviceException('شرکت پرداخت الکترونیک پشتیبانی نمی‌شود!', code=402)  # noqa
        if not organization.active:
            raise POSDeviceException('شرکت پرداخت الکترونیک مسدود شده است!', code=402)  # noqa

        versions = DeviceVersion.objects.filter(organization=organization)
        if not versions.exists():
            raise POSDeviceException('هیچ نسخه‌ای برای این شرکت ثبت نشده است!', code=402)  # noqa

        current_version = versions.filter(code=data['device-version']).first()
        if not current_version or current_version.remove:
            raise POSDeviceException(
                f'نسخه {data["device-vname"]} منقضی شده است. لطفا بروزرسانی کنید.', code=402  # noqa
            )

        session = Sessions.objects.filter(
            device__serial=data['device-serial'],
            sdk=data['device-sdk']
        ).first()

        if session:
            session.session_last_seen_date = now()
            session.lng = data['device-lng']
            session.lot = data['device-lot']
            session.version = data['device-version']
            session.ip = get_client_ip(request)
            session.save()
