"""
Approval rule service for managing approval workflow rules.

Handles CRUD operations and rule resolution — given a tool context,
determines which approval rule applies and who can approve.
"""

import structlog
from typing import List, Optional, Dict, Any
from sqlalchemy.orm import Session
from sqlalchemy import desc

from control_plane_api.app.models.approval_rule import ApprovalRule

logger = structlog.get_logger()


class ApprovalRuleService:
    """Service for managing approval rules."""

    def __init__(self, organization_id: str, db: Session):
        self.organization_id = organization_id
        self.db = db

    def list_rules(self, enabled_only: bool = False) -> List[ApprovalRule]:
        """List all approval rules for the org."""
        query = self.db.query(ApprovalRule).filter(
            ApprovalRule.organization_id == self.organization_id
        )
        if enabled_only:
            query = query.filter(ApprovalRule.enabled == True)
        return query.order_by(desc(ApprovalRule.priority), ApprovalRule.created_at).all()

    def get_rule(self, rule_id: str) -> Optional[ApprovalRule]:
        """Get a single rule by ID."""
        return self.db.query(ApprovalRule).filter(
            ApprovalRule.id == rule_id,
            ApprovalRule.organization_id == self.organization_id,
        ).first()

    def create_rule(self, data: Dict[str, Any]) -> ApprovalRule:
        """Create a new approval rule."""
        rule = ApprovalRule(
            organization_id=self.organization_id,
            **data,
        )
        self.db.add(rule)
        self.db.commit()
        self.db.refresh(rule)

        logger.info(
            "approval_rule_created",
            rule_id=str(rule.id),
            name=rule.name,
            scope_type=rule.scope_type,
        )
        return rule

    def update_rule(self, rule_id: str, data: Dict[str, Any]) -> Optional[ApprovalRule]:
        """Update an existing rule."""
        rule = self.get_rule(rule_id)
        if not rule:
            return None

        for key, value in data.items():
            if value is not None:
                setattr(rule, key, value)

        self.db.commit()
        self.db.refresh(rule)

        logger.info("approval_rule_updated", rule_id=str(rule.id))
        return rule

    def delete_rule(self, rule_id: str) -> bool:
        """Delete an approval rule."""
        rule = self.get_rule(rule_id)
        if not rule:
            return False

        self.db.delete(rule)
        self.db.commit()

        logger.info("approval_rule_deleted", rule_id=str(rule_id))
        return True

    def resolve_rule(
        self,
        tool_name: str,
        risk_level: Optional[str] = None,
        category: Optional[str] = None,
        user_groups: Optional[List[str]] = None,
    ) -> Dict[str, Any]:
        """
        Resolve which approval rule matches the given tool context.

        Checks rules in priority order. Returns matching rule + approver info.
        Also checks auto_approve_roles against the user's Kubiya groups
        (in Kubiya, roles are managed via group membership — e.g. being in the
        "Admin" group grants the admin role).
        """
        rules = self.list_rules(enabled_only=True)
        user_groups = user_groups or []

        for rule in rules:
            if not self._rule_matches(rule, tool_name, risk_level, category):
                continue

            # Check if user's groups allow auto-approval
            # auto_approve_roles contains group names (e.g. ["Admin"])
            auto_roles = rule.auto_approve_roles or []
            if any(group in auto_roles for group in user_groups):
                return {
                    "rule": rule,
                    "approver_emails": self._extract_approver_emails(rule),
                    "auto_approved": True,
                    "message": f"Auto-approved: user is in group matching auto_approve_roles",
                }

            return {
                "rule": rule,
                "approver_emails": self._extract_approver_emails(rule),
                "auto_approved": False,
                "message": f"Matched rule: {rule.name}",
            }

        return {
            "rule": None,
            "approver_emails": [],
            "auto_approved": False,
            "message": "No matching approval rule found",
        }

    def _rule_matches(
        self,
        rule: ApprovalRule,
        tool_name: str,
        risk_level: Optional[str],
        category: Optional[str],
    ) -> bool:
        """Check if a rule matches the given tool context."""
        if rule.scope_type == "all":
            return True
        if rule.scope_type == "tool" and rule.scope_value == tool_name:
            return True
        if rule.scope_type == "risk_level" and rule.scope_value == risk_level:
            return True
        if rule.scope_type == "category" and rule.scope_value == category:
            return True
        return False

    def _extract_approver_emails(self, rule: ApprovalRule) -> List[str]:
        """Extract approver email addresses from a rule's approver_value.

        For 'user_emails' type: approver_value is a list of emails directly.
        For 'group' type: approver_value is {"group_name": "...", "group_uuid": "...", "emails": [...]}.
          The frontend resolves group members when creating the rule and stores their emails.
        For 'role' type: similar to group — emails pre-resolved by the frontend.
        """
        if rule.approver_type == "user_emails":
            if isinstance(rule.approver_value, list):
                return rule.approver_value
            return []

        if rule.approver_type in ("group", "role"):
            if isinstance(rule.approver_value, dict):
                # Pre-resolved emails stored by the frontend
                emails = rule.approver_value.get("emails", [])
                if isinstance(emails, list):
                    return emails
            return []

        return []
