import bcrypt
from jose import JWTError, jwt
from datetime import datetime, timedelta, timezone

from app.core.config import (
    ACCESS_TOKEN_EXPIRE_MINUTES,
    ALGORITHM,
    REFRESH_TOKEN_EXPIRE_DAYS,
    SECRET_KEY,
)


def hash_password(password: str) -> str:
    return bcrypt.hashpw(
        password.encode("utf-8"),
        bcrypt.gensalt(),
    ).decode("utf-8")


def verify_password(plain: str, hashed: str) -> bool:
    return bcrypt.checkpw(
        plain.encode("utf-8"),
        hashed.encode("utf-8"),
    )


def _format_expiry(expire: datetime) -> int:
    """
    Return expiry as unix timestamp (seconds).

    Frontend integrations prefer numeric timestamps over ISO strings.
    """
    if expire.tzinfo is None:
        expire = expire.replace(tzinfo=timezone.utc)
    return int(expire.timestamp())


def _encode_token(data: dict, expires_delta: timedelta, token_type: str) -> tuple[str, int]:
    to_encode = data.copy()
    expire = datetime.utcnow() + expires_delta
    to_encode.update({"exp": expire, "type": token_type})
    token = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return token, _format_expiry(expire)


def create_access_token(data: dict) -> tuple[str, int]:
    return _encode_token(
        data,
        timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES),
        "access",
    )


def create_refresh_token(data: dict) -> tuple[str, int]:
    return _encode_token(
        data,
        timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS),
        "refresh",
    )


def decode_token(token: str) -> dict:
    return jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])


def decode_access_token(token: str) -> dict:
    payload = decode_token(token)
    token_type = payload.get("type")
    if token_type not in (None, "access"):
        raise JWTError("Invalid access token type")
    return payload


def decode_refresh_token(token: str) -> dict:
    payload = decode_token(token)
    if payload.get("type") != "refresh":
        raise JWTError("Invalid refresh token type")
    return payload


def create_token_pair(data: dict) -> dict:
    """Build access + refresh tokens with expiry metadata for API responses."""
    access_token, access_expires = create_access_token(data)
    refresh_token, refresh_expires = create_refresh_token(data)
    return {
        "access": {"token": access_token, "expires": access_expires},
        "refresh": {"token": refresh_token, "expires": refresh_expires},
    }
