import hashlib
from typing import Annotated

import psycopg
from fastapi import Depends, HTTPException, status
from passlib.hash import argon2
from src.__types.errors import ResourceNotFoundError
from src.__types.index import Client, ClientTenant, Tenant
from src.domain.entities.client_api_key import ClientApiKey
from src.infrastructure.repositories.client_api_key_repository import ClientApiKeyRepository
from src.infrastructure.repositories.client_repository import ClientRepository
from src.infrastructure.repositories.client_tenant_repository import ClientTenantRepository
from src.infrastructure.repositories.tenant_repository import TenantRepository
from src.logger import logger
from src.routers.client.connection import get_connection
from src.routers.client.headers import get_api_key, get_client_tenant_name
from src.settings import Settings
from src.tracer import tracer

settings = Settings()


@tracer.capture_method
def get_client_api_key(
    api_key: Annotated[str, Depends(get_api_key)],
    connection: Annotated[psycopg.Connection, Depends(get_connection)],
) -> ClientApiKey:
    try:
        key_id = api_key.split(".")[3]
        raw_secret = api_key.split(".")[4]
    except IndexError as e:
        logger.debug("Invalid API Key format.", exc_info=e)
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="API Key is invalid.",
        ) from e
    if not raw_secret or not key_id:
        logger.debug("API Key is missing key_id or raw_secret.")
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="API Key is invalid.",
        )

    try:
        client_api_key_repository = ClientApiKeyRepository(connection=connection)
        stored_api_key = client_api_key_repository.get(key_id)
    except ResourceNotFoundError as e:
        logger.debug("Client API Key not found.", exc_info=e)
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="API Key is invalid.",
        ) from e

    if stored_api_key.algorithm == "ARGON2" and argon2.verify(raw_secret, stored_api_key.hashed_secret):
        return stored_api_key

    if stored_api_key.algorithm == "SHA256":
        hashed_secret = hashlib.sha256(raw_secret.encode()).hexdigest()
        if hashed_secret == stored_api_key.hashed_secret:
            return stored_api_key

    logger.debug("API Key verification failed.")
    raise HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="API Key is invalid.",
    )


@tracer.capture_method
def get_client(
    client_api_key: Annotated[ClientApiKey, Depends(get_client_api_key)],
    connection: Annotated[psycopg.Connection, Depends(get_connection)],
) -> Client:
    try:
        client_repository = ClientRepository(connection=connection)
        client = client_repository.get(client_api_key.client_id)
    except ResourceNotFoundError as e:
        logger.debug("Client not found.", exc_info=e)
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="API Key is invalid.",
        ) from e
    tracer.put_annotation(key="ClientId", value=str(client.id))
    return client


@tracer.capture_method
def get_client_tenant(
    client_tenant_name: Annotated[str, Depends(get_client_tenant_name)],
    client: Annotated[Client, Depends(get_client)],
    connection: Annotated[psycopg.Connection, Depends(get_connection)],
) -> ClientTenant:
    repository = ClientTenantRepository(connection=connection)
    try:
        client_tenant = repository.get(client_id=client.id, client_tenant_name=client_tenant_name)
    except ResourceNotFoundError as e:
        logger.debug(f"Not found client tenant: {client_tenant_name}", exc_info=e)
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Client tenant not found.",
        ) from e
    tracer.put_annotation(key="ClientTenantId", value=str(client_tenant.id))
    return client_tenant


@tracer.capture_method
def get_tenant(
    client_tenant: Annotated[ClientTenant, Depends(get_client_tenant)],
    connection: Annotated[psycopg.Connection, Depends(get_connection)],
) -> Tenant:
    repository = TenantRepository(connection=connection)
    try:
        tenant = repository.get(client_tenant.tenant_id)
    except ResourceNotFoundError as e:
        logger.debug(f"Not found tenant id: {client_tenant.tenant_id}", exc_info=e)
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Tenant not found.",
        ) from e
    tracer.put_annotation(key="TenantId", value=str(tenant.id))
    return tenant
