import uuid
import jwt
from datetime import datetime, timedelta


class InternalTokenService:
    @staticmethod
    def generate_internal_token(secret, expiration_time_in_seconds, payload_obj):
        '''
        :param secret: This parameter receives a key to encrypt the payload data by using SHA256 algorithm.
        :param expiration_time_in_seconds: This parameter receives expiration time in seconds.
        :param payload_obj: This parameter receives the object whose data has to be encrypted.
        :return: It returns the encoded token.
        '''
        return JwtUtil.generate_internal_token(secret,
                                               expiration_time_in_seconds, payload_obj)

    @staticmethod
    def validate_token(secret, token):
        '''
        :param secret: This parameter receives a key to decode and validate the payload data by using SHA256 algorithm.
        :param token: This parameter receives the token that has to be decoded and validated.
        :return: It returns the decoded data.
        '''
        return JwtUtil.validate_internal_token(secret, token)

    @staticmethod
    def generate_trace_id():
        '''
        :return: This returns a random uuid that can be used as request ID.
        '''
        return str(uuid.uuid4())


class JwtUtil:

    @staticmethod
    def generate_internal_token(secret: str, expiration_time_in_seconds: int, payload_obj) -> str:
        """
        Generate a JWT token with user claims.

        :param secret: The secret key for signing the token.
        :param expiration_time_in_seconds: Token expiration time in seconds.
        :param user: An object representing the authenticated user (e.g., a Django model or serializer).
        :return: Encoded JWT token as a string.
        """
        current_time = datetime.utcnow()
        payload = vars(payload_obj)

        payload["iat"] = current_time
        payload["exp"] = current_time + timedelta(seconds=expiration_time_in_seconds)

        try:
            if payload_obj.request_id is None:
                payload["request_id"] = InternalTokenService.generate_trace_id()
            else:
                payload["request_id"] = payload_obj.request_id

        except Exception as e:
            payload["request_id"] = InternalTokenService.generate_trace_id()

        return jwt.encode(payload, secret, algorithm="HS256")

    @staticmethod
    def validate_internal_token(secret: str, token: str):
        """
        Validate a JWT token and decode its payload.

        :param secret: The secret key used for signing the token.
        :param token: The JWT token to validate.
        :return: Decoded payload as a dictionary.
        """
        try:
            decoded_token = jwt.decode(token, secret, algorithms=["HS256"])
            return decoded_token
        except jwt.ExpiredSignatureError:
            raise ValueError("Token has expired")
        except jwt.InvalidTokenError:
            raise ValueError("Invalid token")
        except Exception as e:
            raise ValueError(f"Error occurred - {e}")