from __future__ import annotations
from datetime import date, datetime
from decimal import Decimal

from pydantic import Field, field_validator

from app.models.enums import ProcurementStatus
from app.schemas.common import ORMModel


class AuditFields(ORMModel):
    id: int
    created_at: datetime
    updated_at: datetime


class DemandCreate(ORMModel):
    demand_no: str = Field(min_length=2, max_length=50)
    department: str
    item_description: str
    quantity: int = Field(gt=0)
    estimated_value: Decimal = Decimal("0.00")
    required_by: date | None = None
    status: ProcurementStatus = ProcurementStatus.draft


class DemandUpdate(ORMModel):
    demand_no: str | None = None
    department: str | None = None
    item_description: str | None = None
    quantity: int | None = Field(default=None, gt=0)
    estimated_value: Decimal | None = None
    required_by: date | None = None
    status: ProcurementStatus | None = None


class DemandRead(DemandCreate, AuditFields):
    pass


class ItemDatabaseCreate(ORMModel):
    sr_no: int = Field(gt=0)
    name: str = Field(min_length=1, max_length=160)
    name_en: str | None = None
    name_or: str | None = None
    item_type: str = Field(default="Equipment", min_length=1, max_length=80)
    item_type_en: str | None = None
    item_type_or: str | None = None
    description: str | None = None
    description_en: str | None = None
    description_or: str | None = None
    quantity: int = Field(default=0, ge=0)
    remarks: str | None = None
    is_active: bool = True


class ItemDatabaseUpdate(ORMModel):
    sr_no: int | None = Field(default=None, gt=0)
    name: str | None = Field(default=None, min_length=1, max_length=160)
    name_en: str | None = None
    name_or: str | None = None
    item_type: str | None = Field(default=None, min_length=1, max_length=80)
    item_type_en: str | None = None
    item_type_or: str | None = None
    description: str | None = None
    description_en: str | None = None
    description_or: str | None = None
    quantity: int | None = Field(default=None, ge=0)
    remarks: str | None = None
    is_active: bool | None = None


class ItemDatabaseRead(ItemDatabaseCreate, AuditFields):
    pass


class RequisitionItem(ORMModel):
    serial_no: int | None = Field(default=None, ge=1)
    description: str = Field(min_length=1)
    item_type: str | None = None
    quantity: int | None = Field(default=None, ge=0)
    challan_number: str | None = None
    challan_date: date | None = None
    quantity_required: int | None = Field(default=None, ge=0)
    quantity_available: int | None = Field(default=None, ge=0)
    present_condition: str | None = None
    condemnation_status: str | None = None
    condemnation_date_year: str | None = None
    justification: str | None = None
    remarks: str | None = None
    date_of_supply: date | None = None
    cause_of_unserviceability: str | None = None
    unserviceable_remarks: str | None = None
    unit: str | None = None
    estimated_cost: float | None = None

    @field_validator("condemnation_status", mode="before")
    @classmethod
    def normalize_condemnation_status(cls, value):
        if value in {"Pending", "Under Process", "Condemned"}:
            return "Condemnable"
        if value == "Not Applicable":
            return "Not Condemnable"
        return value


class RequisitionCreate(ORMModel):
    requisition_no: str
    demand_id: int | None = None
    requisition_date: date | None = None
    requested_by: str | None = None
    received_from: str | None = None
    received_from_district: str | None = None
    received_from_range: str | None = None
    sender_name: str | None = None
    sender_designation: str | None = None
    requisition_items: list[RequisitionItem] = Field(default_factory=list)
    item_description: str | None = None
    quantity: int | None = Field(default=None, ge=0)
    estimated_cost: Decimal | None = None
    procurement_mode: str | None = None
    procurement_mode_other: str | None = Field(default=None, max_length=255)
    budget_head: str | None = None
    administrative_approval_date: date | None = None
    administrative_approval_quantity: int | None = Field(default=None, ge=0)
    lifecycle_stage: str | None = None
    approval_status: ProcurementStatus = ProcurementStatus.submitted
    remarks: str | None = None

    @field_validator("requisition_items", mode="before")
    @classmethod
    def default_requisition_items(cls, value):
        return [] if value is None else value


