import psycopg
from fastapi.security import HTTPAuthorizationCredentials
from jose import JWTError, jwt
from src.__types.errors import ClientError
from src.__types.index import Tenant
from src.infrastructure.repositories.tenant_repository import TenantRepository
from src.routers.auth_helpers import (
    get_key,
    get_kid,
)
from src.settings import Settings
from src.tracer import tracer

settings = Settings()


@tracer.capture_method
def get_user_tenant(
    credential: HTTPAuthorizationCredentials,
    header_tenant_name: str | None = None,
) -> Tenant:
    """ユーザーのIDトークンを検証し、テナント情報を返す

    raise:
        ClientError: テナント名ヘッダーがない場合
        ValueError: 設定不備
        JWTError: トークンの検証に失敗した場合
    """
    if header_tenant_name is None:
        raise ClientError("Tenant name header is required for user authentication.")

    issuer = settings.management_user_issuer
    jwks_url = settings.management_user_jwks_url
    if not issuer or not jwks_url:
        raise ValueError("Management user issuer/JWKS URL is not configured.")

    # IDトークンの検証
    token = credential.credentials
    decoded_token = jwt.decode(
        token=token,
        key=get_key(get_kid(token), jwks_url),
        algorithms=["RS256"],
        options={
            "verify_signature": True,
            "verify_aud": False,  # TODO: aud検証を有効化する
            "verify_iat": True,
            "verify_exp": True,
            "verify_nbf": True,
            "verify_iss": True,
            "verify_sub": True,
            "verify_jti": True,
            "verify_at_hash": True,
            "require_aud": False,
            "require_iat": False,
            "require_exp": False,
            "require_nbf": False,
            "require_iss": False,
            "require_sub": False,
            "require_jti": False,
            "require_at_hash": False,
            "leeway": 0,
        },
        issuer=issuer,
    )
    if decoded_token.get("token_use") != "id":
        raise JWTError("Invalid token_use: expected 'id'.")

    tenant_id = decoded_token.get("custom:tenant_id")
    if tenant_id is None:
        raise JWTError("Missing required claim: 'custom:tenant_id'.")
    tracer.put_annotation("TenantId", str(tenant_id))

    user_id = decoded_token.get("custom:user_id")
    if user_id is None:
        raise JWTError("Missing required claim: 'custom:user_id'.")
    tracer.put_annotation("UserId", str(user_id))

    # Tenant 解決
    with psycopg.connect(settings.postgres_url) as connection:
        repository = TenantRepository(connection=connection)
        tenant = repository.get(tenant_id)

    # IDトークン経路のテナントがnameを持たない場合、設定不備とみなしエラー
    if tenant.name is None:
        raise ValueError("Tenant does not have a name configured.")

    if header_tenant_name != tenant.name:
        raise JWTError("Tenant name header does not match.")

    return tenant
