from dishka import FromDishka
from dishka.integrations.fastapi import DishkaRoute
from fastapi import APIRouter, HTTPException, status
from pydantic import BaseModel, EmailStr, Field, SecretStr

from template_project.application.auth_identity.errors import (
    AuthError,
    InvalidCodeError,
    InvalidCredentialsError,
    UserAlreadyExistsError,
    UserNotFoundError,
)
from template_project.application.auth_identity.interactors.sign_in import SignInInteractor
from template_project.application.auth_identity.interactors.sign_up import SignUpInteractor
from template_project.application.common.containers import SecretString

router = APIRouter(route_class=DishkaRoute, tags=["Auth"])


class EmailSignUpRequest(BaseModel):
    email: EmailStr = Field(..., description="User email address", examples=["user@example.com"])
    password: SecretStr = Field(
        ..., min_length=8, description="User password (minimum 8 characters)", examples=["password123"]
    )

    model_config = {"json_schema_extra": {"example": {"email": "user@example.com", "password": "password123"}}}


class EmailSignUpResponse(BaseModel):
    access_token: str = Field(..., description="JWT access token for authentication")

    model_config = {"json_schema_extra": {"example": {"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}}}


@router.post(
    "/auth/sign_up/email",
    summary="Sign up with email",
    description="Register a new user with email and password",
    responses={
        200: {"description": "User successfully registered", "model": EmailSignUpResponse},
        409: {"description": "User with this email already exists"},
    },
)
async def sign_up_email(
    request: EmailSignUpRequest,
    interactor: FromDishka[SignUpInteractor],
) -> EmailSignUpResponse:
    try:
        response_interactor = await interactor.sign_up_email(
            email=request.email, password=SecretString(request.password.get_secret_value())
        )
    except UserAlreadyExistsError as error:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT, detail="User with this email already exists"
        ) from error

    return EmailSignUpResponse(access_token=response_interactor.access_token)


class YandexSignUpRequest(BaseModel):
    code: str = Field(..., min_length=1, description="Yandex OAuth authorization code", examples=["abc123def456"])

    model_config = {"json_schema_extra": {"example": {"code": "abc123def456"}}}


class YandexSignUpResponse(BaseModel):
    access_token: str = Field(..., description="JWT access token for authentication")

    model_config = {"json_schema_extra": {"example": {"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}}}


@router.post(
    "/auth/sign_up/yandex",
    summary="Sign up with Yandex",
    description="Register a new user using Yandex OAuth",
    responses={
        200: {"description": "User successfully registered", "model": YandexSignUpResponse},
        401: {"description": "Authentication failed - invalid code or OAuth error"},
    },
)
async def sign_up_yandex(
    request: YandexSignUpRequest,
    interactor: FromDishka[SignUpInteractor],
) -> YandexSignUpResponse:
    try:
        response_interactor = await interactor.sign_up_yandex(code=request.code)

    except (InvalidCodeError, AuthError) as error:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication failed") from error

    return YandexSignUpResponse(access_token=response_interactor.access_token)


class EmailSignInRequest(BaseModel):
    email: EmailStr = Field(..., description="User email address", examples=["user@example.com"])
    password: SecretStr = Field(..., min_length=1, description="User password", examples=["password123"])

    model_config = {"json_schema_extra": {"example": {"email": "user@example.com", "password": "password123"}}}


class EmailSignInResponse(BaseModel):
    access_token: str = Field(..., description="JWT access token for authentication")

    model_config = {"json_schema_extra": {"example": {"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}}}


@router.post(
    "/auth/sign_in/email",
    summary="Sign in with email",
    description="Authenticate user with email and password",
    responses={
        200: {"description": "User successfully authenticated", "model": EmailSignInResponse},
        401: {"description": "Invalid credentials"},
        404: {"description": "User not found"},
    },
)
async def sign_in_email(
    request: EmailSignInRequest,
    interactor: FromDishka[SignInInteractor],
) -> EmailSignInResponse:
    try:
        response_interactor = await interactor.sign_in_email(
            email=request.email, password=SecretString(request.password.get_secret_value())
        )

    except UserNotFoundError as error:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found") from error

    except InvalidCredentialsError as error:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials") from error

    return EmailSignInResponse(access_token=response_interactor.access_token)


class YandexSignInRequest(BaseModel):
    code: str = Field(..., min_length=1, description="Yandex OAuth authorization code", examples=["abc123def456"])

    model_config = {"json_schema_extra": {"example": {"code": "abc123def456"}}}


class YandexSignInResponse(BaseModel):
    access_token: str = Field(..., description="JWT access token for authentication")

    model_config = {"json_schema_extra": {"example": {"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}}}


@router.post(
    "/auth/sign_in/yandex",
    summary="Sign in with Yandex",
    description="Authenticate user using Yandex OAuth",
    responses={
        200: {"description": "User successfully authenticated", "model": YandexSignInResponse},
        401: {"description": "Invalid authorization code or authentication failed"},
        404: {"description": "User not found"},
    },
)
async def sign_in_yandex(
    request: YandexSignInRequest,
    interactor: FromDishka[SignInInteractor],
) -> YandexSignInResponse:
    try:
        response_interactor = await interactor.sign_in_yandex(code=request.code)

    except InvalidCodeError as error:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid authorization code") from error

    except AuthError as error:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication failed") from error

    except UserNotFoundError as error:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found") from error

    return YandexSignInResponse(access_token=response_interactor.access_token)
