from typing import Optional, List, Dict, Any, Annotated
from pydantic import BaseModel, Field, ConfigDict, field_validator, model_validator, BeforeValidator
from datetime import datetime
from decimal import Decimal

StrId = Annotated[str, BeforeValidator(str)]


# Base schemas
class JobBase(BaseModel):
    title: str = Field(..., max_length=500)
    description: Optional[str] = None
    job_type_id: str = Field(..., max_length=36)
    job_status_id: str = Field(..., max_length=36)
    job_priority_id: str = Field(..., max_length=36)
    department_id: str = Field(..., max_length=36)
    team_id: Optional[str] = Field(None, max_length=36)
    parent_job_id: Optional[str] = Field(None, max_length=36)
    assigned_to_user_id: Optional[str] = Field(None, max_length=36)
    due_date: Optional[datetime] = None
    started_at: Optional[datetime] = None
    completed_at: Optional[datetime] = None
    estimated_hours: Optional[Decimal] = Field(None, max_digits=10, decimal_places=2)

    @field_validator("title")
    @classmethod
    def validate_title(cls, v):
        if v is not None:
            v = v.strip()
            if len(v) < 5 or len(v) > 200:
                raise ValueError("Job title must be between 5 and 200 characters")
        return v

    @field_validator("description")
    @classmethod
    def validate_description(cls, v):
        if v is not None:
            v = v.strip()
            if len(v) > 0 and len(v) < 10:
                raise ValueError("Job description must be at least 10 characters if provided")
        return v

    @field_validator("estimated_hours")
    @classmethod
    def validate_estimated_hours(cls, v):
        if v is not None and v < 0:
            raise ValueError("Estimated hours must be non-negative")
        return v

    @model_validator(mode="after")
    def validate_dates(self):
        if self.started_at and self.completed_at and self.completed_at < self.started_at:
            raise ValueError("Completion date must be after start date")
        if self.started_at and self.due_date and self.started_at > self.due_date:
            raise ValueError("Start date cannot be after due date")
        return self


class JobCreate(JobBase):
    created_by_user_id: str = Field(..., max_length=36)

    @field_validator("due_date")
    @classmethod
    def validate_due_date_future(cls, v):
        if v is not None:
            from datetime import datetime, timezone
            if v < datetime.now(timezone.utc):
                raise ValueError("Due date must be in the future when creating a new job")
        return v


class JobUpdate(BaseModel):
    title: Optional[str] = Field(None, max_length=500)
    description: Optional[str] = None
    job_type_id: Optional[str] = Field(None, max_length=36)
    job_status_id: Optional[str] = Field(None, max_length=36)
    job_priority_id: Optional[str] = Field(None, max_length=36)
    department_id: Optional[str] = Field(None, max_length=36)
    team_id: Optional[str] = Field(None, max_length=36)
    parent_job_id: Optional[str] = Field(None, max_length=36)
    assigned_to_user_id: Optional[str] = Field(None, max_length=36)
    due_date: Optional[datetime] = None
    started_at: Optional[datetime] = None
    completed_at: Optional[datetime] = None
    estimated_hours: Optional[Decimal] = Field(None, max_digits=10, decimal_places=2)

    @field_validator("title")
    @classmethod
    def validate_title(cls, v):
        if v is not None:
            v = v.strip()
            if len(v) < 5 or len(v) > 200:
                raise ValueError("Job title must be between 5 and 200 characters")
        return v

    @field_validator("description")
    @classmethod
    def validate_description(cls, v):
        if v is not None:
            v = v.strip()
            if len(v) > 0 and len(v) < 10:
                raise ValueError("Job description must be at least 10 characters if provided")
        return v

    @field_validator("estimated_hours")
    @classmethod
    def validate_estimated_hours(cls, v):
        if v is not None and v < 0:
            raise ValueError("Estimated hours must be non-negative")
        return v


