# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""Billing models: BillingAccount, BillingTransaction, BillingSettings, UsageStatistics."""

from typing import List, Dict, Any, Optional
from enum import Enum
from datetime import date, datetime
from pydantic import BaseModel, Field
from modules.shared.attributeUtils import registerModelLabels
import uuid


class BillingModelEnum(str, Enum):
    """Billing model types."""
    PREPAY_MANDATE = "PREPAY_MANDATE"     # Prepaid budget shared by all users in mandate
    PREPAY_USER = "PREPAY_USER"           # Prepaid budget per user within mandate
    CREDIT_POSTPAY = "CREDIT_POSTPAY"     # Credit with monthly invoice (requires billing address)
    UNLIMITED = "UNLIMITED"               # No cost limitation (internal mandates only)


class AccountTypeEnum(str, Enum):
    """Account type for billing accounts."""
    MANDATE = "MANDATE"   # Account for entire mandate
    USER = "USER"         # Account for specific user within mandate


class TransactionTypeEnum(str, Enum):
    """Transaction types for billing."""
    CREDIT = "CREDIT"         # Credit/top-up (positive)
    DEBIT = "DEBIT"           # Debit/usage (positive amount, reduces balance)
    ADJUSTMENT = "ADJUSTMENT" # Manual adjustment by admin


class ReferenceTypeEnum(str, Enum):
    """Reference types for transactions."""
    WORKFLOW = "WORKFLOW"   # AI workflow usage
    PAYMENT = "PAYMENT"     # Payment/top-up
    ADMIN = "ADMIN"         # Admin adjustment
    SYSTEM = "SYSTEM"       # System credit (e.g., initial credit)


class PeriodTypeEnum(str, Enum):
    """Period types for usage statistics."""
    DAY = "DAY"
    MONTH = "MONTH"
    YEAR = "YEAR"


class BillingAddress(BaseModel):
    """Billing address for CREDIT_POSTPAY mandates."""
    company: str = Field(..., description="Company name")
    street: str = Field(..., description="Street and number")
    zip: str = Field(..., description="Postal code")
    city: str = Field(..., description="City")
    country: str = Field(default="CH", description="Country code")
    vatNumber: Optional[str] = Field(None, description="VAT number (optional)")


registerModelLabels(
    "BillingAddress",
    {"en": "Billing Address", "de": "Rechnungsadresse"},
    {
        "company": {"en": "Company", "de": "Firma"},
        "street": {"en": "Street", "de": "Strasse"},
        "zip": {"en": "ZIP", "de": "PLZ"},
        "city": {"en": "City", "de": "Ort"},
        "country": {"en": "Country", "de": "Land"},
        "vatNumber": {"en": "VAT Number", "de": "MwSt-Nummer"},
    },
)


class BillingAccount(BaseModel):
    """Billing account for mandate or user-mandate combination."""
    id: str = Field(
        default_factory=lambda: str(uuid.uuid4()), description="Primary key"
    )
    mandateId: str = Field(..., description="Foreign key to Mandate")
    userId: Optional[str] = Field(None, description="Foreign key to User (only for PREPAY_USER)")
    accountType: AccountTypeEnum = Field(..., description="Account type: MANDATE or USER")
    balance: float = Field(default=0.0, description="Current balance in CHF")
    creditLimit: Optional[float] = Field(None, description="Credit limit in CHF (only for CREDIT_POSTPAY)")
    warningThreshold: float = Field(default=0.0, description="Warning threshold in CHF")
    lastWarningAt: Optional[datetime] = Field(None, description="Last warning sent timestamp")
    enabled: bool = Field(default=True, description="Account is active")


