"""
Quote models for MSP Quote Wizard.

Models for managing quotes, quote items, activity tracking, and notifications
for the public-facing MSP service quote wizard.
"""

import secrets
from datetime import datetime
from decimal import Decimal
from enum import Enum as PyEnum
from typing import TYPE_CHECKING, Optional

from sqlalchemy import (
    CHAR,
    Boolean,
    CheckConstraint,
    DateTime,
    ForeignKey,
    Index,
    Integer,
    Numeric,
    String,
    Text,
    func,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship

from .base import Base, TimestampMixin, UUIDMixin


class QuoteStatus(str, PyEnum):
    """Status options for quotes."""
    DRAFT = "draft"
    SUBMITTED = "submitted"
    VIEWED = "viewed"
    FOLLOWED_UP = "followed_up"
    CONVERTED = "converted"
    EXPIRED = "expired"


class ServiceCategory(str, PyEnum):
    """Service category options for quote items."""
    GPS_MONITORING = "gps_monitoring"
    SUPPORT_PLAN = "support_plan"
    VOIP = "voip"
    WEB_HOSTING = "web_hosting"
    EMAIL = "email"
    HARDWARE = "hardware"
    ADDON = "addon"


class BillingFrequency(str, PyEnum):
    """Billing frequency options for quote items."""
    MONTHLY = "monthly"
    YEARLY = "yearly"
    ONE_TIME = "one_time"


class NotificationType(str, PyEnum):
    """Notification types for quote events."""
    EMAIL = "email"
    WEBHOOK = "webhook"


class Quote(Base, UUIDMixin, TimestampMixin):
    """
    Quote model representing a service quote request.

    Stores quote details including contact information, selections,
    and calculated totals. Uses an access token for public URL access.

    Attributes:
        access_token: Unique token for public access (URL-safe, 43 chars)
        status: Current quote status (draft, submitted, viewed, etc.)
        company_name: Prospect company name
        contact_name: Primary contact name
        contact_email: Contact email address
        contact_phone: Contact phone number
        employee_count: Number of employees/users
        monthly_total: Calculated monthly recurring total
        setup_total: Calculated one-time setup total
        expires_at: Quote expiration date
        submitted_at: Timestamp when quote was submitted
        ip_address: IP address of the requester
        user_agent: Browser user agent string
    """

    __tablename__ = "quotes"

    # Access control
    access_token: Mapped[str] = mapped_column(
        String(64),
        unique=True,
        nullable=False,
        default=lambda: secrets.token_urlsafe(32),
        doc="Unique access token for public URL (URL-safe, 43 chars)"
    )

    # Status
    status: Mapped[str] = mapped_column(
        String(20),
        nullable=False,
        default=QuoteStatus.DRAFT.value,
        server_default=QuoteStatus.DRAFT.value,
        doc="Quote status: draft, submitted, viewed, followed_up, converted, expired"
    )

    # Contact information
    company_name: Mapped[Optional[str]] = mapped_column(
        String(255),
        doc="Prospect company name"
    )

    contact_name: Mapped[Optional[str]] = mapped_column(
        String(255),
        doc="Primary contact name"
    )

    contact_email: Mapped[Optional[str]] = mapped_column(
        String(255),
        doc="Contact email address"
    )

    contact_phone: Mapped[Optional[str]] = mapped_column(
        String(50),
        doc="Contact phone number"
    )

    # Business information
    employee_count: Mapped[Optional[int]] = mapped_column(
        Integer,
        doc="Number of employees/users"
    )

    industry: Mapped[Optional[str]] = mapped_column(
        String(100),
        doc="Industry/vertical of the prospect"
    )

    current_it_situation: Mapped[Optional[str]] = mapped_column(
        Text,
        doc="Description of the prospect's current IT setup"
    )

    # Calculated totals
    monthly_total: Mapped[Decimal] = mapped_column(
        Numeric(10, 2),
        nullable=False,
        default=Decimal("0.00"),
        server_default="0.00",
        doc="Calculated monthly recurring total"
    )

    setup_total: Mapped[Decimal] = mapped_column(
        Numeric(10, 2),
        nullable=False,
        default=Decimal("0.00"),
        server_default="0.00",
        doc="Calculated one-time setup total"
    )

    # Timestamps
    expires_at: Mapped[Optional[datetime]] = mapped_column(
        DateTime,
        doc="Quote expiration date"
    )

    submitted_at: Mapped[Optional[datetime]] = mapped_column(
        DateTime,
        doc="Timestamp when quote was submitted"
    )

    # Tracking
    ip_address: Mapped[Optional[str]] = mapped_column(
        String(45),
        doc="IP address of the requester (IPv4 or IPv6)"
    )

    user_agent: Mapped[Optional[str]] = mapped_column(
        Text,
        doc="Browser user agent string"
    )

    # Marketing attribution
    source: Mapped[Optional[str]] = mapped_column(
        String(50),
        server_default="website",
        doc="Lead source (e.g., website, referral)"
    )

    utm_source: Mapped[Optional[str]] = mapped_column(
        String(100),
        doc="UTM source parameter"
    )

    utm_medium: Mapped[Optional[str]] = mapped_column(
        String(100),
        doc="UTM medium parameter"
    )

    utm_campaign: Mapped[Optional[str]] = mapped_column(
        String(100),
        doc="UTM campaign parameter"
    )

    # Syncro RMM Integration
    syncro_lead_id: Mapped[Optional[str]] = mapped_column(
        String(100),
        doc="Lead ID in SyncroRMM if synced"
    )

    syncro_synced_at: Mapped[Optional[datetime]] = mapped_column(
        DateTime,
        doc="Timestamp when quote was synced to Syncro"
    )

    is_existing_customer: Mapped[bool] = mapped_column(
        Boolean,
        nullable=False,
        default=False,
        server_default="0",
        doc="Whether this is an existing Syncro customer"
    )

    # Relationships
    items: Mapped[list["QuoteItem"]] = relationship(
        "QuoteItem",
        back_populates="quote",
        cascade="all, delete-orphan",
        doc="Line items in this quote"
    )

    activities: Mapped[list["QuoteActivity"]] = relationship(
        "QuoteActivity",
        back_populates="quote",
        cascade="all, delete-orphan",
        order_by="QuoteActivity.created_at.desc()",
        doc="Activity log for this quote"
    )

    notifications: Mapped[list["QuoteNotification"]] = relationship(
        "QuoteNotification",
        back_populates="quote",
        cascade="all, delete-orphan",
        doc="Notifications sent for this quote"
    )

    # Constraints and indexes
    __table_args__ = (
        CheckConstraint(
            "status IN ('draft', 'submitted', 'viewed', 'followed_up', 'converted', 'expired')",
            name="ck_quotes_status"
        ),
        Index("idx_quotes_access_token", "access_token"),
        Index("idx_quotes_status", "status"),
        Index("idx_quotes_contact_email", "contact_email"),
        Index("idx_quotes_created_at", "created_at"),
    )

    def __repr__(self) -> str:
        """String representation of the quote."""
        return f"<Quote(id='{self.id}', status='{self.status}', company='{self.company_name}')>"


class QuoteItem(Base, UUIDMixin):
    """
    Quote item model representing a single line item in a quote.

    Stores product details, pricing, and quantity information.

    Attributes:
        quote_id: Reference to the parent quote
        category: Service category (gps_monitoring, support_plan, etc.)
        product_code: Product code identifier
        product_name: Name of the product/service
        description: Detailed description of the product/service
        quantity: Number of units
        unit_price: Price per unit
        setup_price: One-time setup price
        billing_frequency: Billing frequency (monthly, yearly, one_time)
        tier: Pricing tier
        is_recommended: Whether this item is recommended
        created_at: Timestamp when item was created
    """

    __tablename__ = "quote_items"

    # Foreign keys
    quote_id: Mapped[str] = mapped_column(
        CHAR(36),
        ForeignKey("quotes.id", ondelete="CASCADE"),
        nullable=False,
        doc="Reference to the parent quote"
    )

    # Category
    category: Mapped[str] = mapped_column(
        String(50),
        nullable=False,
        doc="Service category: gps_monitoring, support_plan, voip, web_hosting, email, hardware, addon"
    )

    # Product identification
    product_code: Mapped[str] = mapped_column(
        String(50),
        nullable=False,
        doc="Product code identifier"
    )

    product_name: Mapped[str] = mapped_column(
        String(255),
        nullable=False,
        doc="Name of the product/service"
    )

    description: Mapped[Optional[str]] = mapped_column(
        Text,
        doc="Detailed description of the product/service"
    )

    # Quantity and pricing
    quantity: Mapped[int] = mapped_column(
        Integer,
        nullable=False,
        default=1,
        doc="Number of units"
    )

    unit_price: Mapped[Decimal] = mapped_column(
        Numeric(10, 2),
        nullable=False,
        doc="Price per unit"
    )

    setup_price: Mapped[Decimal] = mapped_column(
        Numeric(10, 2),
        nullable=False,
        default=Decimal("0.00"),
        server_default="0.00",
        doc="One-time setup price"
    )

    # Billing
    billing_frequency: Mapped[str] = mapped_column(
        String(20),
        nullable=False,
        default=BillingFrequency.MONTHLY.value,
        server_default="monthly",
        doc="Billing frequency: monthly, yearly, one_time"
    )

    # Configuration
    tier: Mapped[Optional[str]] = mapped_column(
        String(50),
        doc="Pricing tier"
    )

    is_recommended: Mapped[bool] = mapped_column(
        Boolean,
        nullable=False,
        default=False,
        server_default="0",
        doc="Whether this item is recommended"
    )

    # Timestamp (no updated_at in DB)
    created_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        server_default=func.now(),
        doc="Timestamp when the item was created"
    )

    # Relationships
    quote: Mapped["Quote"] = relationship(
        "Quote",
        back_populates="items"
    )

    # Constraints and indexes
    __table_args__ = (
        CheckConstraint(
            "category IN ('gps_monitoring', 'support_plan', 'voip', 'web_hosting', 'email', 'hardware', 'addon')",
            name="ck_quote_items_category"
        ),
        CheckConstraint(
            "billing_frequency IN ('monthly', 'yearly', 'one_time')",
            name="ck_quote_items_billing_frequency"
        ),
        Index("idx_quote_items_quote_id", "quote_id"),
        Index("idx_quote_items_category", "category"),
    )

    def __repr__(self) -> str:
        """String representation of the quote item."""
        return f"<QuoteItem(product='{self.product_name}', qty={self.quantity}, price={self.unit_price})>"

    @property
    def line_total(self) -> Decimal:
        """Calculate the line total (unit_price * quantity)."""
        return self.unit_price * self.quantity

    @property
    def monthly_amount(self) -> Decimal:
        """Calculate the monthly amount based on billing frequency."""
        if self.billing_frequency == BillingFrequency.MONTHLY.value:
            return self.line_total
        elif self.billing_frequency == BillingFrequency.YEARLY.value:
            return self.line_total / Decimal("12")
        else:  # one_time
            return Decimal("0.00")