class JobResponse(BaseModel):
    id: StrId
    title: str
    description: Optional[str]
    job_type_id: StrId
    job_status_id: StrId
    job_priority_id: StrId
    created_by_user_id: StrId
    assigned_to_user_id: Optional[StrId]
    department_id: StrId
    team_id: Optional[StrId]
    parent_job_id: Optional[StrId]
    due_date: Optional[datetime]
    started_at: Optional[datetime]
    completed_at: Optional[datetime]
    estimated_hours: Optional[Decimal]
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# Job Tag schemas
class JobtagBase(BaseModel):
    job_id: str = Field(..., max_length=36)
    tag_id: str = Field(..., max_length=36)


class JobtagCreate(JobtagBase):
    pass


class JobtagUpdate(BaseModel):
    job_id: Optional[str] = Field(None, max_length=36)
    tag_id: Optional[str] = Field(None, max_length=36)


class JobtagResponse(BaseModel):
    id: StrId
    job_id: StrId
    tag_id: StrId
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# Comment schemas
class CommentBase(BaseModel):
    job_id: str = Field(..., max_length=36)
    content: str = Field(..., min_length=1, max_length=5000)

    @field_validator("content")
    @classmethod
    def validate_content(cls, v):
        if v is not None:
            v = v.strip()
            if len(v) < 1 or len(v) > 5000:
                raise ValueError("Comment content must be 1-5000 characters")
        return v


class CommentCreate(CommentBase):
    user_id: str = Field(..., max_length=36)


class CommentUpdate(BaseModel):
    content: Optional[str] = Field(None, min_length=1, max_length=5000)

    @field_validator("content")
    @classmethod
    def validate_content(cls, v):
        if v is not None:
            v = v.strip()
            if len(v) < 1 or len(v) > 5000:
                raise ValueError("Comment content must be 1-5000 characters")
        return v


class CommentResponse(BaseModel):
    id: StrId
    job_id: StrId
    user_id: StrId
    content: str
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# Attachment schemas
class AttachmentBase(BaseModel):
    job_id: str = Field(..., max_length=36)
    file_name: str = Field(..., max_length=500)
    file_path: str = Field(..., max_length=1000)
    file_type: Optional[str] = Field(None, max_length=100)
    file_size: int


class AttachmentCreate(AttachmentBase):
    uploaded_by_user_id: str = Field(..., max_length=36)


class AttachmentUpdate(BaseModel):
    file_name: Optional[str] = Field(None, max_length=500)
    file_path: Optional[str] = Field(None, max_length=1000)
    file_type: Optional[str] = Field(None, max_length=100)
    file_size: Optional[int] = None


class AttachmentResponse(BaseModel):
    id: StrId
    job_id: StrId
    uploaded_by_user_id: StrId
    file_name: str
    file_path: str
    file_type: Optional[str]
    file_size: int
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# Job History schemas
class JobhistoryBase(BaseModel):
    job_id: str = Field(..., max_length=36)
    action_type: str = Field(..., max_length=100)
    field_name: Optional[str] = Field(None, max_length=255)
    old_value: Optional[str] = None
    new_value: Optional[str] = None


class JobhistoryCreate(JobhistoryBase):
    user_id: str = Field(..., max_length=36)


class JobhistoryUpdate(BaseModel):
    action_type: Optional[str] = Field(None, max_length=100)
    field_name: Optional[str] = Field(None, max_length=255)
    old_value: Optional[str] = None
    new_value: Optional[str] = None


class JobhistoryResponse(BaseModel):
    id: StrId
    job_id: StrId
    user_id: StrId
    action_type: str
    field_name: Optional[str]
    old_value: Optional[str]
    new_value: Optional[str]
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# Detail response schemas
class JobTypeNested(BaseModel):
    id: StrId
    name: str

    model_config = ConfigDict(from_attributes=True)


class JobStatusNested(BaseModel):
    id: StrId
    name: str

    model_config = ConfigDict(from_attributes=True)


class JobPriorityNested(BaseModel):
    id: StrId
    name: str

    model_config = ConfigDict(from_attributes=True)


class UserNested(BaseModel):
    id: StrId
    first_name: str
    last_name: str

    model_config = ConfigDict(from_attributes=True)


class DepartmentNested(BaseModel):
    id: StrId
    name: str

    model_config = ConfigDict(from_attributes=True)


class TeamNested(BaseModel):
    id: StrId
    name: str

    model_config = ConfigDict(from_attributes=True)


