from datetime import date, datetime, time
from decimal import Decimal

from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_validator, model_validator


class ORMModel(BaseModel):
    model_config = ConfigDict(from_attributes=True)


class TokenResponse(BaseModel):
    access_token: str
    refresh_token: str
    token_type: str = "bearer"


class LoginRequest(BaseModel):
    email: EmailStr
    password: str


class RefreshRequest(BaseModel):
    refresh_token: str


class PermissionOut(ORMModel):
    id: int
    key: str
    name: str
    module: str


class RoleOut(ORMModel):
    id: int
    name: str
    description: str | None
    is_system: bool
    permissions: list[PermissionOut] = []


class RoleCreate(BaseModel):
    name: str = Field(min_length=1, max_length=80)
    description: str | None = None
    permission_keys: list[str] = []


class RoleUpdate(BaseModel):
    name: str | None = Field(default=None, min_length=1, max_length=80)
    description: str | None = None
    permission_keys: list[str] | None = None


class UserOut(ORMModel):
    id: int
    name: str
    email: str
    phone: str | None
    is_active: bool
    created_at: datetime
    roles: list[RoleOut] = []
    permissions: list[str] = []


class UserCreate(BaseModel):
    name: str
    email: EmailStr
    phone: str | None = None
    password: str = Field(min_length=6)
    role_ids: list[int] = []
    is_active: bool = True


class UserUpdate(BaseModel):
    name: str | None = None
    email: EmailStr | None = None
    phone: str | None = None
    password: str | None = Field(default=None, min_length=6)
    is_active: bool | None = None
    role_ids: list[int] | None = None


class ClientIn(BaseModel):
    name: str
    company_name: str | None = None
    client_type: str = "Individual"
    phone: str
    alternate_phone: str | None = None
    email: str | None = None
    address: str | None = None
    district: str | None = None
    city: str | None = None
    lead_source: str | None = None
    booking_channel: str | None = None
    status: str = "Active"
    notes: str | None = None


class ClientOut(ORMModel):
    id: int
    public_id: str
    name: str
    company_name: str | None
    client_type: str
    phone: str
    alternate_phone: str | None
    email: str | None
    address: str | None
    district: str | None
    city: str | None
    lead_source: str | None
    booking_channel: str | None
    first_enquiry_date: date | None
    last_enquiry_date: date | None
    status: str
    notes: str | None
    google_review_status: str | None
    referral_generated: bool
    average_rating: Decimal | None
    created_at: datetime
    stats: dict | None = None


class LineItemIn(BaseModel):
    product_id: int | None = None
    name: str
    quantity: int = 1
    unit_price: Decimal = 0
    discount: Decimal = 0
    cost: Decimal = 0

    @field_validator("unit_price", "discount", "cost", mode="before")
    @classmethod
    def _null_decimal_to_zero(cls, value):
        return 0 if value is None else value


class LineItemOut(ORMModel):
    id: int
    product_id: int | None = None
    name: str
    quantity: int
    unit_price: Decimal
    discount: Decimal
    amount: Decimal
    cost: Decimal | None = None


class EnquiryIn(BaseModel):
    client_id: int | None = None
    client_name: str
    company_name: str | None = None
    phone: str
    email: str | None = None
    event_type: str | None = None
    event_date: date | None = None
    start_time: time | None = None
    end_time: time | None = None
    location: str | None = None
    district: str | None = None
    city: str | None = None
    venue: str | None = None
    requirements: str | None = None
    expected_budget: Decimal | None = None
    lead_source: str | None = None
    booking_channel: str | None = None
    assigned_user_id: int | None = None
    status: str = "New"
    products: list[LineItemIn] = []


class EnquiryOut(ORMModel):
    id: int
    public_id: str
    client_id: int
    client_name: str
    phone: str
    email: str | None
    event_type: str | None
    event_date: date | None
    start_time: time | None = None
    end_time: time | None = None
    location: str | None
    district: str | None
    city: str | None
    venue: str | None
    requirements: str | None
    expected_budget: Decimal | None
    lead_source: str | None
    booking_channel: str | None
    assigned_user_id: int | None
    status: str
    created_at: datetime
    updated_at: datetime
    products: list[LineItemOut] = []
    event_id: int | None = None
    event_public_id: str | None = None


class FollowupIn(BaseModel):
    enquiry_id: int
    assigned_user_id: int | None = None
    followup_date: date
    followup_time: time | None = None
    followup_type: str = "Call"
    notes: str | None = None
    status: str = "Pending"
    next_followup_date: date | None = None


class FollowupOut(ORMModel):
    id: int
    enquiry_id: int
    assigned_user_id: int | None
    followup_date: date
    followup_time: time | None
    followup_type: str
    notes: str | None
    status: str
    next_followup_date: date | None


class QuotationIn(BaseModel):
    enquiry_id: int | None = None
    client_id: int
    event_id: int | None = None
    quotation_date: date
    valid_until: date | None = None
    discount: Decimal = 0
    terms: str | None = None
    status: str = "Draft"
    items: list[LineItemIn] = []


