from decimal import Decimal

from template_project.application.common.data_structure import to_data_structure
from template_project.application.common.enums import EducationGrade, ExperienceType
from template_project.application.common.identity_provider import IdentityProvider
from template_project.application.common.interactor import to_interactor
from template_project.application.resume.data_gateway import (
    ResumeDataGateway,
    ResumeEducationDataGateway,
    ResumeExperienceDataGateway,
    ResumePredictionDataGateway,
    ResumeProjectDataGateway,
)
from template_project.application.resume.entity import ResumeId
from template_project.application.resume.errors import ResumeDoesBelongUserError, ResumeNotFoundError


@to_data_structure
class ResumePredictionResponse:
    from_salary: Decimal
    to_salary: Decimal
    recommended_skills: list[str]


@to_data_structure
class ExperienceItemResponse:
    place: str
    description: str
    months_duration: int


@to_data_structure
class EducationItemResponse:
    place: str
    grade: EducationGrade
    specialization: str
    description: str | None


@to_data_structure
class ProjectItemResponse:
    name: str
    description: str


@to_data_structure
class GetResumeResponse:
    id: ResumeId
    position: str
    location: str
    about_me: str
    key_skills: list[str]
    experience_type: ExperienceType
    experience: list[ExperienceItemResponse]
    education: list[EducationItemResponse]
    projects: list[ProjectItemResponse]
    prediction: ResumePredictionResponse | None


@to_interactor
class GetResumeInteractor:
    identity_provider: IdentityProvider
    resume_data_gateway: ResumeDataGateway
    resume_prediction_data_gateway: ResumePredictionDataGateway
    resume_experience_data_gateway: ResumeExperienceDataGateway
    resume_education_data_gateway: ResumeEducationDataGateway
    resume_project_data_gateway: ResumeProjectDataGateway

    async def execute(
        self,
        resume_id: ResumeId,
    ) -> GetResumeResponse:
        user = await self.identity_provider.get_current_user()

        resume = await self.resume_data_gateway.load_by_resume_id(resume_id)
        if resume is None:
            raise ResumeNotFoundError(resume_id=resume_id)

        if resume.user_id != user.id:
            raise ResumeDoesBelongUserError

        resume_prediction = await self.resume_prediction_data_gateway.load_by_resume_id(resume.id)
        if resume_prediction is not None:
            prediction = ResumePredictionResponse(
                from_salary=resume_prediction.from_salary,
                to_salary=resume_prediction.to_salary,
                recommended_skills=resume_prediction.recommended_skills,
            )
        else:
            prediction = None

        experiences = await self.resume_experience_data_gateway.load_by_resume_id(resume.id)
        educations = await self.resume_education_data_gateway.load_by_resume_id(resume.id)
        projects = await self.resume_project_data_gateway.load_by_resume_id(resume.id)

        return GetResumeResponse(
            id=resume.id,
            position=resume.position,
            location=resume.location,
            about_me=resume.about_me,
            key_skills=resume.key_skills,
            experience_type=resume.experience_type,
            experience=[
                ExperienceItemResponse(
                    place=exp.place,
                    description=exp.description,
                    months_duration=exp.months_duration,
                )
                for exp in experiences
            ],
            education=[
                EducationItemResponse(
                    place=edu.place,
                    grade=edu.grade,
                    specialization=edu.specialization,
                    description=edu.description,
                )
                for edu in educations
            ],
            projects=[
                ProjectItemResponse(
                    name=proj.name,
                    description=proj.description,
                )
                for proj in projects
            ],
            prediction=prediction,
        )


@to_data_structure
class ResumeListItemResponse:
    id: ResumeId
    position: str
    location: str
    about_me: str
    key_skills: list[str]
    experience_type: ExperienceType
    experience: list[ExperienceItemResponse]
    education: list[EducationItemResponse]
    projects: list[ProjectItemResponse]
    prediction: ResumePredictionResponse | None


