import secrets
import string
import uuid
from datetime import datetime, timedelta, timezone
from typing import Optional

import bcrypt as _bcrypt
import jwt

from src.core.config import settings


def generate_temp_password(length: int = 12) -> str:
    """Generate a cryptographically secure temporary password.

    Guarantees at least one uppercase, one lowercase, one digit, and one symbol.
    Uses secrets module — never random.
    """
    symbols = "!@#$%^&*"
    alphabet = string.ascii_uppercase + string.ascii_lowercase + string.digits + symbols
    while True:
        password = "".join(secrets.choice(alphabet) for _ in range(length))
        if (
            any(c.isupper() for c in password)
            and any(c.islower() for c in password)
            and any(c.isdigit() for c in password)
            and any(c in symbols for c in password)
        ):
            return password


def hash_password(password: str) -> str:
    """Hash a password with bcrypt. Returns a str starting with $2b$."""
    salt = _bcrypt.gensalt()
    hashed = _bcrypt.hashpw(password.encode("utf-8"), salt)
    return hashed.decode("utf-8")


def verify_password(plain_password: str, hashed_password: str) -> bool:
    """Verify a plain-text password against a bcrypt hash."""
    return _bcrypt.checkpw(
        plain_password.encode("utf-8"),
        hashed_password.encode("utf-8"),
    )


def create_access_token(payload: dict) -> str:
    data = payload.copy()
    expire = datetime.now(timezone.utc) + timedelta(
        minutes=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES
    )
    data.update({"exp": expire, "iat": datetime.now(timezone.utc), "jti": str(uuid.uuid4())})
    return jwt.encode(data, settings.JWT_SECRET, algorithm=settings.JWT_ALGORITHM)


def create_refresh_token(payload: dict) -> str:
    data = payload.copy()
    expire = datetime.now(timezone.utc) + timedelta(days=settings.JWT_REFRESH_TOKEN_EXPIRE_DAYS)
    data.update({"exp": expire, "iat": datetime.now(timezone.utc), "jti": str(uuid.uuid4())})
    return jwt.encode(data, settings.JWT_REFRESH_SECRET, algorithm=settings.JWT_ALGORITHM)


JWT_AUDIENCE = "indelis-client"


def decode_access_token(token: str) -> Optional[dict]:
    try:
        return jwt.decode(
            token,
            settings.JWT_SECRET,
            algorithms=[settings.JWT_ALGORITHM],
            audience=JWT_AUDIENCE,
        )
    except jwt.PyJWTError:
        return None


def decode_refresh_token(token: str) -> Optional[dict]:
    try:
        return jwt.decode(
            token,
            settings.JWT_REFRESH_SECRET,
            algorithms=[settings.JWT_ALGORITHM],
            audience=JWT_AUDIENCE,
        )
    except jwt.PyJWTError:
        return None


def build_token_payload(user, tenant=None) -> dict:
    payload = {
        "sub": str(user.id),
        "email": user.email,
        "role": user.role,
        "iss": "indelis-api",
        "aud": "indelis-client",
    }
    if user.tenant_id:
        payload["tenant_id"] = str(user.tenant_id)
    if tenant:
        payload["tenant_subdomain"] = tenant.subdomain
    return payload