class RequisitionUpdate(ORMModel):
    requisition_no: str | None = None
    demand_id: int | None = None
    requisition_date: date | None = None
    requested_by: str | None = None
    received_from: str | None = None
    received_from_district: str | None = None
    received_from_range: str | None = None
    sender_name: str | None = None
    sender_designation: str | None = None
    requisition_items: list[RequisitionItem] | None = None
    item_description: str | None = None
    quantity: int | None = Field(default=None, ge=0)
    estimated_cost: Decimal | None = None
    procurement_mode: str | None = None
    procurement_mode_other: str | None = Field(default=None, max_length=255)
    budget_head: str | None = None
    administrative_approval_date: date | None = None
    administrative_approval_quantity: int | None = Field(default=None, ge=0)
    lifecycle_stage: str | None = None
    approval_status: ProcurementStatus | None = None
    remarks: str | None = None


class RequisitionRead(RequisitionCreate, AuditFields):
    pass


class ProcurementCaseCreate(ORMModel):
    procurement_case_no: str = Field(min_length=1, max_length=255)
    requisition_id: int | None = None
    approved_quantity: int | None = Field(default=None, ge=0)
    approved_estimated_cost: Decimal | None = None
    procurement_mode: str
    procurement_mode_other: str | None = Field(default=None, max_length=255)
    case_status: str = "In Tendering Process"
    mode_selection_date: date | None = None
    mode_selected_by: str | None = None
    emd_applicable: bool | None = None
    pbg_applicable: bool | None = None
    file_no: str | None = None
    sanction_no: str | None = None
    sanction_date: date | None = None
    approving_authority: str | None = None
    remarks: str | None = None


class ProcurementCaseUpdate(ORMModel):
    procurement_case_no: str | None = Field(default=None, min_length=1, max_length=255)
    requisition_id: int | None = None
    approved_quantity: int | None = Field(default=None, ge=0)
    approved_estimated_cost: Decimal | None = None
    procurement_mode: str | None = None
    procurement_mode_other: str | None = Field(default=None, max_length=255)
    case_status: str | None = None
    mode_selection_date: date | None = None
    mode_selected_by: str | None = None
    emd_applicable: bool | None = None
    pbg_applicable: bool | None = None
    file_no: str | None = None
    sanction_no: str | None = None
    sanction_date: date | None = None
    approving_authority: str | None = None
    remarks: str | None = None


class ProcurementCaseRead(ProcurementCaseCreate, AuditFields):
    emd_applicable: bool = False
    pbg_applicable: bool = False


class GeMProcurementCaseCreate(ORMModel):
    procurement_case_id: int
    gem_procurement_type: str = "Product / Normal Category Bid"
    gem_bid_no: str | None = None
    gem_bid_date: date | None = None
    gem_contract_no: str | None = None
    gem_contract_date: date | None = None
    gem_order_value: Decimal | None = None
    vendor_id: int | None = None
    delivery_status: str | None = None
    delivery_delay_reason: str | None = None
    delivery_delay_document_path: str | None = None
    payment_status: str | None = None
    status: str = "In Tendering Process"


class GeMProcurementCaseUpdate(ORMModel):
    procurement_case_id: int | None = None
    gem_procurement_type: str | None = None
    gem_bid_no: str | None = None
    gem_bid_date: date | None = None
    gem_contract_no: str | None = None
    gem_contract_date: date | None = None
    gem_order_value: Decimal | None = None
    vendor_id: int | None = None
    delivery_status: str | None = None
    delivery_delay_reason: str | None = None
    delivery_delay_document_path: str | None = None
    payment_status: str | None = None
    status: str | None = None


class GeMProcurementCaseRead(GeMProcurementCaseCreate, AuditFields):
    pass


class ETenderCaseCreate(ORMModel):
    procurement_case_id: int
    tender_ref_no: str
    tender_publication_date: date | None = None
    pre_bid_meeting_date: date | None = None
    bid_submission_end_date: date | None = None
    technical_bid_opening_date: date | None = None
    financial_bid_opening_date: date | None = None
    estimated_cost: Decimal | None = None
    technical_evaluation_status: str | None = None
    financial_evaluation_status: str | None = None
    l1_vendor_id: int | None = None
    award_approval_date: date | None = None
    loa_no: str | None = None
    loa_date: date | None = None
    contract_no: str | None = None
    status: str = "In Tendering Process"


