from datetime import datetime
from enum import Enum
from typing import List, Optional, Dict
from fastapi.param_functions import Query
from sqlalchemy.orm import Session
from pydantic import Field, BaseModel, field_validator, model_validator, ConfigDict
from src.apps.payment_requests.schemas.payment_request_product import (
    PaymentRequestProductCreate,
)
from src.apps.base.utils.functions import format_amount
from src.apps.customers.schemas.customer_requests import CustomerAccountCreateRequestSchema
from src.apps.base.schemas.common import AddressCreateRequestSchema
from src.apps.transactions.schemas.transaction_requests import TransactionCreateRequestSchema
from src.core.utils.enums import (
    PaymentMethodTypes,
    PlanType,
)
from src.apps.payment_requests.enums import (
    PaymentAuthorizationTypes,
    PaymentFrequencies,
    PaymentCurrencies,
    PaymentRequestStatusTypes,
    SplitPaymentTypes,
    TaxType,
    SurchargeTypes,
)
from src.apps.base.utils import regexp
from .payment_method import (
    PaymentMethodACHCreate,
    PaymentMethodCardCreate,
    PaymentSplitTenderSchema,
    PartialPaymentRequestConfigCreateSchema,
    PartialPaymentRequestConfigUpdateSchema,
)
from .recurring_payment_request import (
    RecurringPaymentRequestCreate,
    RecurringPaymentRequestSchema,
    RecurringPaymentRequestUpdate,
)
from .split_payment_request import SplitPaymentRequestCreate, SplitPaymentRequestUpdate
from .payment_request_common import (
    PaymentRequestLineItemCreate,
    PaymentRequestLineItemUpdate,
    PaymentRequestBase,
)
from src.apps.invoices.schemas.invoice_adjustment import InvoiceAdjustmentsCreate
from .payment_method import PaymentRequestMethodSchema
from src.apps.payment_requests.schemas.payment_request_adjustment import (
    PaymentRequestAdjustmentsCreate,
    PaymentRequestAdjustmentsUpdate,
)
from src.apps.payment_requests.schemas.payment_request_authorization import (
    PaymentRequestAuthorizationsCreate,
)


class PaymentRequestCreateSchema(PaymentRequestBase):
    model_config = ConfigDict(from_attributes=True)

    amount: float = Field(description="Amount of PaymentRequest, accepts decimal input")
    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: PaymentFrequencies = Field(
        description="Frequency of payment, (Split / Recurring / One-time)"
    )
    authorization_type: PaymentAuthorizationTypes = Field(
        description="PaymentRequest authorisation type, (Pre-Authorized / Request-Authorization)"
    )
    payment_method: Optional[PaymentMethodTypes] = Field(
        default=None,
        description="Preferred payment method, (Cards/ACH/etc)"
    )
    split_config: Optional[List[SplitPaymentRequestCreate]] = Field(
        default=None,
        description="Configuration for split payment request"
    )
    recurring_config: Optional[RecurringPaymentRequestCreate] = Field(
        default=None,
        description="Configuration for recurring payment request"
    )
    card_details: Optional[PaymentMethodCardCreate] = Field(
        default=None,
        description="Card details, Card Details and ACH details are mutually exlusive"
    )
    ach_details: Optional[PaymentMethodACHCreate] = Field(
        default=None,
        description="ACH details, Card Details and ACH details are mutually exlusive"
    )
    attachments: Optional[List[int]] = Field(
        default=None,
        description="Uploaded file ids of optional attachments"
    )
    currency: PaymentCurrencies = Field(
        description="Three-letter ISO currency code, in lowercase."
    )
    payment_method_token: Optional[str] = Field(
        default=None,
        description="Payment method token obtained after saving method with third party"
    )
    transaction_details: Optional[TransactionCreateRequestSchema] = Field(
        default=None,
        description="Transaction details of this payment request when the payment has been made."
    )
    due_date: Optional[datetime] = Field(
        default=None,
        description="Due date and time of payment as unix timestamp"
    )
    status: Optional[PaymentRequestStatusTypes] = Field(
        default=None,
        description="Status of payment request"
    )


