from datetime import datetime
from typing import Optional, List
from enum import Enum
from fastapi import Query
from pydantic import BaseModel, Field, field_validator, model_validator, ConfigDict
from src.apps.base.schemas.common import BaseSchema
from .invoice_common import InvoiceSchema
from src.apps.base.utils.functions import format_amount, round_half_up
from src.apps.base.schemas.common import AddressCreateRequestSchema
from src.core.utils.enums import (
    InvoiceStatusTypes,
)
from src.apps.payment_requests.enums import (
    PaymentAuthorizationTypes,
    PaymentFrequencies,
    PaymentCurrencies,
    SplitPaymentTypes,
    SurchargeTypes,
)
from src.apps.base.utils import regexp
from src.apps.payment_requests.schemas.payment_method import (
    PartialPaymentRequestConfigCreateSchema,
    PartialPaymentRequestConfigUpdateSchema,
)
from src.apps.payment_requests.schemas.recurring_payment_request import (
    RecurringPaymentRequestCreate,
    RecurringPaymentRequestUpdate,
    RecurringPaymentRequestSchema,
)
from src.apps.payment_requests.schemas.split_payment_request import (
    SplitPaymentRequestCreate,
    SplitPaymentRequestUpdate,
)
from apps.payment_requests.schemas.payment_request_common import (
    PaymentRequestLineItemCreate,
    PaymentRequestLineItemUpdate,
    PaymentRequestBase,
)
from src.apps.payment_requests.schemas.payment_method import PaymentRequestMethodSchema
from src.core.database import SessionCelery
from src.core.utils.constants import MAX_UPLOAD_LIMIT
from src.apps.payment_requests.schemas.payment_request_adjustment import (
    PaymentRequestAdjustmentsCreate,
    PaymentRequestAdjustmentsUpdate,
)


class InvoiceCreateRequestSchema(BaseSchema):
    model_config = ConfigDict(from_attributes=True)
    
    amount: float = Field(description="Invoice amount value")
    invoice_literal: Optional[str] = Field(default=None, description="Invoice # value")
    reference: Optional[str] = Field(default=None, description="Invoice reference")
    due_date: Optional[datetime] = Field(
        default=None,
        description="Due date and time as unix timestamp"
    )
    billing_date: Optional[datetime] = Field(
        default=None,
        description="Billing date and time as unix timestamp"
    )
    next_due_date: Optional[datetime] = Field(
        default=None,
        description="Next due date and time as unix timestamp, only applicable for recurring payments"
    )
    tax_fee: Optional[int] = Field(default=0.0, description="Tax fees, if applicable")
    shipping_fee: Optional[int] = Field(
        default=0.0, description="Shipping fees, if applicable"
    )
    additional_fee: Optional[int] = Field(
        default=0.0, description="Additional fees, if applicable"
    )
    tip: Optional[int] = Field(default=0.0, description="Tax fees, if applicable")
    charge: Optional[int] = Field(default=0.0, description="Tax fees, if applicable")
    status: Optional[InvoiceStatusTypes] = Field(default=None, description="Invoice status")
    comments: Optional[str] = Field(default=None, description="Invoice comments")
    is_surcharge_enabled: Optional[bool] = Field(
        default=None,
        description="Surcharge enabled for cost adjustment"
    )
    surcharge_type: Optional[SurchargeTypes] = Field(
        default=None,
        description="Surcharge type inclusive/exclusive"
    )
    sequence_id: Optional[int] = Field(default=None, description="Invoice sequence id")


class InvoiceUpdateRequestSchema(InvoiceSchema):
    model_config = ConfigDict(from_attributes=True)


class InvoiceListFilterSchema(BaseModel):
    search: Optional[str] = Query(
        default=None, description="Search by amount, customer, etc"
    )
    status: Optional[str] = Query(
        default=None,
        description="Comma separated Status of invoice. Eg: ?status=301,400",
    )
    customer_name: Optional[str] = Query(default=None)
    invoice_literal: Optional[str] = Query(default=None)
    invoice_id: Optional[str] = Query(default=None)
    authorization_type: Optional[str] = Query(default=None)
    payment_frequency: Optional[str] = Query(default=None)
    category: Optional[str] = Query(default=None)
    next_due_range: Optional[str] = Query(default=None)
    amount_range: Optional[str] = Query(default=None)