class QuoteActivity(Base, UUIDMixin):
    """
    Quote activity model for tracking quote history and changes.

    Logs all actions taken on a quote for audit and tracking purposes.

    Attributes:
        quote_id: Reference to the parent quote
        action: Action performed (created, updated, submitted, etc.)
        step_name: Name of the wizard step associated with the action
        details: Additional details about the action (longtext)
        ip_address: IP address of the actor
        created_at: Timestamp when the activity was recorded
    """

    __tablename__ = "quote_activity"

    # Foreign keys
    quote_id: Mapped[str] = mapped_column(
        CHAR(36),
        ForeignKey("quotes.id", ondelete="CASCADE"),
        nullable=False,
        doc="Reference to the parent quote"
    )

    # Activity details
    action: Mapped[str] = mapped_column(
        String(50),
        nullable=False,
        doc="Action performed: created, updated, item_added, item_removed, submitted, status_changed, etc."
    )

    step_name: Mapped[Optional[str]] = mapped_column(
        String(50),
        doc="Name of the wizard step associated with the action"
    )

    details: Mapped[Optional[str]] = mapped_column(
        Text,
        doc="Additional details about the action"
    )

    ip_address: Mapped[Optional[str]] = mapped_column(
        String(45),
        doc="IP address of the actor"
    )

    # Timestamp (no updated_at in DB)
    created_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        server_default=func.now(),
        doc="Timestamp when the activity was recorded"
    )

    # Relationships
    quote: Mapped["Quote"] = relationship(
        "Quote",
        back_populates="activities"
    )

    # Indexes
    __table_args__ = (
        Index("idx_quote_activities_quote_id", "quote_id"),
        Index("idx_quote_activities_action", "action"),
        Index("idx_quote_activities_created_at", "created_at"),
    )

    def __repr__(self) -> str:
        """String representation of the quote activity."""
        return f"<QuoteActivity(quote_id='{self.quote_id}', action='{self.action}')>"


