from pydantic import BaseModel, Field, field_validator, model_validator
from typing import Optional, List
from datetime import date
import re

class RecruitInput(BaseModel):
    @field_validator('*', mode='before')
    @classmethod
    def normalize_fields(cls, value, info):
        if info.field_name.endswith('_date'):
            if value == '':
                return None
            if value is not None:
                if not isinstance(value, str) or not re.fullmatch(r'\d{4}-\d{2}-\d{2}', value):
                    raise ValueError('Use YYYY-MM-DD for dates')
                date.fromisoformat(value)
        if info.field_name == 'email' and value:
            if not re.fullmatch(r'[^\s@]+@[^\s@.]+(?:\.[^\s@.]+)+', value):
                raise ValueError('Invalid email format')
        return value

    @model_validator(mode='after')
    def reject_required_nulls(self):
        required = {'first_name', 'last_name', 'reimbursement_amount'}
        for name in self.model_fields_set:
            if (name in required or name.endswith('_status') or
                    name.endswith('_attended') or name.endswith('_submitted') or
                    name in {'llqp_registered', 'prov_exam_booked'}):
                if getattr(self, name) is None:
                    raise ValueError(f'{name} cannot be null')
        return self

class LoginRequest(BaseModel):
    username: str = Field(..., min_length=1, max_length=50)
    password: str = Field(..., min_length=6, max_length=128)

class RVPUserCreate(BaseModel):
    username: str = Field(..., min_length=1, max_length=50, pattern=r'^[A-Za-z0-9_.-]+$')
    password: str = Field(..., min_length=12, max_length=128)
    rvp: str = Field(..., pattern='^(Mike Vanderloos|Luciano DiSerafino|Matt Lumley)$')
    email: Optional[str] = Field(None, max_length=100)
    phone: Optional[str] = Field(None, max_length=20)

    @field_validator('email')
    @classmethod
    def validate_email(cls, value):
        if value and not re.fullmatch(r'[^\s@]+@[^\s@.]+(?:\.[^\s@.]+)+', value):
            raise ValueError('Invalid email format')
        return value

class UserResponse(BaseModel):
    id: int
    username: str
    role: str
    rvp: Optional[str]
    email: Optional[str]
    csrf_token: str

class RVPUserResponse(BaseModel):
    id: int
    username: str
    role: str
    rvp: str
    email: Optional[str]
    phone: Optional[str]

class ExamAttemptCreate(RecruitInput):
    exam_type: str = Field(..., pattern="^(Qualification|Provincial)$")
    exam_date: str = Field(..., pattern=r"^\d{4}-\d{2}-\d{2}$")
    result: str = Field(..., pattern="^(Pass|Fail)$")
    notes: Optional[str] = Field(None, max_length=500)

class ExamHistoryResponse(BaseModel):
    id: int
    recruit_id: int
    exam_type: str
    exam_date: str
    result: str
    notes: Optional[str]
    created_at: str

class RecruitCreate(RecruitInput):
    first_name: str = Field(..., min_length=1, max_length=50)
    last_name: str = Field(..., min_length=1, max_length=50)
    email: Optional[str] = Field(None, max_length=100)
    phone: Optional[str] = Field(None, max_length=20)
    iba_date: Optional[str] = Field(None, pattern=r"^(\d{4}-\d{2}-\d{2})?$")
    iba_status: Optional[str] = Field("Pending", pattern="^(Pending|Approved)$")
    solution_number: Optional[str] = Field(None, pattern=r"^([a-zA-Z0-9]{5})?$")
    rvp: Optional[str] = Field(None, pattern="^(Mike Vanderloos|Luciano DiSerafino|Matt Lumley)?$")