class MerchantInvoiceCreateSchema(PaymentRequestBase):
    model_config = ConfigDict(
        from_attributes=True,
        json_schema_extra={
            "example": {
                "customer_id": "cus_asbbeqwb123123",
                "payment_frequency": "split",
                "authorization_type": "request_auth",
                "payment_method": "card",
                "save_payment_method": True,
                "payment_method_token": "pm_asbbeqwb123123",
                "amount": 100.00,
                "currency": "usd",
                "line_items": [
                    {
                        "title": "Horlicks",
                        "description": "Taste for good",
                        "unit_price": 100,
                        "quantity": 1,
                    }
                ],
                "split_config": [
                    {
                        "sequence": 1,
                        "split_type": "percentage",
                        "split_value": 50,
                        "billing_date": "2021-10-18T12:33:28.146250+00:00",
                        "due_date": "2021-10-18T12:33:28.146250+00:00",
                    },
                    {
                        "sequence": 2,
                        "split_type": "percentage",
                        "split_value": 50,
                        "billing_date": "2021-10-18T12:33:28.146250+00:00",
                        "due_date": "2021-10-18T12:33:28.146250+00:00",
                    },
                ],
                "recurring_config": {
                    "prorate_first_payment": False,
                    "start_date": "2022-01-19T12:33:28.146250+00:00",
                    "repeat_type": "on_date",
                    "interval": "month",
                    "interval_value": 12,
                    "end_type": "date",
                    "end_date": "2023-01-11T12:33:28.146250+00:00",
                },
            }
        }
    )
    
    is_draft: bool = Field(
        default=False, description="Save this payment request as draft?"
    )
    currency: Optional[PaymentCurrencies] = Field(
        default=None,
        description="Three-letter ISO currency code, in lowercase. Eg: usd, eur, etc."
    )
    customer_id: str = Field(
        pattern=regexp.CUSTOMER_UNIQUE_ID,
        description="Unique ID (cus_xxx) of customer for whom this payment request is to be created",
    )
    payment_frequency: Optional[PaymentFrequencies] = Field(
        default=None,
        description="Frequency of payment, (Split / Recurring / One-time)"
    )
    authorization_type: Optional[PaymentAuthorizationTypes] = Field(
        default=None,
        description="PaymentRequest authorisation type, (Pre-Authorized / Request-Authorization)"
    )
    shipping_address_id: Optional[int] = Field(
        default=None,
        description="Shipping Address id for the payment"
    )
    invoice_literal: Optional[str] = Field(default=None, description="Invoice # value")
    reference: Optional[str] = Field(default=None, description="Invoice reference")
    partial_payment: Optional[PartialPaymentRequestConfigCreateSchema] = Field(
        default=None,
        description="Partial payment details of the one time payment"
    )
    payment_split_frequency: Optional[str] = Field(
        default=None,
        description="Frequency of payment split, (Weekly / Monthly / Quarterly / Yearly)"
    )
    payment_methods: Optional[List[PaymentRequestMethodSchema]] = Field(
        default=None,
        description="Payment Methods Requests"
    )
    payment_adjustments: Optional[PaymentRequestAdjustmentsCreate] = Field(
        default=None,
        description="Payment Adjustment Details which consist of surcharge/Discounts"
    )
    billing_date: Optional[datetime] = Field(
        default=None,
        description="Issue date and time of payment as unix timestamp"
    )
    due_date: Optional[datetime] = Field(
        default=None,
        description="Due date and time of payment as unix timestamp"
    )
    line_items: Optional[List[PaymentRequestLineItemCreate]] = Field(
        default=None,
        description="Any transactional items included in the payment request"
    )
    split_config: Optional[List[SplitPaymentRequestCreate]] = Field(
        default=None,
        description="Configuration for split payment request"
    )
    payment_redirect_url: str = Field(
        default=None, description="Redirection url of the payment"
    )
    recurring_config: Optional[RecurringPaymentRequestCreate] = Field(
        default=None,
        description="Configuration for recurring payment request"
    )
    attachments: Optional[List[int]] = Field(
        default=None,
        description="Uploaded file ids of optional attachments"
    )
    shipping_fee: Optional[float] = Field(
        default=None, ge=0, le=99999, description="Shipping fees for the billable items."
    )
    billing_address: Optional[AddressCreateRequestSchema] = Field(
        default=None,
        description="Billing address that will be attached to the payment method"
    )
    approver_id: Optional[str] = Field(
        default=None,
        description="ID of approver for whom this payment request is to be created",
    )
    payer_id: Optional[str] = Field(
        default=None,
        description="ID of payer for whom this payment request is to be created",
    )
    payer_email_request_enabled: Optional[bool] = Field(
        default=None,
        description="Receive request email for the payment for payer",
    )
    payer_email_receipt_enabled: Optional[bool] = Field(
        default=None,
        description="Receive receipt email for the payment for payer",
    )
    payer_sms_request_enabled: Optional[bool] = Field(
        default=None,
        description="Receive request sms for the payment for payer",
    )
    payer_sms_receipt_enabled: Optional[bool] = Field(
        default=None,
        description="Receive receipt sms for the payment for payer",
    )
    approver_email_receipt_enabled: Optional[bool] = Field(
        default=None,
        description="Receive receipt sms for the payment for approver",
    )
    approver_sms_receipt_enabled: Optional[bool] = Field(
        default=None,
        description="Receive receipt sms for the payment for approver",
    )
    is_surcharge_enabled: Optional[bool] = Field(
        default=None,
        description="Surcharge enabled for cost adjustment"
    )
    surcharge_type: Optional[SurchargeTypes] = Field(
        default=None,
        description="Surcharge type inclusive/exclusive"
    )


