# app/modules/messaging/services/store_email_settings_service.py
"""
Store Email Settings Service.

Handles CRUD operations for store email configuration:
- SMTP settings
- Advanced providers (SendGrid, Mailgun, SES) - tier-gated
- Sender identity (from_email, from_name, reply_to)
- Signature/footer customization
- Configuration verification via test email
"""

from __future__ import annotations

import logging
import smtplib
from datetime import UTC, datetime
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from typing import TYPE_CHECKING

from sqlalchemy.orm import Session

from app.exceptions import (
    AuthorizationException,
    ExternalServiceException,
    ResourceNotFoundException,
    ValidationException,
)
from app.modules.messaging.models import (
    PREMIUM_EMAIL_PROVIDERS,
    EmailProvider,
    StoreEmailSettings,
)

if TYPE_CHECKING:
    from app.modules.billing.models import TierCode

logger = logging.getLogger(__name__)


def _get_premium_tiers() -> set:
    """Get premium tier codes (deferred to avoid cross-module import at module level)."""
    from app.modules.billing.models import TierCode

    return {TierCode.BUSINESS, TierCode.ENTERPRISE}


class StoreEmailSettingsService:
    """Service for managing store email settings."""

    # =========================================================================
    # READ OPERATIONS
    # =========================================================================

    def get_settings(self, db: Session, store_id: int) -> StoreEmailSettings | None:
        """Get email settings for a store."""
        return (
            db.query(StoreEmailSettings)
            .filter(StoreEmailSettings.store_id == store_id)
            .first()
        )

    def get_settings_or_404(self, db: Session, store_id: int) -> StoreEmailSettings:
        """Get email settings or raise 404."""
        settings = self.get_settings(db, store_id)
        if not settings:
            raise ResourceNotFoundException(
                resource_type="store_email_settings",
                identifier=str(store_id),
            )
        return settings

    def is_configured(self, db: Session, store_id: int) -> bool:
        """Check if store has configured email settings."""
        settings = self.get_settings(db, store_id)
        return settings is not None and settings.is_configured

    def get_status(self, db: Session, store_id: int) -> dict:
        """
        Get email configuration status for a store.

        Returns:
            dict with is_configured, is_verified, provider, etc.
        """
        settings = self.get_settings(db, store_id)
        if not settings:
            return {
                "is_configured": False,
                "is_verified": False,
                "provider": None,
                "from_email": None,
                "from_name": None,
                "message": "Email settings not configured. Configure SMTP to send emails.",
            }

        return {
            "is_configured": settings.is_configured,
            "is_verified": settings.is_verified,
            "provider": settings.provider,
            "from_email": settings.from_email,
            "from_name": settings.from_name,
            "last_verified_at": settings.last_verified_at.isoformat() if settings.last_verified_at else None,
            "verification_error": settings.verification_error,
            "message": self._get_status_message(settings),
        }

    def _get_status_message(self, settings: StoreEmailSettings) -> str:
        """Generate a human-readable status message."""
        if not settings.is_configured:
            return "Complete your email configuration to send emails."
        if not settings.is_verified:
            return "Email configured but not verified. Send a test email to verify."
        return "Email settings configured and verified."

    # =========================================================================
    # WRITE OPERATIONS
    # =========================================================================

    def create_or_update(
        self,
        db: Session,
        store_id: int,
        data: dict,
        current_tier: TierCode | None = None,
    ) -> StoreEmailSettings:
        """
        Create or update store email settings.

        Args:
            db: Database session
            store_id: Store ID
            data: Settings data (from_email, from_name, smtp_*, etc.)
            current_tier: Store's current subscription tier (for premium provider validation)

        Returns:
            Updated StoreEmailSettings

        Raises:
            AuthorizationException: If trying to use premium provider without required tier
        """
        # Validate premium provider access
        provider = data.get("provider", "smtp")
        if provider in [p.value for p in PREMIUM_EMAIL_PROVIDERS]:
            if current_tier not in _get_premium_tiers():
                raise AuthorizationException(
                    message=f"Provider '{provider}' requires Business or Enterprise tier. "
                    "Upgrade your plan to use advanced email providers.",
                    details={"required_permission": "business_tier"},
                )

        settings = self.get_settings(db, store_id)
        if not settings:
            settings = StoreEmailSettings(store_id=store_id)
            db.add(settings)

        # Update fields
        for field in [
            "from_email",
            "from_name",
            "reply_to_email",
            "signature_text",
            "signature_html",
            "provider",
            # SMTP
            "smtp_host",
            "smtp_port",
            "smtp_username",
            "smtp_password",
            "smtp_use_tls",
            "smtp_use_ssl",
            # SendGrid
            "sendgrid_api_key",
            # Mailgun
            "mailgun_api_key",
            "mailgun_domain",
            # SES
            "ses_access_key_id",
            "ses_secret_access_key",
            "ses_region",
        ]:
            if field in data and data[field] is not None:
                # Don't overwrite passwords/keys with empty strings
                if field.endswith(("_password", "_key", "_access_key")) and data[field] == "":
                    continue
                setattr(settings, field, data[field])

        # Update configuration status
        settings.update_configuration_status()

        # Reset verification if provider/credentials changed
        if any(
            f in data
            for f in ["provider", "smtp_host", "smtp_password", "sendgrid_api_key", "mailgun_api_key", "ses_access_key_id"]
        ):
            settings.is_verified = False
            settings.verification_error = None

        db.flush()
        logger.info(f"Updated email settings for store {store_id}: provider={settings.provider}")
        return settings

    def delete(self, db: Session, store_id: int) -> None:
        """
        Delete email settings for a store.

        Args:
            db: Database session
            store_id: Store ID

        Raises:
            ResourceNotFoundException: If settings not found
        """
        settings = self.get_settings(db, store_id)
        if not settings:
            raise ResourceNotFoundException(
                resource_type="store_email_settings",
                identifier=str(store_id),
            )
        db.delete(settings)
        db.flush()
        logger.info(f"Deleted email settings for store {store_id}")

    # =========================================================================
    # VERIFICATION
    # =========================================================================

    def verify_settings(self, db: Session, store_id: int, test_email: str) -> dict:
        """
        Verify email settings by sending a test email.

        Args:
            db: Database session
            store_id: Store ID
            test_email: Email address to send test email to

        Returns:
            dict with success status and message

        Raises:
            ResourceNotFoundException: If settings not found
            ValidationException: If settings incomplete
        """
        settings = self.get_settings_or_404(db, store_id)

        if not settings.is_fully_configured():
            raise ValidationException(
                message="Email settings incomplete. Configure all required fields first.",
                field="settings",
            )

        try:
            # Send test email based on provider
            if settings.provider == EmailProvider.SMTP.value:
                self._send_smtp_test(settings, test_email)
            elif settings.provider == EmailProvider.SENDGRID.value:
                self._send_sendgrid_test(settings, test_email)
            elif settings.provider == EmailProvider.MAILGUN.value:
                self._send_mailgun_test(settings, test_email)
            elif settings.provider == EmailProvider.SES.value:
                self._send_ses_test(settings, test_email)
            else:
                raise ValidationException(
                    message=f"Unknown provider: {settings.provider}",
                    field="provider",
                )

            # Mark as verified
            settings.mark_verified()
            db.flush()

            logger.info(f"Email settings verified for store {store_id}")
            return {
                "success": True,
                "message": f"Test email sent successfully to {test_email}",
            }

        except (ValidationException, ExternalServiceException):
            raise  # Re-raise domain exceptions
        except Exception as e:  # noqa: EXC003
            error_msg = str(e)
            settings.mark_verification_failed(error_msg)
            db.flush()

            logger.warning(f"Email verification failed for store {store_id}: {error_msg}")
            # Return error dict instead of raising - verification failure is not a server error
            return {
                "success": False,
                "message": f"Failed to send test email: {error_msg}",
            }

    def _send_smtp_test(self, settings: StoreEmailSettings, to_email: str) -> None:
        """Send test email via SMTP."""
        msg = MIMEMultipart("alternative")
        msg["Subject"] = "Orion Email Configuration Test"
        msg["From"] = f"{settings.from_name} <{settings.from_email}>"
        msg["To"] = to_email

        text_content = (
            "This is a test email from Orion.\n\n"
            "Your email settings are configured correctly!\n\n"
            f"Provider: SMTP\n"
            f"Host: {settings.smtp_host}\n"
        )
        html_content = f"""
        <html>
        <body style="font-family: Arial, sans-serif; padding: 20px;">
            <h2 style="color: #6b46c1;">Email Configuration Test</h2>
            <p>This is a test email from <strong>Orion</strong>.</p>
            <p style="color: #22c55e; font-weight: bold;">
                Your email settings are configured correctly!
            </p>
            <hr style="border: 1px solid #e5e7eb; margin: 20px 0;">
            <p style="color: #6b7280; font-size: 12px;">
                Provider: SMTP<br>
                Host: {settings.smtp_host}<br>
                Sent at: {datetime.now(UTC).strftime('%Y-%m-%d %H:%M:%S UTC')}
            </p>
        </body>
        </html>
        """

        msg.attach(MIMEText(text_content, "plain"))
        msg.attach(MIMEText(html_content, "html"))

        # Connect and send
        if settings.smtp_use_ssl:
            server = smtplib.SMTP_SSL(settings.smtp_host, settings.smtp_port)
        else:
            server = smtplib.SMTP(settings.smtp_host, settings.smtp_port)
            if settings.smtp_use_tls:
                server.starttls()

        server.login(settings.smtp_username, settings.smtp_password)
        server.sendmail(settings.from_email, to_email, msg.as_string())
        server.quit()

    def _send_sendgrid_test(self, settings: StoreEmailSettings, to_email: str) -> None:
        """Send test email via SendGrid."""
        try:
            from sendgrid import SendGridAPIClient
            from sendgrid.helpers.mail import Mail
        except ImportError:
            raise ExternalServiceException(
                service_name="SendGrid",
                message="SendGrid library not installed. Contact support.",
            )

        message = Mail(
            from_email=(settings.from_email, settings.from_name),
            to_emails=to_email,
            subject="Orion Email Configuration Test",
            html_content="""
            <html>
            <body style="font-family: Arial, sans-serif; padding: 20px;">
                <h2 style="color: #6b46c1;">Email Configuration Test</h2>
                <p>This is a test email from <strong>Orion</strong>.</p>
                <p style="color: #22c55e; font-weight: bold;">
                    Your SendGrid settings are configured correctly!
                </p>
            </body>
            </html>
            """,
        )

        sg = SendGridAPIClient(settings.sendgrid_api_key)
        response = sg.send(message)

        if response.status_code >= 400:
            raise ExternalServiceException(
                service_name="SendGrid",
                message=f"SendGrid error: HTTP {response.status_code}",
            )

    def _send_mailgun_test(self, settings: StoreEmailSettings, to_email: str) -> None:
        """Send test email via Mailgun."""
        import requests

        response = requests.post(
            f"https://api.mailgun.net/v3/{settings.mailgun_domain}/messages",
            auth=("api", settings.mailgun_api_key),
            data={
                "from": f"{settings.from_name} <{settings.from_email}>",
                "to": to_email,
                "subject": "Orion Email Configuration Test",
                "html": """
                <html>
                <body style="font-family: Arial, sans-serif; padding: 20px;">
                    <h2 style="color: #6b46c1;">Email Configuration Test</h2>
                    <p>This is a test email from <strong>Orion</strong>.</p>
                    <p style="color: #22c55e; font-weight: bold;">
                        Your Mailgun settings are configured correctly!
                    </p>
                </body>
                </html>
                """,
            },
            timeout=30,
        )

        if response.status_code >= 400:
            raise ExternalServiceException(
                service_name="Mailgun",
                message=f"Mailgun error: {response.text}",
            )

    def _send_ses_test(self, settings: StoreEmailSettings, to_email: str) -> None:
        """Send test email via Amazon SES."""
        try:
            import boto3
        except ImportError:
            raise ExternalServiceException(
                service_name="Amazon SES",
                message="boto3 library not installed. Contact support.",
            )

        client = boto3.client(
            "ses",
            region_name=settings.ses_region,
            aws_access_key_id=settings.ses_access_key_id,
            aws_secret_access_key=settings.ses_secret_access_key,
        )

        client.send_email(
            Source=f"{settings.from_name} <{settings.from_email}>",
            Destination={"ToAddresses": [to_email]},
            Message={
                "Subject": {"Data": "Orion Email Configuration Test"},
                "Body": {
                    "Html": {
                        "Data": """
                        <html>
                        <body style="font-family: Arial, sans-serif; padding: 20px;">
                            <h2 style="color: #6b46c1;">Email Configuration Test</h2>
                            <p>This is a test email from <strong>Orion</strong>.</p>
                            <p style="color: #22c55e; font-weight: bold;">
                                Your Amazon SES settings are configured correctly!
                            </p>
                        </body>
                        </html>
                        """
                    }
                },
            },
        )

    # =========================================================================
    # TIER HELPERS
    # =========================================================================

    def get_available_providers(self, tier: TierCode | None) -> list[dict]:
        """
        Get list of available email providers for a tier.

        Returns list of providers with availability status.
        """
        providers = [
            {
                "code": EmailProvider.SMTP.value,
                "name": "SMTP",
                "description": "Standard SMTP email server",
                "available": True,
                "tier_required": None,
            },
            {
                "code": EmailProvider.SENDGRID.value,
                "name": "SendGrid",
                "description": "SendGrid email delivery platform",
                "available": tier in _get_premium_tiers() if tier else False,
                "tier_required": "business",
            },
            {
                "code": EmailProvider.MAILGUN.value,
                "name": "Mailgun",
                "description": "Mailgun email API",
                "available": tier in _get_premium_tiers() if tier else False,
                "tier_required": "business",
            },
            {
                "code": EmailProvider.SES.value,
                "name": "Amazon SES",
                "description": "Amazon Simple Email Service",
                "available": tier in _get_premium_tiers() if tier else False,
                "tier_required": "business",
            },
        ]
        return providers


# Module-level service instance (singleton pattern)
store_email_settings_service = StoreEmailSettingsService()