class ETenderCaseUpdate(ORMModel):
    procurement_case_id: int | None = None
    tender_ref_no: str | None = None
    tender_publication_date: date | None = None
    pre_bid_meeting_date: date | None = None
    bid_submission_end_date: date | None = None
    technical_bid_opening_date: date | None = None
    financial_bid_opening_date: date | None = None
    estimated_cost: Decimal | None = None
    technical_evaluation_status: str | None = None
    financial_evaluation_status: str | None = None
    l1_vendor_id: int | None = None
    award_approval_date: date | None = None
    loa_no: str | None = None
    loa_date: date | None = None
    contract_no: str | None = None
    status: str | None = None


class ETenderCaseRead(ETenderCaseCreate, AuditFields):
    pass


class GlobalTenderCaseCreate(ORMModel):
    procurement_case_id: int
    global_tender_ref_no: str
    country_of_vendor: str | None = None
    foreign_vendor_name: str | None = None
    foreign_currency: str | None = None
    exchange_rate: Decimal | None = None
    bid_value_foreign_currency: Decimal | None = None
    bid_value_inr: Decimal | None = None
    technical_evaluation_status: str | None = None
    commercial_evaluation_status: str | None = None
    approval_date: date | None = None
    contract_no: str | None = None
    status: str = "In Tendering Process"


class GlobalTenderCaseUpdate(ORMModel):
    procurement_case_id: int | None = None
    global_tender_ref_no: str | None = None
    country_of_vendor: str | None = None
    foreign_vendor_name: str | None = None
    foreign_currency: str | None = None
    exchange_rate: Decimal | None = None
    bid_value_foreign_currency: Decimal | None = None
    bid_value_inr: Decimal | None = None
    technical_evaluation_status: str | None = None
    commercial_evaluation_status: str | None = None
    approval_date: date | None = None
    contract_no: str | None = None
    status: str | None = None


class GlobalTenderCaseRead(GlobalTenderCaseCreate, AuditFields):
    pass


class LPCProcurementCaseCreate(ORMModel):
    procurement_case_id: int
    lpc_meeting_no: str | None = None
    lpc_meeting_date: date | None = None
    committee_members: str | None = None
    quotation_count: int | None = Field(default=None, ge=0)
    lowest_vendor_id: int | None = None
    comparative_statement_path: str | None = None
    recommendation_date: date | None = None
    approval_date: date | None = None
    status: str = "In Tendering Process"


class LPCProcurementCaseUpdate(ORMModel):
    procurement_case_id: int | None = None
    lpc_meeting_no: str | None = None
    lpc_meeting_date: date | None = None
    committee_members: str | None = None
    quotation_count: int | None = Field(default=None, ge=0)
    lowest_vendor_id: int | None = None
    comparative_statement_path: str | None = None
    recommendation_date: date | None = None
    approval_date: date | None = None
    status: str | None = None


class LPCProcurementCaseRead(LPCProcurementCaseCreate, AuditFields):
    pass


class OpenMarketCaseCreate(ORMModel):
    procurement_case_id: int
    procurement_justification: str | None = None
    market_survey_date: date | None = None
    quotation_count: int | None = Field(default=None, ge=0)
    selected_vendor_id: int | None = None
    comparative_statement_path: str | None = None
    approval_date: date | None = None
    status: str = "In Tendering Process"


class OpenMarketCaseUpdate(ORMModel):
    procurement_case_id: int | None = None
    procurement_justification: str | None = None
    market_survey_date: date | None = None
    quotation_count: int | None = Field(default=None, ge=0)
    selected_vendor_id: int | None = None
    comparative_statement_path: str | None = None
    approval_date: date | None = None
    status: str | None = None


class OpenMarketCaseRead(OpenMarketCaseCreate, AuditFields):
    pass


class OtherProcurementCaseCreate(ORMModel):
    procurement_case_id: int
    other_procurement_type: str | None = None
    procurement_reference_no: str | None = None
    procurement_description: str | None = None
    vendor_id: int | None = None
    approval_date: date | None = None
    status: str = "In Tendering Process"