class PaymentRequestUpdateSchema(PaymentRequestBase):
    model_config = ConfigDict(from_attributes=True)

    status: Optional[PaymentRequestStatusTypes] = Field(
        default=None,
        description="Status of payment request"
    )


class MerchantPaymentRequestCreateSchema(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?"
    )
    amount: float = Field(
        description="Amount of Payment Request, accepts decimal input"
    )
    currency: PaymentCurrencies = Field(
        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",
    )
    payer_id: str = Field(
        pattern=regexp.CONTACT_UNIQUE_ID,
        description="ID of payer for whom this payment request is to be created",
    )
    payment_frequency: PaymentFrequencies = Field(
        description="Frequency of payment, (Split / Recurring / One-time)"
    )
    authorization_type: Optional[PaymentAuthorizationTypes] = Field(
        default=None,
        description="PaymentRequest authorisation type, (Pre-Authorized / Request-Authorization)"
    )
    payment_redirect_url: str = Field(
        default=None, description="Redirection url of the payment"
    )

    payment_split_tenders: Optional[List[PaymentSplitTenderSchema]] = Field(
        default=None,
        description="Split Tender details of the single type Payment"
    )
    partial_payment: Optional[PartialPaymentRequestConfigCreateSchema] = 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[PaymentRequestAdjustmentsCreate] = Field(
        default=None,
        description="Payment Adjustment Details which consist of surcharge/Discounts"
    )
    payment_split_frequency: Optional[str] = Field(
        default=None,
        description="Frequency of payment split, (Weekly / Monthly / Quarterly / Yearly)"
    )
    due_date: Optional[datetime] = Field(
        default=None,
        description="Due date and time of payment as unix timestamp"
    )
    shipping_address_id: Optional[int] = Field(
        default=None,
        description="Shipping Address id for the payment"
    )
    billing_date: Optional[datetime] = Field(
        default=None,
        description="Billing 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"
    )
    billed_products: Optional[List[PaymentRequestProductCreate]] = Field(
        default=None,
        description="Billed products in this payment request in the payment request"
    )
    split_config: Optional[List[SplitPaymentRequestCreate]] = Field(
        default=None,
        description="Configuration for split payment request"
    )
    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."
    )
    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 that will be attached to the payment method"
    )
    approver_id: Optional[str] = Field(
        default=None,
        pattern=regexp.CONTACT_UNIQUE_ID,
        description="ID of approver 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",
    )
    enable_email_receipt: Optional[bool] = Field(
        default=None,
        description="Enable Email notifications for this payment request?"
    )
    enable_sms_receipt: Optional[bool] = Field(
        default=None,
        description="Enable Email notifications for this payment request?"
    )
    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"
    )
    invoice_terms: Optional[str] = Field(
        default=None,
        description="Terms and conditions for the invoice"
    )

    @field_validator("line_items")
    @classmethod
    def validate_line_items(cls, v):
        return v


    @model_validator(mode='before')
    @classmethod
    def validate_payment_config(cls, values):
        payment_frequency = values.get("payment_frequency", None)
        if payment_frequency == PaymentFrequencies.ONE_TIME:
            if not values.get("due_date", False):
                raise ValueError("due_date is required")
        elif payment_frequency == PaymentFrequencies.SPLIT:
            if not values.get("split_config", False):
                raise ValueError("split_config is required")
        elif payment_frequency == PaymentFrequencies.RECURRING:
            if not values.get("recurring_config", False):
                raise ValueError("recurring_config is required")
        return values

    @model_validator(mode='before')
    @classmethod
    def validate_authorization_type(cls, values):
        is_draft = values.get("is_draft", False)
        if not is_draft and values.get("authorization_type", None) is None:
            raise ValueError("Please provide an Authorization Type")
        return values

    @model_validator(mode='before')
    @classmethod
    def validate_notifications(cls, values):
        is_draft = values.get("is_draft", False)
        auth_mode = values.get("authorization_type")
        if not is_draft:
            if auth_mode == PaymentAuthorizationTypes.REQUEST_AUTH:
                if not values.get("enable_email", False):
                    raise ValueError(
                        "Please enable email notifications for request authorization payments"
                    )
        return values

    @model_validator(mode='before')
    @classmethod
    def validate_transaction_amount(cls, values):
        # ...existing transaction amount validation logic...
        return values

    @model_validator(mode='before')
    @classmethod
    def validate_products_amount(cls, values):
        # ...existing products amount validation logic...
        return values

    @model_validator(mode='before')
    @classmethod
    def validate_tax_type(cls, val):
        line_item = val["line_items"]
        if len(line_item) > 0:
            return val
        else:
            val["tax_type"] = TaxType.NOTAX
            return val


