import json
import base64
import logging
import httpx

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad as pkcs7_pad
from google.protobuf import json_format

try:
    from .Token_pb2 import LoginReq, LoginRes
except ImportError:
    LoginReq = None
    LoginRes = None
    print("Error: Token_pb2.py not found.")

from .utils import fetch_ob_version
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class N1LUXGenerator:

    def __init__(self):
        try:
            self.MAIN_KEY = base64.b64decode("WWcmdGMlREV1aDYlWmNeOA==")
            self.MAIN_IV = base64.b64decode("Nm95WkRyMjJFM3ljaGpNJQ==")
        except Exception as e:
            logger.error(f"Key Decode Failed: {e}")
            raise

        self.USER_AGENT = (
            "Dalvik/2.1.0 (Linux; U; Android 13; CPH2095 Build/RKQ1.211119.001)"
        )

        self.release_version = fetch_ob_version()

    def _aes_encrypt(self, plaintext: bytes) -> bytes:
        padded = pkcs7_pad(plaintext, AES.block_size)
        cipher = AES.new(self.MAIN_KEY, AES.MODE_CBC, self.MAIN_IV)
        return cipher.encrypt(padded)

    def _json_to_proto(self, json_body: dict) -> bytes:
        message = LoginReq()
        json_format.ParseDict(json_body, message)
        return message.SerializeToString()

    async def _get_access_token(self, uid: str, password: str):

        url = "https://ffmconnect.live.gop.garenanow.com/oauth/guest/token/grant"

        payload = {
            "uid": uid,
            "password": password,
            "response_type": "token",
            "client_type": "2",
            "client_secret": "2ee44819e9b4598845141067b281621874d0d5d7af9d8f7e00c1e54715b7d1e3",
            "client_id": "100067"
        }

        headers = {
            "User-Agent": self.USER_AGENT,
            "Content-Type": "application/x-www-form-urlencoded"
        }

        async with httpx.AsyncClient(timeout=15) as client:
            response = await client.post(url, data=payload, headers=headers)
            response.raise_for_status()

        data = response.json()

        access_token = data.get("access_token")
        open_id = data.get("open_id")

        if not access_token or not open_id:
            raise ValueError(f"Invalid response from server: {data}")

        return access_token, open_id
        
    async def create_token(self, uid: str, password: str):

        if LoginReq is None or LoginRes is None:
            return {"error": "Token_pb2.py missing or not imported properly."}

        try:
            logger.info(f"Generating token for UID: {uid} | OB: {self.release_version}")
            
            access_token, open_id = await self._get_access_token(uid, password)
            
            login_body = {
                "open_id": open_id,
                "open_id_type": "4",
                "login_token": access_token,
                "orign_platform_type": "4"
            }
            
            proto_bytes = self._json_to_proto(login_body)
            encrypted_payload = self._aes_encrypt(proto_bytes)
            login_url = "https://loginbp.ggblueshark.com/MajorLogin"

            headers = {
                "User-Agent": self.USER_AGENT,
                "Content-Type": "application/octet-stream",
                "X-Unity-Version": "2022.3.47f1",
                "X-GA": "v1 1",
                "ReleaseVersion": self.release_version
            }

            async with httpx.AsyncClient(timeout=15) as client:
                response = await client.post(
                    login_url,
                    content=encrypted_payload,
                    headers=headers
                )
                response.raise_for_status()
            response_proto = LoginRes.FromString(response.content)

            response_dict = json_format.MessageToDict(
                response_proto,
                preserving_proto_field_name=True,
                use_integers_for_enums=False
            )

            return response_dict

        except Exception as e:
            logger.error(f"Token generation failed: {e}")
            return {"error": str(e)}