class ParentJobNested(BaseModel):
    id: StrId
    title: str

    model_config = ConfigDict(from_attributes=True)


class CommentDetailNested(BaseModel):
    id: StrId
    content: str
    created_at: datetime
    user: UserNested

    model_config = ConfigDict(from_attributes=True)


class AttachmentDetailNested(BaseModel):
    id: StrId
    file_name: str
    file_size: int
    created_at: datetime
    uploaded_by_user: UserNested

    model_config = ConfigDict(from_attributes=True)


class JobhistoryDetailNested(BaseModel):
    id: StrId
    action_type: str
    field_name: Optional[str]
    old_value: Optional[str]
    new_value: Optional[str]
    created_at: datetime
    user: UserNested

    model_config = ConfigDict(from_attributes=True)


class TimelogDetailNested(BaseModel):
    id: StrId
    start_time: datetime
    end_time: Optional[datetime]
    duration_minutes: Optional[int]
    user: UserNested

    model_config = ConfigDict(from_attributes=True)


class TagNested(BaseModel):
    id: StrId
    name: str

    model_config = ConfigDict(from_attributes=True)


class JobtagDetailNested(BaseModel):
    id: StrId
    tag: TagNested

    model_config = ConfigDict(from_attributes=True)


class ChecklistitemDetailNested(BaseModel):
    id: StrId
    description: str
    is_completed: bool
    assigned_to_user_id: Optional[StrId]
    display_order: int
    completed_at: Optional[datetime]
    assigned_to_user: Optional[UserNested]

    model_config = ConfigDict(from_attributes=True)


class ChecklistDetailNested(BaseModel):
    id: StrId
    title: str
    display_order: int
    checklist_items: List[ChecklistitemDetailNested]

    model_config = ConfigDict(from_attributes=True)


class JobDetailResponse(BaseModel):
    id: StrId
    title: str
    description: Optional[str]
    job_type_id: StrId
    job_status_id: StrId
    job_priority_id: StrId
    created_by_user_id: StrId
    assigned_to_user_id: Optional[StrId]
    department_id: StrId
    team_id: Optional[StrId]
    parent_job_id: Optional[StrId]
    due_date: Optional[datetime]
    started_at: Optional[datetime]
    completed_at: Optional[datetime]
    estimated_hours: Optional[Decimal]
    created_at: datetime
    updated_at: datetime
    job_type: JobTypeNested
    job_status: JobStatusNested
    job_priority: JobPriorityNested
    created_by_user: UserNested
    assigned_to_user: Optional[UserNested]
    department: DepartmentNested
    team: Optional[TeamNested]
    parent_job: Optional[ParentJobNested]
    comments: List[CommentDetailNested]
    attachments: List[AttachmentDetailNested]
    job_history: List[JobhistoryDetailNested]
    time_logs: List[TimelogDetailNested]
    job_tags: List[JobtagDetailNested]
    checklists: List[ChecklistDetailNested]

    model_config = ConfigDict(from_attributes=True)


# Paginated response schemas
class PaginatedJobResponse(BaseModel):
    items: List[JobResponse]
    total: int
    limit: int
    offset: int

    model_config = ConfigDict(from_attributes=True)


class PaginatedCommentResponse(BaseModel):
    items: List[CommentResponse]
    total: int
    limit: int
    offset: int

    model_config = ConfigDict(from_attributes=True)


class PaginatedAttachmentResponse(BaseModel):
    items: List[AttachmentResponse]
    total: int
    limit: int
    offset: int

    model_config = ConfigDict(from_attributes=True)


class PaginatedJobhistoryResponse(BaseModel):
    items: List[JobhistoryResponse]
    total: int
    limit: int
    offset: int

    model_config = ConfigDict(from_attributes=True)


# Workflow schemas
class JobAssignRequest(BaseModel):
    assigned_to_user_id: str = Field(..., max_length=36)


class JobReassignRequest(BaseModel):
    assigned_to_user_id: str = Field(..., max_length=36)


class JobStatusUpdateRequest(BaseModel):
    job_status_id: str = Field(..., max_length=36)