class MerchantInvoiceUpdateSchema(PaymentRequestBase):
    model_config = ConfigDict(from_attributes=True)
    
    is_draft: bool = Field(default=False, description="Save this invoice as draft?")
    amount: float = Field(description="Amount of invoice")
    invoice_literal: Optional[str] = Field(default=None, description="Invoice # value")
    reference: Optional[str] = Field(default=None, description="Invoice reference")
    customer_id: str = Field(
        pattern=regexp.CUSTOMER_UNIQUE_ID,
        description="Unique ID (cus_xxx) of customer for whom this invoice is to be created",
    )
    currency: Optional[PaymentCurrencies] = Field(
        default=None,
        description="Three-letter ISO currency code, in lowercase. Eg: usd, eur, etc."
    )
    billing_date: Optional[datetime] = Field(
        default=None,
        description="Issue date and time of payment as unix timestamp"
    )
    due_date: Optional[datetime] = Field(
        default=None,
        description="Due date and time of payment as unix timestamp"
    )
    partial_payment: Optional[PartialPaymentRequestConfigUpdateSchema] = Field(
        default=None,
        description="Partial payment details of the one time payment"
    )
    payment_methods: Optional[List[PaymentRequestMethodSchema]] = Field(
        default=None,
        description="Payment Methods Requests"
    )
    payment_adjustments: Optional[PaymentRequestAdjustmentsUpdate] = Field(
        default=None,
        description="Payment Adjustment Details which consist of surcharge/Discounts"
    )
    payment_redirect_url: str = Field(
        default=None, description="Redirection url of the payment"
    )
    line_items: Optional[List[PaymentRequestLineItemUpdate]] = Field(
        default=None,
        description="Line items in this payment request in the payment request"
    )
    split_config: Optional[List[SplitPaymentRequestUpdate]] = Field(
        default=None,
        description="Configuration for split payment request"
    )
    recurring_config: Optional[RecurringPaymentRequestUpdate] = Field(
        default=None,
        description="Configuration for recurring payment request"
    )
    attachments: Optional[List[int]] = Field(
        default=None,
        description="Uploaded file ids of optional attachments"
    )
    shipping_address_id: Optional[int] = Field(
        default=None,
        description="Shipping Address id for the payment"
    )
    shipping_fee: Optional[float] = Field(
        default=None, ge=0, le=99999, description="Shipping fees for the billable items."
    )
    enable_email: Optional[bool] = Field(
        default=None,
        description="Enable Email notifications for this payment request?"
    )
    enable_sms: Optional[bool] = Field(
        default=None,
        description="Enable SMS notifications for this payment request?"
    )
    billing_address: Optional[AddressCreateRequestSchema] = Field(
        default=None,
        description="Billing address for the payment method"
    )
    approver_id: Optional[str] = Field(
        default=None,
        description="ID of approver for whom this payment request is to be created",
    )
    payer_id: Optional[str] = Field(
        default=None,
        description="ID of payer for whom this payment request is to be created",
    )
    payer_email_request_enabled: Optional[bool] = Field(
        default=None,
        description="Receive request email for the payment for payer",
    )
    payer_email_receipt_enabled: Optional[bool] = Field(
        default=None,
        description="Receive receipt email for the payment for payer",
    )
    payer_sms_request_enabled: Optional[bool] = Field(
        default=None,
        description="Receive request sms for the payment for payer",
    )
    payer_sms_receipt_enabled: Optional[bool] = Field(
        default=None,
        description="Receive receipt sms for the payment for payer",
    )
    approver_email_receipt_enabled: Optional[bool] = Field(
        default=None,
        description="Receive receipt sms for the payment for approver",
    )
    approver_sms_receipt_enabled: Optional[bool] = Field(
        default=None,
        description="Receive receipt sms for the payment for approver",
    )
    status: Optional[int] = Field(default=None, description="Status of the invoice")
    is_surcharge_enabled: Optional[bool] = Field(
        default=None,
        description="Surcharge enabled for cost adjustment"
    )
    surcharge_type: Optional[SurchargeTypes] = Field(
        default=None,
        description="Surcharge type inclusive/exclusive"
    )