class QuotationFromEnquiryIn(BaseModel):
    transport_amount: Decimal = 0
    discount: Decimal = 0
    terms: str | None = None
    status: str = "Draft"


class QuotationOut(ORMModel):
    id: int
    public_id: str
    enquiry_id: int | None
    client_id: int
    event_id: int | None
    quotation_date: date
    valid_until: date | None
    discount: Decimal
    subtotal: Decimal
    final_amount: Decimal
    terms: str | None
    status: str
    items: list[LineItemOut] = []


class ProductCategoryIn(BaseModel):
    name: str
    description: str | None = None
    is_active: bool = True


class ProductCategoryOut(ORMModel):
    id: int
    name: str
    description: str | None
    is_active: bool


class ProductIn(BaseModel):
    public_id: str | None = None
    slug: str | None = None
    name: str
    category_id: int | None = None
    category_name: str | None = None
    description: str | None = None
    base_price: Decimal = 0
    cost: Decimal = 0
    service_duration: str | None = None
    image_url: str | None = None
    video_url: str | None = None
    is_active: bool = True


class ProductOut(ORMModel):
    id: int
    public_id: str
    slug: str | None = None
    name: str
    category_id: int | None
    category_name: str | None = None
    description: str | None
    base_price: Decimal
    cost: Decimal
    service_duration: str | None
    image_url: str | None
    video_url: str | None = None
    is_active: bool


class PublicProductOut(ORMModel):
    id: int
    public_id: str
    slug: str
    name: str
    description: str | None
    category_name: str | None = None
    base_price: Decimal
    image_url: str | None
    video_url: str | None = None


class PublicCompanyOut(BaseModel):
    company_name: str
    phone: str | None = None
    email: str | None = None
    address: str | None = None
    website: str | None = None


class PublicGalleryItemOut(ORMModel):
    id: int
    title: str
    tag: str
    image_url: str
    sort_order: int


class GalleryItemIn(BaseModel):
    title: str
    tag: str = "Celebration"
    image_url: str
    is_active: bool = True
    sort_order: int = 0


class GalleryItemOut(ORMModel):
    id: int
    title: str
    tag: str
    image_url: str
    is_active: bool
    sort_order: int


class EventIn(BaseModel):
    name: str
    event_type: str | None = None
    client_id: int
    enquiry_id: int | None = None
    quotation_id: int | None = None
    event_date: date
    district: str | None = None
    city: str | None = None
    venue_name: str | None = None
    venue_address: str | None = None
    start_time: time | None = None
    end_time: time | None = None
    status: str = "Confirmed"
    lead_source: str | None = None
    booking_channel: str | None = None
    products: list[LineItemIn] = []


class EventOut(ORMModel):
    id: int
    public_id: str
    name: str
    event_type: str | None
    client_id: int
    enquiry_id: int | None
    quotation_id: int | None
    invoice_id: int | None
    event_date: date
    district: str | None
    city: str | None
    venue_name: str | None
    venue_address: str | None
    start_time: time | None
    end_time: time | None
    status: str
    lead_source: str | None
    booking_channel: str | None
    products: list[LineItemOut] = []
    profitability: dict | None = None


class ConvertEnquiryIn(BaseModel):
    name: str | None = None
    event_date: date | None = None


class EmployeeIn(BaseModel):
    name: str
    phone: str
    email: str | None = None
    role: str
    department: str | None = None
    joining_date: date | None = None
    employment_type: str = "Regular"
    salary: Decimal | None = None
    availability_status: str = "Available"
    is_active: bool = True
    address: str | None = None
    emergency_contact: str | None = None

    @field_validator("joining_date", mode="before")
    @classmethod
    def parse_joining_date(cls, value):
        if value in (None, ""):
            return None
        if isinstance(value, date) and not isinstance(value, datetime):
            return value
        text = str(value).strip()
        if not text:
            return None
        return date.fromisoformat(text[:10])


class EmployeeOut(ORMModel):
    id: int
    public_id: str
    name: str
    phone: str
    email: str | None
    role: str
    department: str | None
    joining_date: date | None
    employment_type: str
    salary: Decimal | None
    availability_status: str
    is_active: bool
    address: str | None
    emergency_contact: str | None


class AllocationIn(BaseModel):
    event_id: int
    employee_id: int
    assigned_role: str
    allocation_date: date
    status: str = "Assigned"
    event_payment: Decimal = 0
    travel_allowance: Decimal = 0
    other_allowance: Decimal = 0
    manager_feedback: str | None = None
    performance_rating: Decimal | None = None
    tasks_completed: int = 0


class AllocationOut(ORMModel):
    id: int
    event_id: int
    employee_id: int
    assigned_role: str
    allocation_date: date
    status: str
    event_payment: Decimal
    travel_allowance: Decimal
    other_allowance: Decimal
    total_staff_cost: Decimal
    manager_feedback: str | None
    performance_rating: Decimal | None
    tasks_completed: int
    employee_name: str | None = None
    event_name: str | None = None


class InvoiceIn(BaseModel):
    event_id: int | None = None
    client_id: int
    invoice_date: date
    due_date: date | None = None
    discount: Decimal = 0
    status: str = "Issued"
    items: list[LineItemIn] = []