registerModelLabels(
    "BillingAccount",
    {"en": "Billing Account", "de": "Abrechnungskonto"},
    {
        "id": {"en": "ID", "de": "ID"},
        "mandateId": {"en": "Mandate ID", "de": "Mandanten-ID"},
        "userId": {"en": "User ID", "de": "Benutzer-ID"},
        "accountType": {"en": "Account Type", "de": "Kontotyp"},
        "balance": {"en": "Balance (CHF)", "de": "Guthaben (CHF)"},
        "creditLimit": {"en": "Credit Limit (CHF)", "de": "Kreditlimit (CHF)"},
        "warningThreshold": {"en": "Warning Threshold (CHF)", "de": "Warnschwelle (CHF)"},
        "lastWarningAt": {"en": "Last Warning", "de": "Letzte Warnung"},
        "enabled": {"en": "Enabled", "de": "Aktiv"},
    },
)


class BillingTransaction(BaseModel):
    """Single billing transaction (credit, debit, adjustment)."""
    id: str = Field(
        default_factory=lambda: str(uuid.uuid4()), description="Primary key"
    )
    accountId: str = Field(..., description="Foreign key to BillingAccount")
    transactionType: TransactionTypeEnum = Field(..., description="Transaction type")
    amount: float = Field(..., description="Amount in CHF (always positive)")
    description: str = Field(..., description="Transaction description")
    
    # Reference to source
    referenceType: Optional[ReferenceTypeEnum] = Field(None, description="Reference type")
    referenceId: Optional[str] = Field(None, description="Reference ID")
    
    # Context for workflow transactions
    workflowId: Optional[str] = Field(None, description="Workflow ID (for WORKFLOW transactions)")
    featureInstanceId: Optional[str] = Field(None, description="Feature instance ID")
    featureCode: Optional[str] = Field(None, description="Feature code (e.g., chatplayground, automation)")
    aicoreProvider: Optional[str] = Field(None, description="AICore provider (anthropic, openai, etc.)")
    aicoreModel: Optional[str] = Field(None, description="AICore model name (e.g., claude-4-sonnet, gpt-4o)")
    createdByUserId: Optional[str] = Field(None, description="User who created/caused this transaction")


registerModelLabels(
    "BillingTransaction",
    {"en": "Billing Transaction", "de": "Transaktion"},
    {
        "id": {"en": "ID", "de": "ID"},
        "accountId": {"en": "Account ID", "de": "Konto-ID"},
        "transactionType": {"en": "Type", "de": "Typ"},
        "amount": {"en": "Amount (CHF)", "de": "Betrag (CHF)"},
        "description": {"en": "Description", "de": "Beschreibung"},
        "referenceType": {"en": "Reference Type", "de": "Referenztyp"},
        "referenceId": {"en": "Reference ID", "de": "Referenz-ID"},
        "workflowId": {"en": "Workflow ID", "de": "Workflow-ID"},
        "featureInstanceId": {"en": "Feature Instance ID", "de": "Feature-Instanz-ID"},
        "featureCode": {"en": "Feature Code", "de": "Feature-Code"},
        "aicoreProvider": {"en": "AI Provider", "de": "AI-Anbieter"},
        "aicoreModel": {"en": "AI Model", "de": "AI-Modell"},
        "createdByUserId": {"en": "Created By User", "de": "Erstellt von Benutzer"},
    },
)


class BillingSettings(BaseModel):
    """Billing settings per mandate."""
    id: str = Field(
        default_factory=lambda: str(uuid.uuid4()), description="Primary key"
    )
    mandateId: str = Field(..., description="Foreign key to Mandate (UNIQUE)")
    billingModel: BillingModelEnum = Field(..., description="Billing model")
    
    # Configuration
    defaultUserCredit: float = Field(default=10.0, description="Initial credit in CHF for new users (PREPAY_USER)")
    warningThresholdPercent: float = Field(default=10.0, description="Warning threshold as percentage")
    blockOnZeroBalance: bool = Field(default=True, description="Block AI features when balance is zero")
    
    # Billing address (required for CREDIT_POSTPAY)
    billingAddress: Optional[BillingAddress] = Field(None, description="Billing address")
    
    # Notifications
    notifyEmails: List[str] = Field(default_factory=list, description="Email addresses for billing notifications")
    notifyOnWarning: bool = Field(default=True, description="Send email when warning threshold is reached")