class OtherProcurementCaseUpdate(ORMModel):
    procurement_case_id: int | None = None
    other_procurement_type: str | None = None
    procurement_reference_no: str | None = None
    procurement_description: str | None = None
    vendor_id: int | None = None
    approval_date: date | None = None
    status: str | None = None


class OtherProcurementCaseRead(OtherProcurementCaseCreate, AuditFields):
    pass


class SampleItem(ORMModel):
    serial_no: int | None = Field(default=None, ge=1)
    description: str = Field(min_length=1)
    make: str | None = None
    model: str | None = None
    make_model: str | None = None
    quantity: int | None = Field(default=None, ge=0)
    unit: str | None = None


class TenderItem(ORMModel):
    serial_no: int | None = Field(default=None, ge=1)
    description: str = Field(min_length=1)
    item_type: str | None = None
    quantity: int | None = Field(default=None, ge=0)
    unit: str | None = None


class TenderBidCreate(ORMModel):
    serial_no: int | None = Field(default=None, ge=1)
    tender_no: str
    requisition_id: int | None = None
    procurement_case_id: int | None = None
    title: str | None = None
    tender_date: date | None = None
    tender_items: list[TenderItem] = Field(default_factory=list)
    item_name: str | None = None
    quantity: int | None = Field(default=None, ge=0)
    estimated_cost: Decimal | None = None
    budget_head: str | None = None
    bid_published_date: date | None = None
    bid_closed_date: date | None = None
    technical_bid_opening_date: date | None = None
    product_demo_date: date | None = None
    technical_evaluation_date: date | None = None
    technical_qualified_bidders: int | None = Field(default=None, ge=0)
    financial_bid_opening_date: date | None = None
    l1_bidder_name: str | None = None
    contract_number: str | None = None
    contract_date: date | None = None
    amount: Decimal | None = None
    remarks: str | None = None
    publication_date: date | None = None
    closing_date: date | None = None
    bid_type: str = "Open Tender"
    status: ProcurementStatus = ProcurementStatus.in_progress

    @field_validator("tender_items", mode="before")
    @classmethod
    def default_tender_items(cls, value):
        return [] if value is None else value


class TenderBidUpdate(ORMModel):
    serial_no: int | None = Field(default=None, ge=1)
    tender_no: str | None = None
    requisition_id: int | None = None
    procurement_case_id: int | None = None
    title: str | None = None
    tender_date: date | None = None
    tender_items: list[TenderItem] | None = None
    item_name: str | None = None
    quantity: int | None = Field(default=None, ge=0)
    estimated_cost: Decimal | None = None
    budget_head: str | None = None
    bid_published_date: date | None = None
    bid_closed_date: date | None = None
    technical_bid_opening_date: date | None = None
    product_demo_date: date | None = None
    technical_evaluation_date: date | None = None
    technical_qualified_bidders: int | None = Field(default=None, ge=0)
    financial_bid_opening_date: date | None = None
    l1_bidder_name: str | None = None
    contract_number: str | None = None
    contract_date: date | None = None
    amount: Decimal | None = None
    remarks: str | None = None
    publication_date: date | None = None
    closing_date: date | None = None
    bid_type: str | None = None
    status: ProcurementStatus | None = None


class TenderBidRead(TenderBidCreate, AuditFields):
    pass


class EMDCreate(ORMModel):
    tender_id: int | None = None
    procurement_case_id: int | None = None
    serial_no: int | None = Field(default=None, ge=1)
    emd_unique_id: str | None = None
    entry_date: date | None = None
    tender_number: str | None = None
    tender_date: date | None = None
    item_name: str | None = None
    bidder_name: str
    emd_form: str | None = None
    emd_no: str | None = None
    emd_date: date | None = None
    amount: Decimal | None = None
    issuing_branch: str | None = None
    bank_confirmation_date: date | None = None
    expiry_date: date | None = None
    returned_date: date | None = None
    return_details: str | None = None
    remarks: str | None = None
    instrument_no: str | None = None
    received_date: date | None = None
    refund_status: str = "Pending"