class InvoiceDraftItemOut(BaseModel):
    product_id: int | None = None
    name: str
    quantity: int
    unit_price: Decimal
    discount: Decimal = 0
    amount: Decimal


class InvoiceDraftOut(BaseModel):
    event_id: int
    client_id: int
    client_name: str | None = None
    event_name: str | None = None
    event_public_id: str | None = None
    invoice_date: date
    due_date: date | None = None
    discount: Decimal = 0
    status: str = "Issued"
    items: list[InvoiceDraftItemOut] = []
    subtotal: Decimal = 0
    final_amount: Decimal = 0
    existing_invoice_id: int | None = None
    existing_invoice_number: str | None = None


class InvoiceOut(ORMModel):
    id: int
    public_id: str
    invoice_number: str
    event_id: int | None
    client_id: int
    invoice_date: date
    due_date: date | None
    subtotal: Decimal
    discount: Decimal
    final_amount: Decimal
    amount_paid: Decimal
    balance_amount: Decimal
    payment_status: str
    status: str
    items: list[LineItemOut] = []


class PaymentIn(BaseModel):
    invoice_id: int | None = None
    event_id: int | None = None
    client_id: int | None = None
    payment_date: date
    amount: Decimal
    payment_mode: str = "UPI"
    transaction_reference: str | None = None
    notes: str | None = None

    @model_validator(mode="after")
    def require_invoice_or_event(self):
        if self.invoice_id is None and self.event_id is None:
            raise ValueError("Either invoice_id or event_id is required")
        return self


class PaymentOut(ORMModel):
    id: int
    public_id: str
    invoice_id: int
    event_id: int | None
    client_id: int
    payment_date: date
    amount: Decimal
    payment_mode: str
    transaction_reference: str | None
    notes: str | None
    recorded_by_id: int | None


class ExpenseIn(BaseModel):
    event_id: int | None = None
    category: str
    description: str | None = None
    amount: Decimal
    expense_date: date
    payment_mode: str = "Cash"
    receipt_url: str | None = None
    approval_status: str = "Pending"
    notes: str | None = None


class ExpenseOut(ORMModel):
    id: int
    public_id: str
    event_id: int | None
    category: str
    description: str | None
    amount: Decimal
    expense_date: date
    payment_mode: str
    receipt_url: str | None
    added_by_id: int | None
    approval_status: str
    notes: str | None


class FormFieldIn(BaseModel):
    label: str
    field_key: str
    field_type: str = "text"
    is_required: bool = False
    sort_order: int = 0
    options: list[str] = []


class FormIn(BaseModel):
    name: str
    description: str | None = None
    is_active: bool = True
    fields: list[FormFieldIn] = []


class FormFieldOut(ORMModel):
    id: int
    label: str
    field_key: str
    field_type: str
    is_required: bool
    sort_order: int
    options: list[str] = []


class FormOut(ORMModel):
    id: int
    name: str
    description: str | None
    is_active: bool
    fields: list[FormFieldOut] = []


class FormSubmitIn(BaseModel):
    payload: dict


class FormSubmissionOut(ORMModel):
    id: int
    form_id: int
    payload: dict
    enquiry_id: int | None
    client_id: int | None
    created_at: datetime


class LeadSourceIn(BaseModel):
    name: str
    is_active: bool = True


class LeadSourceOut(ORMModel):
    id: int
    name: str
    is_active: bool


class BookingChannelIn(BaseModel):
    name: str
    is_active: bool = True


class BookingChannelOut(ORMModel):
    id: int
    name: str
    is_active: bool


class EmploymentTypeIn(BaseModel):
    name: str
    is_active: bool = True


class EmploymentTypeOut(ORMModel):
    id: int
    name: str
    is_active: bool


class CompanySettingIn(BaseModel):
    company_name: str
    phone: str | None = None
    email: str | None = None
    address: str | None = None
    gstin: str | None = None
    website: str | None = None


class CompanySettingOut(ORMModel):
    id: int
    company_name: str
    phone: str | None
    email: str | None
    address: str | None
    gstin: str | None
    website: str | None


class InvoiceSettingIn(BaseModel):
    prefix: str = "INV"
    next_number: int = 1
    default_due_days: int = 7
    terms: str | None = None


class InvoiceSettingOut(ORMModel):
    id: int
    prefix: str
    next_number: int
    default_due_days: int
    terms: str | None


class QuotationSettingIn(BaseModel):
    prefix: str = "QUO"
    next_number: int = 1


class QuotationSettingOut(ORMModel):
    id: int
    prefix: str
    next_number: int


class SystemSettingIn(BaseModel):
    currency: str = "INR"
    timezone: str = "Asia/Kolkata"
    date_format: str = "dd MMM yyyy"


class SystemSettingOut(ORMModel):
    id: int
    currency: str
    timezone: str
    date_format: str


class FeedbackIn(BaseModel):
    event_id: int
    client_id: int
    rating: Decimal | None = None
    google_review_given: bool = False
    notes: str | None = None


class FeedbackOut(ORMModel):
    id: int
    event_id: int
    client_id: int
    rating: Decimal | None
    google_review_given: bool
    notes: str | None