class PaymentRequestListFilterSchema(BaseModel):
    search: Optional[str] = Query(
        default=None, description="Search by customer's name, email, phone, etc"
    )
    name: Optional[str] = Query(default=None, description="Search by customer's name")
    email: Optional[str] = Query(default=None, description="Search by customer's email")
    phone: Optional[str] = Query(default=None, description="Search by customer's phone")
    message: Optional[str] = Query(
        default=None, description="Search by payment request message"
    )
    currency: Optional[str] = Query(
        default=None,
        description="Search by comma separated list of currencies. Eg.: ?currency=usd,yuan",
    )
    payment_request_id: Optional[str] = Query(
        default=None, description="Search by the unique payment request id"
    )
    payment_request_literal: Optional[str] = Query(
        default=None, description="Search by the unique payment request literal"
    )
    payment_frequency: Optional[str] = Query(
        default=None,
        description="Search by comma separated list of frequencies. Eg.: ?payment_frequeny=split,one_time",
    )
    authorization_type: Optional[str] = Query(
        default=None,
        description="Search by comma separated list of authorization type. Eg.: ?authorization_type=request_auth,pre_auth",
    )
    status: Optional[str] = Query(
        default=None,
        description="Search by comma separated list of status. Eg.: ?status=200,501",
    )
    customer_id: Optional[str] = Query(
        default=None, description="Customer id of the customer"
    )
    next_due_range: Optional[str] = Query(default=None)
    amount_range: Optional[str] = Query(default=None)


class PaymentRequestPlansListFilterSchema(BaseModel):
    plan_type: Optional[PlanType] = Query(
        default=None,
        description="Available types are: split and recurring. Eg.: ?plan_type=split",
    )
    search: Optional[str] = Query(
        default=None, description="Search by customer's name, email, phone, etc"
    )
    name: Optional[str] = Query(default=None, description="Search by customer's name")
    email: Optional[str] = Query(default=None, description="Search by customer's email")
    phone: Optional[str] = Query(default=None, description="Search by customer's phone")
    amount_from: float = Query(
        default=None, description="Put the Minimum amount of your need to search"
    )
    amount_to: float = Query(
        default=None, description="Put Maximum amount of your need to search between"
    )
    message: Optional[str] = Query(
        default=None, description="Search by payment request message"
    )
    currency: Optional[str] = Query(
        default=None,
        description="Search by comma separated list of currencies. Eg.: ?currency=usd,yuan",
    )
    payment_request_id: Optional[str] = Query(
        default=None, description="Search by the unique payment request id"
    )
    payment_request_literal: Optional[str] = Query(
        default=None, description="Search by the unique payment request literal"
    )
    authorization_type: Optional[PaymentAuthorizationTypes] = Query(
        default=None, description="Search by authorisation type"
    )
    status: Optional[str] = Query(
        default=None,
        description="Search by comma separated list of status. Eg.: ?status=200,501",
    )