registerModelLabels(
    "BillingSettings",
    {"en": "Billing Settings", "de": "Abrechnungseinstellungen"},
    {
        "id": {"en": "ID", "de": "ID"},
        "mandateId": {"en": "Mandate ID", "de": "Mandanten-ID"},
        "billingModel": {"en": "Billing Model", "de": "Abrechnungsmodell"},
        "defaultUserCredit": {"en": "Default User Credit (CHF)", "de": "Standard-Startguthaben (CHF)"},
        "warningThresholdPercent": {"en": "Warning Threshold (%)", "de": "Warnschwelle (%)"},
        "blockOnZeroBalance": {"en": "Block on Zero Balance", "de": "Bei 0 blockieren"},
        "billingAddress": {"en": "Billing Address", "de": "Rechnungsadresse"},
        "notifyEmails": {"en": "Notification Emails", "de": "Benachrichtigungs-Emails"},
        "notifyOnWarning": {"en": "Notify on Warning", "de": "Bei Warnung benachrichtigen"},
    },
)


class UsageStatistics(BaseModel):
    """Aggregated usage statistics for quick retrieval."""
    id: str = Field(
        default_factory=lambda: str(uuid.uuid4()), description="Primary key"
    )
    accountId: str = Field(..., description="Foreign key to BillingAccount")
    periodType: PeriodTypeEnum = Field(..., description="Period type")
    periodStart: date = Field(..., description="Period start date")
    
    # Aggregated values
    totalCostCHF: float = Field(default=0.0, description="Total cost in CHF")
    transactionCount: int = Field(default=0, description="Number of transactions")
    
    # Breakdown by provider
    costByProvider: Dict[str, float] = Field(
        default_factory=dict, 
        description="Cost breakdown by provider (e.g., {'anthropic': 12.50, 'openai': 8.30})"
    )
    
    # Breakdown by feature
    costByFeature: Dict[str, float] = Field(
        default_factory=dict,
        description="Cost breakdown by feature (e.g., {'chatplayground': 15.00, 'automation': 5.80})"
    )


registerModelLabels(
    "UsageStatistics",
    {"en": "Usage Statistics", "de": "Nutzungsstatistik"},
    {
        "id": {"en": "ID", "de": "ID"},
        "accountId": {"en": "Account ID", "de": "Konto-ID"},
        "periodType": {"en": "Period Type", "de": "Periodentyp"},
        "periodStart": {"en": "Period Start", "de": "Periodenbeginn"},
        "totalCostCHF": {"en": "Total Cost (CHF)", "de": "Gesamtkosten (CHF)"},
        "transactionCount": {"en": "Transaction Count", "de": "Anzahl Transaktionen"},
        "costByProvider": {"en": "Cost by Provider", "de": "Kosten nach Anbieter"},
        "costByFeature": {"en": "Cost by Feature", "de": "Kosten nach Feature"},
    },
)


# ============================================================================
# Response Models for API
# ============================================================================

class BillingBalanceResponse(BaseModel):
    """Response model for balance endpoint."""
    mandateId: str
    mandateName: str
    billingModel: BillingModelEnum
    balance: float
    currency: str = "CHF"
    warningThreshold: float
    isWarning: bool
    creditLimit: Optional[float] = None


class BillingStatisticsChartData(BaseModel):
    """Chart data point for statistics."""
    label: str
    totalCost: float
    byProvider: Dict[str, float]


class BillingStatisticsResponse(BaseModel):
    """Response model for statistics endpoint."""
    mandateId: str
    period: PeriodTypeEnum
    year: int
    month: Optional[int] = None
    currency: str = "CHF"
    data: List[BillingStatisticsChartData]
    totals: Dict[str, Any]


class BillingCheckResult(BaseModel):
    """Result of a billing balance check."""
    allowed: bool
    reason: Optional[str] = None
    currentBalance: Optional[float] = None
    requiredAmount: Optional[float] = None
    billingModel: Optional[BillingModelEnum] = None
