"""
Service for payment business logic.
"""

from datetime import date
from decimal import Decimal
from typing import Any, Dict, List, Optional

from app import db
from app.constants import WebhookEvent
from app.models import Invoice, Payment
from app.repositories import InvoiceRepository, PaymentRepository
from app.utils.db import safe_commit
from app.utils.event_bus import emit_event


class PaymentService:
    """Service for payment operations"""

    def __init__(self):
        self.payment_repo = PaymentRepository()
        self.invoice_repo = InvoiceRepository()

    def create_payment(
        self,
        invoice_id: int,
        amount: Decimal,
        payment_date: date,
        received_by: int,
        currency: Optional[str] = None,
        method: Optional[str] = None,
        reference: Optional[str] = None,
        notes: Optional[str] = None,
        status: str = "completed",
        gateway_transaction_id: Optional[str] = None,
        gateway_fee: Optional[Decimal] = None,
    ) -> Dict[str, Any]:
        """
        Create a new payment.

        Returns:
            dict with 'success', 'message', and 'payment' keys
        """
        # Validate invoice
        invoice = self.invoice_repo.get_by_id(invoice_id)
        if not invoice:
            return {"success": False, "message": "Invoice not found", "error": "invalid_invoice"}

        # Validate amount
        if amount <= 0:
            return {"success": False, "message": "Amount must be greater than zero", "error": "invalid_amount"}

        # Get currency from invoice if not provided
        if not currency:
            currency = invoice.currency_code

        # Create payment
        payment = self.payment_repo.create(
            invoice_id=invoice_id,
            amount=amount,
            currency=currency,
            payment_date=payment_date,
            method=method,
            reference=reference,
            notes=notes,
            status=status,
            received_by=received_by,
            gateway_transaction_id=gateway_transaction_id,
            gateway_fee=gateway_fee,
        )

        # Calculate net amount
        payment.calculate_net_amount()

        # Update invoice payment status if payment is completed
        if status == "completed":
            total_payments = self.payment_repo.get_total_for_invoice(invoice_id)
            invoice.amount_paid = total_payments + amount

            # Update payment status
            if invoice.amount_paid >= invoice.total_amount:
                invoice.payment_status = "fully_paid"
            elif invoice.amount_paid > 0:
                invoice.payment_status = "partially_paid"
            else:
                invoice.payment_status = "unpaid"

        if not safe_commit("create_payment", {"invoice_id": invoice_id, "received_by": received_by}):
            return {
                "success": False,
                "message": "Could not create payment due to a database error",
                "error": "database_error",
            }

        # Emit domain event
        emit_event("payment.created", {"payment_id": payment.id, "invoice_id": invoice_id, "amount": float(amount)})

        # Notify client about payment received
        if invoice.client_id and status == "completed":
            try:
                from app.services.client_notification_service import ClientNotificationService

                notification_service = ClientNotificationService()
                notification_service.notify_invoice_paid(invoice_id, invoice.client_id, float(amount))
            except Exception as e:
                import logging

                logger = logging.getLogger(__name__)
                logger.error(f"Failed to send client notification for payment {payment.id}: {e}", exc_info=True)

        return {"success": True, "message": "Payment created successfully", "payment": payment}

    def get_invoice_payments(self, invoice_id: int) -> List[Payment]:
        """Get all payments for an invoice"""
        return self.payment_repo.get_by_invoice(invoice_id, include_relations=True)

    def get_total_paid(self, invoice_id: int) -> Decimal:
        """Get total amount paid for an invoice"""
        return self.payment_repo.get_total_for_invoice(invoice_id)

    def update_payment(self, payment_id: int, user_id: int, **kwargs) -> Dict[str, Any]:
        """
        Update a payment.

        Returns:
            dict with 'success', 'message', and 'payment' keys
        """
        payment = self.payment_repo.get_by_id(payment_id)
        if not payment:
            return {"success": False, "message": "Payment not found", "error": "not_found"}

        # Update fields
        for field in ("currency", "method", "reference", "notes", "status"):
            if field in kwargs:
                setattr(payment, field, kwargs[field])
        if "amount" in kwargs:
            payment.amount = kwargs["amount"]
        if "payment_date" in kwargs:
            payment.payment_date = kwargs["payment_date"]

        # Recalculate net amount
        payment.calculate_net_amount()

        # Update invoice payment status if needed
        if payment.status == "completed" and payment.invoice:
            total_payments = self.payment_repo.get_total_for_invoice(payment.invoice_id)
            payment.invoice.amount_paid = total_payments

            # Update payment status
            if payment.invoice.amount_paid >= payment.invoice.total_amount:
                payment.invoice.payment_status = "fully_paid"
            elif payment.invoice.amount_paid > 0:
                payment.invoice.payment_status = "partially_paid"
            else:
                payment.invoice.payment_status = "unpaid"

        if not safe_commit("update_payment", {"payment_id": payment_id, "user_id": user_id}):
            return {
                "success": False,
                "message": "Could not update payment due to a database error",
                "error": "database_error",
            }

        return {"success": True, "message": "Payment updated successfully", "payment": payment}

    def delete_payment(self, payment_id: int, user_id: int) -> Dict[str, Any]:
        """
        Delete a payment.

        Returns:
            dict with 'success' and 'message' keys
        """
        payment = self.payment_repo.get_by_id(payment_id)
        if not payment:
            return {"success": False, "message": "Payment not found", "error": "not_found"}

        invoice_id = payment.invoice_id

        # Delete payment
        db.session.delete(payment)

        # Update invoice payment status
        if payment.invoice:
            total_payments = self.payment_repo.get_total_for_invoice(invoice_id)
            payment.invoice.amount_paid = total_payments

            # Update payment status
            if payment.invoice.amount_paid >= payment.invoice.total_amount:
                payment.invoice.payment_status = "fully_paid"
            elif payment.invoice.amount_paid > 0:
                payment.invoice.payment_status = "partially_paid"
            else:
                payment.invoice.payment_status = "unpaid"

        if not safe_commit("delete_payment", {"payment_id": payment_id, "user_id": user_id}):
            return {
                "success": False,
                "message": "Could not delete payment due to a database error",
                "error": "database_error",
            }

        return {"success": True, "message": "Payment deleted successfully"}
