import base64
import logging
import os

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from dotenv import load_dotenv

load_dotenv()

logger = logging.getLogger(__name__)

# AES-CBC の IV サイズ（固定 16 バイト）
_IV_SIZE = 16
# セパレータ（PHPの慣習に合わせた形式）
_SEP = b"::"


def _get_key() -> bytes:
    """AES_KEY を 32 バイトに調整して返す（不足はゼロ埋め、超過は切り捨て）。"""
    raw = os.getenv("AES_KEY", "").encode("utf-8")
    return raw[:32].ljust(32, b"\0")


def encrypt(text: str) -> str:
    """
    文字列を AES-256-CBC で暗号化し base64 文字列を返す。

    形式: base64(encrypted_bytes + '::' + iv_bytes)

    PHP との互換例:
        $iv  = openssl_random_pseudo_bytes(16);
        $enc = openssl_encrypt($text, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
        $out = base64_encode($enc . '::' . $iv);
    """
    try:
        key = _get_key()
        iv = os.urandom(_IV_SIZE)
        cipher = AES.new(key, AES.MODE_CBC, iv)
        encrypted = cipher.encrypt(pad(text.encode("utf-8"), AES.block_size))
        # format: base64(encrypted::iv)
        return base64.b64encode(encrypted + _SEP + iv).decode("utf-8")
    except Exception as e:
        logger.error("暗号化エラー: %s", e)
        raise


def decrypt(encrypted_text: str) -> str:
    """
    base64 文字列を AES-256-CBC で復号化して元の文字列を返す。

    IV は末尾 16 バイト固定、セパレータ '::' の前が暗号文。

    PHP との互換例:
        [$enc, $iv] = explode('::', base64_decode($encrypted_text), 2);
        $out = openssl_decrypt($enc, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
    """
    try:
        key = _get_key()
        decoded = base64.b64decode(encrypted_text)

        # IV は末尾 16 バイト、セパレータ '::' は直前の 2 バイト
        iv = decoded[-_IV_SIZE:]
        encrypted = decoded[: -(_IV_SIZE + len(_SEP))]

        cipher = AES.new(key, AES.MODE_CBC, iv)
        return unpad(cipher.decrypt(encrypted), AES.block_size).decode("utf-8")
    except Exception as e:
        logger.error("復号化エラー: %s", e)
        raise
