import json
import traceback

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import base64
from django.conf import settings
from rest_framework.exceptions import APIException

from api.models.error_log import ErrorLog
from api.models.setting import Setting
from api.models.tt_academic_term import TtAcademicTerm

def encrypt_aes_128_cbc(text: str) -> str:
    cipher = AES.new(settings.PWD_ENCRYPTION_KEY, AES.MODE_CBC, settings.PWD_ENCRYPTION_IV)
    padded = pad(text.encode(), AES.block_size)
    return base64.b64encode(cipher.encrypt(padded)).decode()

def decrypt_aes_128_cbc(enc_text: str) -> str:
    cipher = AES.new(settings.PWD_ENCRYPTION_KEY, AES.MODE_CBC, settings.PWD_ENCRYPTION_IV)
    decrypted = unpad(cipher.decrypt(base64.b64decode(enc_text)), AES.block_size)
    return decrypted.decode()

def get_ip(request):
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        # Take first IP from comma-separated list
        return x_forwarded_for.split(',')[0].strip()
    return request.META.get('REMOTE_ADDR')

def throw_validation_error(code=None, error=None):
    e = APIException()
    e.status_code = code
    e.detail = {
        "code": code,
        "error": error
    }
    raise e

def log_critical_error(user_id=None, descr="", url="", trace: list = None, data: list = None):
    if isinstance(trace, (list, dict)):
        trace = json.dumps(trace)
    if isinstance(data, (list, dict)):
        data = json.dumps(data)

    ErrorLog.insert_log(user_id, descr, trace, data,url)


def get_exception_detail(e):
    descr = str(e)
    url = e.__traceback__.tb_frame.f_code.co_filename
    trace = "".join(traceback.format_exception(type(e), e, e.__traceback__))

    return {
        "descr": descr,
        "url": url,
        "trace": trace
    }


def get_setting_academic_term_ids():
    setting_value = Setting.get_setting("academic_term")

    if setting_value in [None, ""]:
        return list(
            TtAcademicTerm.objects
            .values_list("id", flat=True)
            .order_by("id")
        )

    parsed_value = setting_value
    if isinstance(setting_value, str):
        try:
            parsed_value = json.loads(setting_value)
        except Exception:
            parsed_value = [setting_value]

    if isinstance(parsed_value, (int, str)):
        parsed_value = [parsed_value]

    if not isinstance(parsed_value, (list, tuple, set)):
        return []

    academic_term_ids = []
    for item in parsed_value:
        try:
            academic_term_ids.append(int(item))
        except (TypeError, ValueError):
            continue

    return list(dict.fromkeys(academic_term_ids))