class EMDUpdate(ORMModel):
    tender_id: int | None = None
    procurement_case_id: int | None = None
    serial_no: int | None = Field(default=None, ge=1)
    emd_unique_id: str | None = None
    entry_date: date | None = None
    tender_number: str | None = None
    tender_date: date | None = None
    item_name: str | None = None
    bidder_name: str | None = None
    emd_form: str | None = None
    emd_no: str | None = None
    emd_date: date | None = None
    amount: Decimal | None = None
    issuing_branch: str | None = None
    bank_confirmation_date: date | None = None
    expiry_date: date | None = None
    returned_date: date | None = None
    return_details: str | None = None
    remarks: str | None = None
    instrument_no: str | None = None
    received_date: date | None = None
    refund_status: str | None = None


class EMDRead(EMDCreate, AuditFields):
    pass


class SampleRegisterCreate(ORMModel):
    serial_no: int | None = Field(default=None, ge=1)
    tender_id: int | None = None
    procurement_case_id: int | None = None
    tender_number: str | None = None
    tender_date: date | None = None
    sample_items: list[SampleItem] = Field(default_factory=list)
    item_name: str | None = None
    quantity: int | None = Field(default=None, ge=0)
    demonstration_date: date | None = None
    bidder_name: str
    make: str | None = None
    model: str | None = None
    make_model: str | None = None
    receipt_date: date | None = None
    recipient_name: str | None = None
    return_date: date | None = None
    remarks: str | None = None
    sample_description: str | None = None
    received_date: date | None = None
    evaluation_status: ProcurementStatus = ProcurementStatus.submitted

    @field_validator("sample_items", mode="before")
    @classmethod
    def default_sample_items(cls, value):
        return [] if value is None else value


class SampleRegisterUpdate(ORMModel):
    serial_no: int | None = Field(default=None, ge=1)
    tender_id: int | None = None
    procurement_case_id: int | None = None
    tender_number: str | None = None
    tender_date: date | None = None
    sample_items: list[SampleItem] | None = None
    item_name: str | None = None
    quantity: int | None = Field(default=None, ge=0)
    demonstration_date: date | None = None
    bidder_name: str | None = None
    make: str | None = None
    model: str | None = None
    make_model: str | None = None
    receipt_date: date | None = None
    recipient_name: str | None = None
    return_date: date | None = None
    remarks: str | None = None
    sample_description: str | None = None
    received_date: date | None = None
    evaluation_status: ProcurementStatus | None = None


class SampleRegisterRead(SampleRegisterCreate, AuditFields):
    pass


class TechnicalEvaluationCreate(ORMModel):
    tender_id: int | None = None
    procurement_case_id: int | None = None
    bidder_name: str
    evaluator: str
    score: int = Field(ge=0, le=100)
    result: str = "Pending"
    remarks: str | None = None


class TechnicalEvaluationUpdate(ORMModel):
    tender_id: int | None = None
    procurement_case_id: int | None = None
    bidder_name: str | None = None
    evaluator: str | None = None
    score: int | None = Field(default=None, ge=0, le=100)
    result: str | None = None
    remarks: str | None = None


class TechnicalEvaluationRead(TechnicalEvaluationCreate, AuditFields):
    pass


class FinancialEvaluationCreate(ORMModel):
    tender_id: int | None = None
    procurement_case_id: int | None = None
    bidder_name: str
    quoted_amount: Decimal
    negotiated_amount: Decimal | None = None
    rank: int | None = None
    result: str = "Pending"


class FinancialEvaluationUpdate(ORMModel):
    tender_id: int | None = None
    procurement_case_id: int | None = None
    bidder_name: str | None = None
    quoted_amount: Decimal | None = None
    negotiated_amount: Decimal | None = None
    rank: int | None = None
    result: str | None = None


class FinancialEvaluationRead(FinancialEvaluationCreate, AuditFields):
    pass


class PurchaseOrderCreate(ORMModel):
    po_no: str
    tender_id: int | None = None
    procurement_case_id: int | None = None
    procurement_mode: str | None = None
    procurement_mode_other: str | None = Field(default=None, max_length=255)
    tender_reference_no: str | None = None
    tender_reference_date: date | None = None
    estimated_cost: Decimal | None = None
    process_initiate_date: date | None = None
    budget_head: str | None = None
    vendor_name: str
    item_description: str | None = None
    quantity: int | None = Field(default=None, ge=0)
    unit: str | None = None
    po_date: date | None = None
    po_value: Decimal | None = None
    delivery_due_date: date | None = None
    status: ProcurementStatus = ProcurementStatus.approved