@to_interactor
class GetResumeListInteractor:
    identity_provider: IdentityProvider
    resume_data_gateway: ResumeDataGateway
    resume_prediction_data_gateway: ResumePredictionDataGateway
    resume_experience_data_gateway: ResumeExperienceDataGateway
    resume_education_data_gateway: ResumeEducationDataGateway
    resume_project_data_gateway: ResumeProjectDataGateway

    async def execute(self, limit: int, offset: int) -> list[ResumeListItemResponse]:
        user = await self.identity_provider.get_current_user()

        resumes = await self.resume_data_gateway.list_latest_by_user_id(user.id, limit=limit, offset=offset)

        result = []
        for r in resumes:
            resume_prediction = await self.resume_prediction_data_gateway.load_by_resume_id(r.id)
            if resume_prediction is not None:
                prediction = ResumePredictionResponse(
                    from_salary=resume_prediction.from_salary,
                    to_salary=resume_prediction.to_salary,
                    recommended_skills=resume_prediction.recommended_skills,
                )
            else:
                prediction = None

            experiences = await self.resume_experience_data_gateway.load_by_resume_id(r.id)
            educations = await self.resume_education_data_gateway.load_by_resume_id(r.id)
            projects = await self.resume_project_data_gateway.load_by_resume_id(r.id)

            result.append(
                ResumeListItemResponse(
                    id=r.id,
                    position=r.position,
                    location=r.location,
                    about_me=r.about_me,
                    key_skills=r.key_skills,
                    experience_type=r.experience_type,
                    experience=[
                        ExperienceItemResponse(
                            place=exp.place,
                            description=exp.description,
                            months_duration=exp.months_duration,
                        )
                        for exp in experiences
                    ],
                    education=[
                        EducationItemResponse(
                            place=edu.place,
                            grade=edu.grade,
                            specialization=edu.specialization,
                            description=edu.description,
                        )
                        for edu in educations
                    ],
                    projects=[
                        ProjectItemResponse(
                            name=proj.name,
                            description=proj.description,
                        )
                        for proj in projects
                    ],
                    prediction=prediction,
                )
            )

        return result


@to_interactor
class GetResumeHistoryInteractor:
    identity_provider: IdentityProvider
    resume_data_gateway: ResumeDataGateway
    resume_prediction_data_gateway: ResumePredictionDataGateway
    resume_experience_data_gateway: ResumeExperienceDataGateway
    resume_education_data_gateway: ResumeEducationDataGateway
    resume_project_data_gateway: ResumeProjectDataGateway

    async def execute(self, resume_id: ResumeId) -> list[ResumeListItemResponse]:
        user = await self.identity_provider.get_current_user()

        resume = await self.resume_data_gateway.load_by_resume_id(resume_id)
        if resume is None:
            raise ResumeNotFoundError(resume_id=resume_id)
        if resume.user_id != user.id:
            raise ResumeDoesBelongUserError

        history = await self.resume_data_gateway.get_history(resume_id)

        result = []
        for r in history:
            resume_prediction = await self.resume_prediction_data_gateway.load_by_resume_id(r.id)
            if resume_prediction is not None:
                prediction = ResumePredictionResponse(
                    from_salary=resume_prediction.from_salary,
                    to_salary=resume_prediction.to_salary,
                    recommended_skills=resume_prediction.recommended_skills,
                )
            else:
                prediction = None

            experiences = await self.resume_experience_data_gateway.load_by_resume_id(r.id)
            educations = await self.resume_education_data_gateway.load_by_resume_id(r.id)
            projects = await self.resume_project_data_gateway.load_by_resume_id(r.id)

            result.append(
                ResumeListItemResponse(
                    id=r.id,
                    position=r.position,
                    location=r.location,
                    about_me=r.about_me,
                    key_skills=r.key_skills,
                    experience_type=r.experience_type,
                    experience=[
                        ExperienceItemResponse(
                            place=exp.place,
                            description=exp.description,
                            months_duration=exp.months_duration,
                        )
                        for exp in experiences
                    ],
                    education=[
                        EducationItemResponse(
                            place=edu.place,
                            grade=edu.grade,
                            specialization=edu.specialization,
                            description=edu.description,
                        )
                        for edu in educations
                    ],
                    projects=[
                        ProjectItemResponse(
                            name=proj.name,
                            description=proj.description,
                        )
                        for proj in projects
                    ],
                    prediction=prediction,
                )
            )

        return result
