# [DEF:backend.src.services.notifications.service:Module]
#
# @TIER:      CRITICAL
# @SEMANTICS: notifications, service, routing, dispatch, background-tasks
# @PURPOSE:   Orchestrates notification routing based on user preferences and policy context.
# @LAYER:     Domain
# @RELATION:  DEPENDS_ON -> backend.src.services.notifications.providers
# @RELATION:  DEPENDS_ON -> backend.src.services.profile_service
# @RELATION:  DEPENDS_ON -> backend.src.models.llm
#
# @INVARIANT: Notifications are dispatched asynchronously via BackgroundTasks.
# @INVARIANT: Missing profile or provider config must not crash the pipeline.

from typing import Any, Dict, List, Optional
from fastapi import BackgroundTasks
from sqlalchemy.orm import Session

from ...core.logger import logger, belief_scope
from ...core.config_manager import ConfigManager
from ...models.llm import ValidationRecord, ValidationPolicy
from ...models.profile import UserDashboardPreference
from .providers import SMTPProvider, TelegramProvider, SlackProvider, NotificationProvider


# [DEF:NotificationService:Class]
# @PURPOSE: Routes validation reports to appropriate users and channels.
class NotificationService:
    def __init__(self, db: Session, config_manager: ConfigManager):
        self.db = db
        self.config_manager = config_manager
        self._providers: Dict[str, NotificationProvider] = {}
        self._initialized = False

    def _initialize_providers(self):
        if self._initialized:
            return
        
        # In a real implementation, we would fetch these from NotificationConfig model
        # For now, we'll use a placeholder initialization logic
        # T033 will implement the UI/API for this.
        configs = self.config_manager.get_payload().get("notifications", {})
        
        if "smtp" in configs:
            self._providers["SMTP"] = SMTPProvider(configs["smtp"])
        if "telegram" in configs:
            self._providers["TELEGRAM"] = TelegramProvider(configs["telegram"])
        if "slack" in configs:
            self._providers["SLACK"] = SlackProvider(configs["slack"])
            
        self._initialized = True

    async def dispatch_report(
        self, 
        record: ValidationRecord, 
        policy: Optional[ValidationPolicy] = None,
        background_tasks: Optional[BackgroundTasks] = None
    ):
        """
        Route a validation record to owners and custom channels.
        @PRE: record is persisted.
        @POST: Dispatches async tasks for each resolved target.
        """
        with belief_scope("NotificationService.dispatch_report", f"record_id={record.id}"):
            self._initialize_providers()
            
            # 1. Determine if we should notify based on status and policy
            should_notify = self._should_notify(record, policy)
            if not should_notify:
                logger.reason(f"[REASON] Notification skipped for record {record.id} (status={record.status})")
                return

            # 2. Resolve targets (Owners + Custom Channels)
            targets = self._resolve_targets(record, policy)
            
            # 3. Dispatch
            subject = f"Dashboard Health Alert: {record.status}"
            body = self._build_body(record)
            
            for channel_type, recipient in targets:
                provider = self._providers.get(channel_type)
                if not provider:
                    logger.warning(f"[NotificationService][EXPLORE] Unsupported or unconfigured channel: {channel_type}")
                    continue
                
                if background_tasks:
                    background_tasks.add_task(provider.send, recipient, subject, body)
                else:
                    # Fallback to sync for tests or if no background_tasks provided
                    await provider.send(recipient, subject, body)

    def _should_notify(self, record: ValidationRecord, policy: Optional[ValidationPolicy]) -> bool:
        condition = policy.alert_condition if policy else "FAIL_ONLY"
        
        if condition == "ALWAYS":
            return True
        if condition == "WARN_AND_FAIL":
            return record.status in ("WARN", "FAIL")
        return record.status == "FAIL"

    def _resolve_targets(self, record: ValidationRecord, policy: Optional[ValidationPolicy]) -> List[tuple]:
        targets = []
        
        # Owner routing
        if not policy or policy.notify_owners:
            owners = self._find_dashboard_owners(record)
            for owner_pref in owners:
                if not owner_pref.notify_on_fail:
                    continue
                
                if owner_pref.telegram_id:
                    targets.append(("TELEGRAM", owner_pref.telegram_id))
                
                email = owner_pref.email_address or getattr(owner_pref.user, "email", None)
                if email:
                    targets.append(("SMTP", email))

        # Custom channels from policy
        if policy and policy.custom_channels:
            for channel in policy.custom_channels:
                # channel format: {"type": "SLACK", "target": "#alerts"}
                targets.append((channel.get("type"), channel.get("target")))
                
        return targets

    def _find_dashboard_owners(self, record: ValidationRecord) -> List[UserDashboardPreference]:
        # This is a simplified owner lookup. 
        # In a real scenario, we'd query Superset for owners, then match them to our UserDashboardPreference.
        # For now, we'll return all users who have bound this dashboard's environment and have a username.
        
        # Placeholder: return all preferences that have a superset_username
        # (In production, we'd filter by actual ownership from Superset metadata)
        return self.db.query(UserDashboardPreference).filter(
            UserDashboardPreference.superset_username != None
        ).all()

    def _build_body(self, record: ValidationRecord) -> str:
        return (
            f"Dashboard ID: {record.dashboard_id}\n"
            f"Environment: {record.environment_id}\n"
            f"Status: {record.status}\n\n"
            f"Summary: {record.summary}\n\n"
            f"Issues found: {len(record.issues)}"
        )
# [/DEF:NotificationService:Class]

# [/DEF:backend.src.services.notifications.service:Module]