class PurchaseOrderUpdate(ORMModel):
    po_no: str | None = None
    tender_id: int | None = None
    procurement_case_id: int | None = None
    procurement_mode: str | None = None
    procurement_mode_other: str | None = Field(default=None, max_length=255)
    tender_reference_no: str | None = None
    tender_reference_date: date | None = None
    estimated_cost: Decimal | None = None
    process_initiate_date: date | None = None
    budget_head: str | None = None
    vendor_name: str | None = None
    item_description: str | None = None
    quantity: int | None = Field(default=None, ge=0)
    unit: str | None = None
    po_date: date | None = None
    po_value: Decimal | None = None
    delivery_due_date: date | None = None
    status: ProcurementStatus | None = None


class PurchaseOrderRead(PurchaseOrderCreate, AuditFields):
    pass


class PerformanceSecurityCreate(ORMModel):
    po_id: int | None = None
    procurement_case_id: int | None = None
    entry_date: date | None = None
    contract_number: str | None = None
    contract_date: date | None = None
    item_name: str | None = None
    quantity: int | None = Field(default=None, ge=0)
    vendor_name: str | None = None
    bidder_name: str | None = None
    security_form: str | None = None
    security_number: str | None = None
    security_date: date | None = None
    amount: Decimal | None = None
    issuing_branch: str | None = None
    bank_confirmation_date: date | None = None
    expiry_date: date | None = None
    warranty_period: str | None = None
    return_date: date | None = None
    return_details: str | None = None
    remarks: str | None = None
    instrument_no: str | None = None
    valid_until: date | None = None
    release_status: str = "Held"


class PerformanceSecurityUpdate(ORMModel):
    po_id: int | None = None
    procurement_case_id: int | None = None
    entry_date: date | None = None
    contract_number: str | None = None
    contract_date: date | None = None
    item_name: str | None = None
    quantity: int | None = Field(default=None, ge=0)
    vendor_name: str | None = None
    bidder_name: str | None = None
    security_form: str | None = None
    security_number: str | None = None
    security_date: date | None = None
    amount: Decimal | None = None
    issuing_branch: str | None = None
    bank_confirmation_date: date | None = None
    expiry_date: date | None = None
    warranty_period: str | None = None
    return_date: date | None = None
    return_details: str | None = None
    remarks: str | None = None
    instrument_no: str | None = None
    valid_until: date | None = None
    release_status: str | None = None


class PerformanceSecurityRead(PerformanceSecurityCreate, AuditFields):
    pass


class DeliveryAcceptanceCreate(ORMModel):
    po_id: int | None = None
    procurement_case_id: int | None = None
    delivery_date: date | None = None
    accepted_quantity: int = Field(ge=0)
    acceptance_status: ProcurementStatus = ProcurementStatus.submitted
    remarks: str | None = None


class DeliveryAcceptanceUpdate(ORMModel):
    po_id: int | None = None
    procurement_case_id: int | None = None
    delivery_date: date | None = None
    accepted_quantity: int | None = Field(default=None, ge=0)
    acceptance_status: ProcurementStatus | None = None
    remarks: str | None = None


class DeliveryAcceptanceRead(DeliveryAcceptanceCreate, AuditFields):
    pass


class PaymentTrackingCreate(ORMModel):
    po_id: int | None = None
    procurement_case_id: int | None = None
    invoice_no: str
    invoice_date: date | None = None
    invoice_amount: Decimal
    paid_amount: Decimal = Decimal("0.00")
    payment_status: str = "Pending"


class PaymentTrackingUpdate(ORMModel):
    po_id: int | None = None
    procurement_case_id: int | None = None
    invoice_no: str | None = None
    invoice_date: date | None = None
    invoice_amount: Decimal | None = None
    paid_amount: Decimal | None = None
    payment_status: str | None = None


class PaymentTrackingRead(PaymentTrackingCreate, AuditFields):
    pass