class RecruitUpdate(RecruitInput):
    first_name: Optional[str] = Field(None, min_length=1, max_length=50)
    last_name: Optional[str] = Field(None, min_length=1, max_length=50)
    email: Optional[str] = Field(None, max_length=100)
    phone: Optional[str] = Field(None, max_length=20)
    iba_date: Optional[str] = Field(None, pattern=r"^(\d{4}-\d{2}-\d{2})?$")
    iba_status: Optional[str] = Field(None, pattern="^(Pending|Approved)$")
    pol_account_logged_in: Optional[bool] = None
    whatsapp_invite_sent: Optional[bool] = None
    llqp_registered: Optional[bool] = None
    llqp_class_date: Optional[str] = Field(None, pattern=r"^(\d{4}-\d{2}-\d{2})?$")
    w1_sat_attended: Optional[bool] = None
    w1_sun_attended: Optional[bool] = None
    w2_sat_attended: Optional[bool] = None
    w2_sun_attended: Optional[bool] = None
    qual_exam_status: Optional[str] = Field(None, pattern="^(Not Taken|Passed|Failed)$")
    qual_exam_date: Optional[str] = Field(None, pattern=r"^(\d{4}-\d{2}-\d{2})?$")
    prov_exam_booked: Optional[bool] = None
    prov_exam_booking_date: Optional[str] = Field(None, pattern=r"^(\d{4}-\d{2}-\d{2})?$")
    prov_exam_date: Optional[str] = Field(None, pattern=r"^(\d{4}-\d{2}-\d{2})?$")
    prov_exam_status: Optional[str] = Field(None, pattern="^(Not Taken|Passed|Failed)$")
    license_app_submitted: Optional[bool] = None
    license_app_date: Optional[str] = Field(None, pattern=r"^(\d{4}-\d{2}-\d{2})?$")
    license_app_status: Optional[str] = Field(None, pattern="^(Pending|Approved|Rejected)$")
    reimbursement_submitted: Optional[bool] = None
    reimbursement_date: Optional[str] = Field(None, pattern=r"^(\d{4}-\d{2}-\d{2})?$")
    reimbursement_amount: Optional[float] = Field(None, ge=0)
    reimbursement_status: Optional[str] = Field(None, pattern="^(Pending|Approved|Rejected)$")
    reimbursement_notes: Optional[str] = Field(None, max_length=1000)
    rvp_invoice_status: Optional[str] = Field(None, pattern="^(Pending|Invoiced)$")
    rvp_invoice_date: Optional[str] = Field(None, pattern=r"^(\d{4}-\d{2}-\d{2})?$")
    rvp_invoice_amount: Optional[float] = Field(None, ge=0, le=50)
    mf_course_registered: Optional[bool] = None
    mf_registration_date: Optional[str] = Field(None, pattern=r"^(\d{4}-\d{2}-\d{2})?$")
    mf_study_completed: Optional[bool] = None
    mf_review_1_attended: Optional[bool] = None
    mf_review_2_attended: Optional[bool] = None
    mf_review_3_attended: Optional[bool] = None
    mf_review_4_attended: Optional[bool] = None
    mf_review_5_attended: Optional[bool] = None
    mf_review_6_attended: Optional[bool] = None
    mf_exam_booked: Optional[bool] = None
    mf_exam_date: Optional[str] = Field(None, pattern=r"^(\d{4}-\d{2}-\d{2})?$")
    mf_exam_status: Optional[str] = Field(None, pattern="^(Not Taken|Passed|Failed)$")
    mf_nrd_application_completed: Optional[bool] = None
    mf_roadd_completed: Optional[bool] = None
    mf_nrd_access_date: Optional[str] = Field(None, pattern=r"^(\d{4}-\d{2}-\d{2})?$")
    mf_nrd_form4_completed: Optional[bool] = None
    mf_pfsl10_completed: Optional[bool] = None
    mf_approved_person_agreement_completed: Optional[bool] = None
    mf_license_received: Optional[bool] = None
    mf_license_received_date: Optional[str] = Field(None, pattern=r"^(\d{4}-\d{2}-\d{2})?$")
    mf_supervision_completed: Optional[bool] = None
    mf_district_leader_at_signup: Optional[bool] = None
    mf_district_leader_date: Optional[str] = Field(None, pattern=r"^(\d{4}-\d{2}-\d{2})?$")
    mf_reimbursement_status: Optional[str] = Field(None, pattern="^(Pending|Approved|Rejected)$")
    solution_number: Optional[str] = Field(None, pattern=r"^([a-zA-Z0-9]{5})?$")
    rvp: Optional[str] = Field(None, pattern="^(Mike Vanderloos|Luciano DiSerafino|Matt Lumley)?$")

class RecruitResponse(BaseModel):
    id: int
    first_name: str
    last_name: str
    email: Optional[str]
    phone: Optional[str]
    iba_date: Optional[str]
    iba_status: str
    pol_account_logged_in: bool
    whatsapp_invite_sent: bool
    llqp_registered: bool
    llqp_class_date: Optional[str]
    w1_sat_attended: bool
    w1_sun_attended: bool
    w2_sat_attended: bool
    w2_sun_attended: bool
    qual_exam_status: str
    qual_exam_date: Optional[str]
    prov_exam_booked: bool
    prov_exam_booking_date: Optional[str]
    prov_exam_date: Optional[str]
    prov_exam_status: str
    license_app_submitted: bool
    license_app_date: Optional[str]
    license_app_status: str
    reimbursement_submitted: bool
    reimbursement_date: Optional[str]
    reimbursement_amount: float
    reimbursement_status: str
    reimbursement_notes: Optional[str]
    rvp_invoice_status: str
    rvp_invoice_date: Optional[str]
    rvp_invoice_amount: float
    mf_course_registered: bool
    mf_registration_date: Optional[str]
    mf_study_completed: bool
    mf_review_1_attended: bool
    mf_review_2_attended: bool
    mf_review_3_attended: bool
    mf_review_4_attended: bool
    mf_review_5_attended: bool
    mf_review_6_attended: bool
    mf_exam_booked: bool
    mf_exam_date: Optional[str]
    mf_exam_status: str
    mf_nrd_application_completed: bool
    mf_roadd_completed: bool
    mf_nrd_access_date: Optional[str]
    mf_nrd_form4_completed: bool
    mf_pfsl10_completed: bool
    mf_approved_person_agreement_completed: bool
    mf_license_received: bool
    mf_license_received_date: Optional[str]
    mf_supervision_completed: bool
    mf_district_leader_at_signup: bool
    mf_district_leader_date: Optional[str]
    mf_reimbursement_status: str
    solution_number: Optional[str]
    rvp: Optional[str]
    created_at: str
    updated_at: str
    exam_attempts: List[ExamHistoryResponse] = Field(default_factory=list)
    prov_booking_days_left: Optional[int] = None

class RecruitImportResponse(BaseModel):
    imported: int
    skipped: List[str] = Field(default_factory=list)