class AuthorizeHPPSchema(BaseModel):
    amount: Optional[float] = Field(
        description="Amount to be paid by payer", default=None
    )
    is_hpp: Optional[bool] = Field(description="", default=False)
    tip: Optional[float] = Field(description="Tip given by the customer", default=None)
    payment_methods: Optional[List[PaymentRequestMethodSchema]] = Field(
        description="Payment Methods Requests"
    )
    skip_save: bool = Field(
        default=False,
        description="Skip saving this payment method? Applicable only for one time payments",
    )
    card_details: Optional[PaymentMethodCardCreate] = Field(
        description="Card details, Card Details and ACH details are mutually exlusive"
    )
    ach_details: Optional[PaymentMethodACHCreate] = Field(
        description="ACH details, Card Details and ACH details are mutually exlusive"
    )
    save_payment_method: Optional[bool] = Field(
        default=False,
        description="Indicates if the chosen payment method needs to be saved",
    )
    notes: Optional[str] = Field(
        description="Additional notes for this payment authorization"
    )
    billing_address: Optional[AddressCreateRequestSchema] = Field(
        description="Billing address for the payment method"
    )
    account_details: Optional[CustomerAccountCreateRequestSchema] = Field(
        description="Customer's registration details"
    )
    partial_payment: Optional[PartialPaymentRequestConfigUpdateSchema] = Field(
        description="Partial payment details of the one time payment"
    )
    auth_metadata: Optional[Dict] = Field(description="Auth metadata")
    payment_request_authoriztions: Optional[
        List[PaymentRequestAuthorizationsCreate]
    ] = Field(description="Authorization list of the payment request")
    user_id: Optional[str] = Field(description="User ID of the user who is making payment")


class AuthorizeScheduleHPPSchema(BaseModel):
    amount: Optional[float] = Field(
        description="Amount to be paid by payer", default=None
    )
    payment_methods: Optional[List[PaymentRequestMethodSchema]] = Field(
        description="Payment Methods Requests"
    )
    skip_save: bool = Field(
        default=False,
        description="Skip saving this payment method? Applicable only for one time payments",
    )
    save_payment_method: Optional[bool] = Field(
        default=False,
        description="Indicates if the chosen payment method needs to be saved",
    )
    billing_address: Optional[AddressCreateRequestSchema] = Field(
        description="Billing address for the payment method"
    )
    payment_request_authoriztions: Optional[
        List[PaymentRequestAuthorizationsCreate]
    ] = Field(description="Authorization list of the payment request")


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


class PaymentRequestBulkActionSchema(BaseModel):
    action: PaymentRequestBulkActions = Field(
        description="Type of action which needs to be performed."
    )
    ids: List[str] = Field(
        description="An array of payment_request_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 payment request")
        if len(v) > max_limit:
            raise ValueError(
                f"You can provide atmost of {max_limit} payment requests at once"
            )
        for item in v:
            if not regexp.test(item, regexp.PAYMENT_REQUEST_UNIQUE_ID):
                raise ValueError(f"'{item}' is not a valid payment_request_id")
        return v


