"""
IdealCandidate (BaseModel)

Because unvalidated data is a liability in pipelines -AND- in hiring!
"""

from __future__ import annotations

from datetime import date
from enum import Enum

from pydantic import BaseModel, Field, computed_field, field_validator, model_validator


# ---
# re-usable types
# ---
class Strengths(str, Enum):
    """CliftonStrengths — top strength categories"""

    IDEATION = "Ideation"
    LEARNER = "Learner"
    COMMAND = "Command"
    ACTIVATOR = "Activator"
    INPUT = "Input"
    ANALYTICAL = "Analytical"


class QuotaResult(BaseModel):
    """Details about quota target / year"""

    year: int = Field(..., ge=2008, le=date.today().year)
    attainment_pct: int = Field(
        ..., ge=0, description="Quota attainment as a percentage"
    )
    highlight: str | None = None

    @field_validator("attainment_pct")
    @classmethod
    def eric_doesnt_miss(cls, v: int) -> int:
        """Validate consistent track record meeting quota"""
        if v < 100:
            raise ValueError("Candidate without track record. Must not be Eric Kraus!")
        return v


class Role(BaseModel):
    """A single position at a company"""

    company: str
    title: str
    start: date
    end: date | None = Field(None, description="'None' means still there (for now?).")
    location: str = "Remote"
    sold_to: list[str] = Field(
        default_factory=list, description="Buyer personas engaged"
    )
    highlights: list[str] = Field(default_factory=list)
    quota_results: list[QuotaResult] = Field(default_factory=list)
    largest_deal: str | None = None

    @computed_field
    @property
    def tenure_years(self) -> float:
        """Returns the number of years working"""
        end = self.end or date.today()
        return round((end - self.start).days / 365.25, 1)


class TechnicalProject(BaseModel):
    """Side projects, learnings, hobbies"""

    name: str
    description: str
    technologies: list[str] = Field(default_factory=list)


class Education(BaseModel):
    """Education that laid my ground-work"""

    degree: str
    major: str
    minors: list[str] = Field(default_factory=list)
    school: str


class IdealCandidate(BaseModel):
    """
    The ideal candidate object to instantiate later
    """

    name: str = Field(..., description="Full name")

    email: str = Field(..., pattern=r".+@gmail\.com", description="Email address")

    phone: str = Field(
        ...,
        pattern=r"\d{3}[.]\d{3}[.]\d{4}",
        description="Phone number e.g. 123.456.7890",
    )

    location: str = Field(..., description="Current location")

    location_note: str = Field(
        ..., description="Additional information about location or timezone"
    )

    years_experience: int = Field(..., ge=0, description="Years of experience")

    available_for_work: bool

    strengths: list[Strengths] = Field(
        default_factory=list,
        description="Clifton Strengths Finder (top strength categories)",
    )

    experience: list[Role] = Field(
        default_factory=list,
        description="Career history (sorted desc)",
    )

    technical_projects: list[TechnicalProject] = Field(
        default_factory=list,
        description="Side projects, learnings, hobbies",
    )

    why_pydantic: str = Field(
        ...,
        description="The most important question...",
    )

    languages: list[str] = Field(
        default_factory=list,
        description="Languages spoken",
    )

    education: Education = Field(..., description="Education")

    # Validate the best candidate!
    @model_validator(mode="after")
    def validate_not_just_another_ae(self) -> IdealCandidate:
        """What makes candidate unique/differentiating as an AE"""
        has_technical = len(self.technical_projects) > 0
        has_quota_proof = any(
            qr.attainment_pct > 100
            for role in self.experience
            for qr in role.quota_results
        )
        if not (has_technical and has_quota_proof):
            raise ValueError(
                "Not required to write code. But doing so AND crushing targets is a bonus!"
            )
        return self
