from datetime import date
from typing import Optional, Literal, Tuple

from fastapi import HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from starlette import status

from app.application.lens_issues_repository import LensIssuesRepository
from app.application.lenses_repository import LensesRepository
from app.application.patients_repository import PatientsRepository
from app.application.users_repository import UsersRepository
from app.domain.entities.lens_issues import LensIssueEntity
from app.domain.models import LensIssue
from app.infrastructure.lenses_service import LensesService
from app.infrastructure.patients_service import PatientsService
from app.infrastructure.users_service import UsersService


class LensIssuesService:
    def __init__(self, db: AsyncSession):
        self.lens_issues_repository = LensIssuesRepository(db)
        self.patient_repository = PatientsRepository(db)
        self.users_repository = UsersRepository(db)
        self.lenses_repository = LensesRepository(db)

    async def get_all_lens_issues(
        self,
        skip: int = 0,
        limit: int = 10,
        search: Optional[str] = None,
        sort_order: Literal["asc", "desc"] = "desc",
        start_date: Optional[date] = None,
        end_date: Optional[date] = None
    ) -> Tuple[list[LensIssueEntity], int]:
        lens_issues, total_count = await self.lens_issues_repository.get_all(
            skip=skip,
            limit=limit,
            search=search,
            sort_order=sort_order,
            start_date=start_date,
            end_date=end_date
        )
        return (
            [self.model_to_entity(lens_issue) for lens_issue in lens_issues],
            total_count
        )

    async def create_lens_issue(self, lens_issue: LensIssueEntity, user_id: int) -> Optional[LensIssueEntity]:
        patient = await self.patient_repository.get_by_id(lens_issue.patient_id)

        if not patient:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail='Пациент с таким ID не найден',
            )

        user = await self.users_repository.get_by_id(user_id)

        if not user:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail='Пользователь с таким ID не найден',
            )

        lens_issue.doctor_id = user_id

        lens = await self.lenses_repository.get_by_id(lens_issue.lens_id)

        if not lens:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail='Линза с таким ID не найдена',
            )

        if lens.issued:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail='Линза уже выдана',
            )

        lens_issue_model = self.entity_to_model(lens_issue)

        await self.lens_issues_repository.create(lens_issue_model)

        lens.issued = True

        await self.lenses_repository.update(lens)

        return self.model_to_entity(lens_issue_model)

    @staticmethod
    def entity_to_model(lens_issue: LensIssueEntity) -> LensIssue:
        lens_issue_model = LensIssue(
            issue_date=lens_issue.issue_date,
            patient_id=lens_issue.patient_id,
            doctor_id=lens_issue.doctor_id,
            lens_id=lens_issue.lens_id,
        )

        if lens_issue.id is not None:
            lens_issue_model.id = lens_issue.id

        return lens_issue_model

    @staticmethod
    def model_to_entity(lens_issue_model: LensIssue) -> LensIssueEntity:
        lens_issue_entity = LensIssueEntity(
            id=lens_issue_model.id,
            issue_date=lens_issue_model.issue_date,
            patient_id=lens_issue_model.patient_id,
            doctor_id=lens_issue_model.doctor_id,
            lens_id=lens_issue_model.lens_id,
        )

        if lens_issue_model.doctor is not None:
            lens_issue_entity.doctor = UsersService.model_to_entity(lens_issue_model.doctor)

        if lens_issue_model.patient is not None:
            lens_issue_entity.patient = PatientsService.model_to_entity(lens_issue_model.patient)

        if lens_issue_model.lens is not None:
            lens_issue_entity.lens = LensesService.model_to_entity(lens_issue_model.lens)

        return lens_issue_entity