class QuoteNotification(Base, UUIDMixin):
    """
    Quote notification model for tracking notifications sent.

    Records all notifications (emails, webhooks) sent for a quote.

    Attributes:
        quote_id: Reference to the parent quote
        notification_type: Type of notification (email, webhook)
        recipient: Notification recipient (email address, webhook URL, etc.)
        subject: Notification subject
        body: Notification body content
        status: Delivery status (pending, sent, failed)
        attempts: Number of delivery attempts
        last_attempt_at: Timestamp of last delivery attempt
        sent_at: Timestamp when notification was successfully sent
        error_message: Error message if delivery failed
        created_at: Timestamp when notification was created
    """

    __tablename__ = "quote_notifications"

    # Foreign keys
    quote_id: Mapped[str] = mapped_column(
        CHAR(36),
        ForeignKey("quotes.id", ondelete="CASCADE"),
        nullable=False,
        doc="Reference to the parent quote"
    )

    # Notification details
    notification_type: Mapped[str] = mapped_column(
        String(30),
        nullable=False,
        doc="Type of notification: email, webhook"
    )

    recipient: Mapped[str] = mapped_column(
        String(255),
        nullable=False,
        doc="Notification recipient (email address, webhook URL, etc.)"
    )

    subject: Mapped[Optional[str]] = mapped_column(
        String(255),
        doc="Notification subject"
    )

    body: Mapped[Optional[str]] = mapped_column(
        Text,
        doc="Notification body content"
    )

    # Status tracking
    status: Mapped[str] = mapped_column(
        String(20),
        nullable=False,
        default="pending",
        server_default="pending",
        doc="Delivery status: pending, sent, failed"
    )

    attempts: Mapped[int] = mapped_column(
        Integer,
        nullable=False,
        default=0,
        server_default="0",
        doc="Number of delivery attempts"
    )

    last_attempt_at: Mapped[Optional[datetime]] = mapped_column(
        DateTime,
        doc="Timestamp of last delivery attempt"
    )

    sent_at: Mapped[Optional[datetime]] = mapped_column(
        DateTime,
        doc="Timestamp when notification was successfully sent"
    )

    error_message: Mapped[Optional[str]] = mapped_column(
        Text,
        doc="Error message if delivery failed"
    )

    # Timestamp (no updated_at in DB)
    created_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        server_default=func.now(),
        doc="Timestamp when the notification was created"
    )

    # Relationships
    quote: Mapped["Quote"] = relationship(
        "Quote",
        back_populates="notifications"
    )

    # Constraints and indexes
    __table_args__ = (
        CheckConstraint(
            "notification_type IN ('email', 'webhook')",
            name="ck_quote_notifications_type"
        ),
        CheckConstraint(
            "status IN ('pending', 'sent', 'failed')",
            name="ck_quote_notifications_status"
        ),
        Index("idx_quote_notifications_quote_id", "quote_id"),
        Index("idx_quote_notifications_type", "notification_type"),
        Index("idx_quote_notifications_status", "status"),
    )

    def __repr__(self) -> str:
        """String representation of the quote notification."""
        return f"<QuoteNotification(type='{self.notification_type}', recipient='{self.recipient}', status='{self.status}')>"
