from __future__ import annotations

from typing import Any, Dict, List, Optional, Type

from pydantic import ConfigDict
from sqlalchemy import Boolean, Column, String
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.ext.mutable import MutableDict
from sqlalchemy.orm import RelationshipProperty, Session, relationship

from fides.api.db.base_class import Base
from fides.api.models.property import Property
from fides.api.schemas.messaging.messaging import MessagingActionType

# Provides default values for initializing the database or replacing deleted values for messaging templates.
# Note: There are additional MessagingActionTypes that are internally used but are not exposed for user customization.
DEFAULT_MESSAGING_TEMPLATES: Dict[str, Any] = {
    MessagingActionType.SUBJECT_IDENTITY_VERIFICATION.value: {
        "label": "Subject identity verification",
        "content": {
            "subject": "Your one-time code is __CODE__",
            "body": "Your privacy request verification code is __CODE__. Please return to the Privacy Center and enter the code to continue. This code will expire in __MINUTES__ minutes.",
        },
    },
    MessagingActionType.PRIVACY_REQUEST_RECEIPT.value: {
        "label": "Privacy request received",
        "content": {
            "subject": "Your privacy request has been received",
            "body": "Your privacy request has been received. We will get back to you shortly.",
        },
    },
    MessagingActionType.PRIVACY_REQUEST_REVIEW_APPROVE.value: {
        "label": "Privacy request approved",
        "content": {
            "subject": "Your privacy request has been approved",
            "body": "Your privacy request has been approved and is currently processing.",
        },
    },
    MessagingActionType.PRIVACY_REQUEST_REVIEW_DENY.value: {
        "label": "Privacy request denied",
        "content": {
            "subject": "Your privacy request has been denied",
            "body": "Your privacy request has been denied. __DENIAL_REASON__.",
        },
    },
    MessagingActionType.PRIVACY_REQUEST_COMPLETE_ACCESS.value: {
        "label": "Access request completed",
        "content": {
            "subject": "Your data is ready to be downloaded",
            "body": "Your access request has been completed and can be downloaded at __DOWNLOAD_LINK__. For security purposes, this secret link will expire in __DAYS__ days.",
        },
    },
    MessagingActionType.PRIVACY_REQUEST_COMPLETE_DELETION.value: {
        "label": "Erasure request completed",
        "content": {
            "subject": "Your data has been deleted",
            "body": "Your erasure request has been completed.",
        },
    },
    MessagingActionType.MANUAL_TASK_DIGEST.value: {
        "label": "Manual task digest",
        "content": {
            "subject": "Weekly DSR Summary from __ORGANIZATION_NAME__",
            "body": "This is your weekly summary of open data subject requests tasks from __ORGANIZATION_NAME__ that require your attention. Please review the tasks in our Privacy Center to ensure timely completion.",
        },
    },
    MessagingActionType.EXTERNAL_USER_WELCOME.value: {
        "label": "External user welcome",
        "content": {
            "subject": "Welcome to our Privacy Center",
            "body": "An account has been created for you by the privacy team at __ORG_NAME__ so that you can assist them in responding to data subject requests, as required by the GDPR and many US state laws, for users that have data in your systems. A portal is available to view and take action on these requests at: __PORTAL_LINK__. Through this portal you can upload or provide user data for users who have requested a copy of their information and attest that users have been removed from your system in the case of an erasure request. The provided link is valid for 30 days and you will be asked to verify your email address each time you access the portal. If you have any questions, please reach out first to your contacts at: __ORG_NAME__",
        },
    },
}


def _link_properties_to_messaging_template(
    db: Session,
    properties: List[Dict[str, Any]],
    messaging_template: MessagingTemplate,
) -> Optional[List[Property]]:
    """
    Link supplied properties to MessagingTemplate and unlink any properties not supplied.
    """
    new_properties = []
    if len(properties) > 0:
        new_properties = (
            db.query(Property)
            .filter(Property.id.in_([prop["id"] for prop in properties]))
            .all()
        )
    messaging_template.properties = new_properties
    messaging_template.save(db)
    return messaging_template.properties


class MessagingTemplate(Base):
    @declared_attr
    def __tablename__(self) -> str:
        return "messaging_template"

    type = Column(String, index=True, nullable=False)
    content = Column(MutableDict.as_mutable(JSONB), nullable=False)
    properties: RelationshipProperty[List[Property]] = relationship(
        "Property",
        secondary="messaging_template_to_property",
        back_populates="messaging_templates",
        lazy="selectin",
    )
    is_enabled = Column(Boolean, default=False, nullable=False)
    model_config = ConfigDict(from_attributes=True)

    @classmethod
    def create(
        cls: Type[MessagingTemplate],
        db: Session,
        *,
        data: dict[str, Any],
        check_name: bool = True,
    ) -> MessagingTemplate:
        """
        Creates a Messaging Template, allows linking properties
        """
        properties = data.pop("properties", [])
        messaging_template: MessagingTemplate = super().create(
            db=db, data=data, check_name=check_name
        )
        # Link Properties to this Messaging Template via the MessagingTemplateToProperty table
        _link_properties_to_messaging_template(db, properties, messaging_template)

        return messaging_template

    def update(self, db: Session, *, data: dict[str, Any]) -> MessagingTemplate:
        """
        Updates a Messaging Template, allows linking properties
        """
        properties = data.pop("properties", [])
        super().update(db=db, data=data)

        # Link Properties to this Messaging Template via the MessagingTemplateToProperty table
        _link_properties_to_messaging_template(db, properties, self)
        return self