class InvoiceBulkActions(str, Enum):
    remove = "remove"
    cancel = "cancel"
    resend = "resend"


class InvoiceBulkActionSchema(BaseModel):
    action: InvoiceBulkActions = Field(
        description="Type of action which needs to be performed."
    )
    ids: List[str] = Field(
        description="An array of invoice_id of the items that you want to perform actions for"
    )

    @field_validator("ids")
    @classmethod
    def validate_ids(cls, v):
        max_limit = 10
        if v is None or len(v) == 0:
            raise ValueError(f"Please provide atleast one invoice id")
        if len(v) > max_limit:
            raise ValueError(
                f"You can provide atmost of {max_limit} invoice ids at once"
            )
        for item in v:
            if not regexp.test(item, regexp.INVOICE_UNIQUE_ID):
                raise ValueError(f"'{item}' is not a valid invoice_id")
        return v


class InvoiceActionSchema(BaseModel):
    is_approved: bool = Field(description="Signer has approved or not", default=False)


class PlanBulkActions(str, Enum):
    delete = "delete"
    show_onboarding = "show_onboarding"


class PlanBulkActionSchema(BaseModel):
    action: PlanBulkActions = Field(
        description="Type of action which needs to be performed."
    )
    ids: List[str] = Field(
        description="An array of plan_id of the items that you want to perform actions for"
    )

    @field_validator("ids")
    @classmethod
    def validate_ids(cls, v):
        max_limit = 10
        if v is None or len(v) == 0:
            raise ValueError(f"Please provide atleast one plan id")
        if len(v) > max_limit:
            raise ValueError(f"You can provide atmost of {max_limit} plan ids at once")
        for item in v:
            if not regexp.test(item, regexp.PLAN_UNIQUE_ID):
                raise ValueError(f"'{item}' is not a valid plan_id")
        return v