class MerchantPaymentRequestUpdateSchema(PaymentRequestBase):
    is_draft: bool = Field(
        default=False, description="Save this payment request as draft?"
    )
    amount: float = Field(description="Amount of invoice")
    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",
    )
    currency: Optional[PaymentCurrencies] = Field(
        description="Three-letter ISO currency code, in lowercase. Eg: usd, eur, etc."
    )
    due_date: Optional[datetime] = Field(
        description="Due date and time of payment as unix timestamp"
    )
    billing_date: Optional[datetime] = Field(
        description="Billing date and time of payment as unix timestamp"
    )
    partial_payment: Optional[PartialPaymentRequestConfigUpdateSchema] = Field(
        description="Partial payment details of the one time payment"
    )
    payment_methods: Optional[List[PaymentRequestMethodSchema]] = Field(
        description="Payment Methods Requests"
    )
    payment_adjustments: Optional[PaymentRequestAdjustmentsUpdate] = Field(
        description="Payment Adjustment Details which consist of surcharge/Discounts"
    )
    line_items: Optional[List[PaymentRequestLineItemUpdate]] = Field(
        description="Line items in this payment request in the payment request"
    )
    split_config: Optional[List[SplitPaymentRequestUpdate]] = Field(
        description="Configuration for split payment request"
    )
    recurring_config: Optional[RecurringPaymentRequestUpdate] = Field(
        description="Configuration for recurring payment request"
    )
    attachments: Optional[List[int]] = Field(
        description="Uploaded file ids of optional attachments"
    )
    shipping_fee: Optional[float] = Field(
        ge=0, le=99999, description="Shipping fees for the billable items."
    )
    enable_email: Optional[bool] = Field(
        description="Enable Email notifications for this payment request?"
    )
    enable_sms: Optional[bool] = Field(
        description="Enable SMS notifications for this payment request?"
    )
    shipping_fee: Optional[float] = Field(
        ge=0, le=99999, description="Shipping fees for the billable items."
    )
    billing_address: Optional[AddressCreateRequestSchema] = Field(
        description="Billing address for the payment method"
    )
    approver_id: Optional[str] = Field(
        description="ID of approver for whom this payment request is to be created",
    )
    payer_id: Optional[str] = Field(
        description="ID of payer for whom this payment request is to be created",
    )
    payer_email_request_enabled: Optional[bool] = Field(
        description="Receive request email for the payment for payer",
    )
    payer_email_receipt_enabled: Optional[bool] = Field(
        description="Receive receipt email for the payment for payer",
    )
    payer_sms_request_enabled: Optional[bool] = Field(
        description="Receive request sms for the payment for payer",
    )
    payer_sms_receipt_enabled: Optional[bool] = Field(
        description="Receive receipt sms for the payment for payer",
    )
    approver_email_receipt_enabled: Optional[bool] = Field(
        description="Receive receipt sms for the payment for approver",
    )
    approver_sms_receipt_enabled: Optional[bool] = Field(
        description="Receive receipt sms for the payment for approver",
    )
    enable_email_receipt: Optional[bool] = Field(
        description="Enable Email notifications for this payment request?"
    )
    enable_sms_receipt: Optional[bool] = Field(
        description="Enable Email notifications for this payment request?"
    )
    invoice_terms: Optional[str] = Field(
        description="Terms and conditions for the invoice"
    )

    @model_validator(mode='before')
    @classmethod
    def validate_payment_config(cls, values):
        payment_frequency = values.get("payment_frequency", None)
        if payment_frequency == PaymentFrequencies.ONE_TIME:
            if not values.get("due_date", False):
                raise ValueError("due_date is required")
        elif payment_frequency == PaymentFrequencies.SPLIT:
            if not values.get("split_config", False):
                raise ValueError("split_config is required")
        elif payment_frequency == PaymentFrequencies.RECURRING:
            if not values.get("recurring_config", False):
                raise ValueError("recurring_config is required")
        return values

    @model_validator(mode='before')
    @classmethod
    def validate_authorization_type(cls, values):
        is_draft = values.get("is_draft", False)
        if not is_draft and values.get("authorization_type", None) is None:
            raise ValueError("Please provide an Authorization Type")
        return values


class PaymentRequestScheduleSchema(BaseModel):
    sequence_id: int = Field(description="Payment Request Sequence ID")
    invoice_id: Optional[str] = Field(description="Invoice ID")


class PaymentRequestScheduleUpdateSchema(PaymentRequestScheduleSchema):
    sequence_id: Optional[int] = Field(description="Payment Request Sequence ID")
    invoice_id: Optional[str] = Field(description="Invoice ID")
    amount: Optional[float] = Field(description="Amount")
    line_items: Optional[List[PaymentRequestLineItemUpdate]] = Field(
        description="Any transactional items included in the payment request"
    )
    shipping_fee: Optional[float] = Field(description="Shipping fee")
    invoice_update_type: Optional[str] = Field(description="Current Invoice")
    payer_id: Optional[str] = Field(
        description="ID of payer for whom this payment request is to be created",
    )
    payer_email_request_enabled: Optional[bool] = Field(
        description="Receive request email for the payment for payer",
    )
    payer_sms_request_enabled: Optional[bool] = Field(
        description="Receive request sms for the payment for payer",
    )
    type: Optional[str] = Field(description="Payment Request Type")
    payment_adjustments: Optional[InvoiceAdjustmentsCreate] = Field(
        description="Payment Adjustment Details which consist of surcharge/Discounts"
    )
    tax_type: Optional[TaxType] = Field(description="Tax Type")
