"""property specific messaging migration data


Revision ID: 5f96f13be8ec
Revises: 2736c942faa2
Create Date: 2024-05-28 14:52:08.114674

"""

import random
import string
import uuid
from typing import Any, Dict, List

import sqlalchemy as sa
from alembic import op
from sqlalchemy import bindparam, text

# revision identifiers, used by Alembic.
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.engine import Connection, LegacyCursorResult
from sqlalchemy.exc import IntegrityError
from sqlalchemy.sql.elements import TextClause

from fides.api.alembic.migrations.helpers.database_functions import generate_record_id
from fides.api.schemas.messaging.messaging import MessagingActionType

# revision identifiers, used by Alembic.
revision = "5f96f13be8ec"
down_revision = "2736c942faa2"
branch_labels = None
depends_on = None


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.",
        },
    },
}

AUTO_MIGRATED_STRING = "auto-migrated"


def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    bind: Connection = op.get_bind()

    # STEP 1: Ensure we have exactly 1 default property in the DB
    existing_properties: LegacyCursorResult = bind.execute(
        text("select id from plus_property;")
    )
    if len(existing_properties.all()) == 1:
        # If exactly one property exists, assume this is the "default"
        bind.execute(text("UPDATE plus_property SET is_default = TRUE;"))
        only_property: LegacyCursorResult = bind.execute(
            text("select id from plus_property;")
        ).first()
        default_property_id = only_property["id"]
    else:
        # Create new property, label as default
        insert_into_property_query: TextClause = text(
            "INSERT INTO plus_property (id, name, is_default, type)"
            "VALUES (:id, :name, :is_default, :type)"
        )
        characters = string.ascii_uppercase + string.digits
        new_property_id: str = "FDS-" + "".join(random.choices(characters, k=6))
        default_property_id = new_property_id

        new_property: Dict[str, Any] = {
            "id": new_property_id,
            "name": "Default Property Autogenerated",
            "is_default": True,
            "type": "website",
        }
        try:
            bind.execute(insert_into_property_query, new_property)
        except IntegrityError as exc:
            raise Exception(
                f"Fides attempted to create a new default property but got error: {exc}. "
            )

    # STEP 2: Ensure all default templates are saved to the DB
    existing_templates: LegacyCursorResult = bind.execute(
        text("SELECT id, type from messaging_template;")
    )
    templates_from_db: List[str] = [
        template.type for template in existing_templates.all()
    ]
    for template_type, template in DEFAULT_MESSAGING_TEMPLATES.items():
        # if a template is not already in the DB, save it with defaults
        if template_type not in templates_from_db:
            insert_into_messaging_template_query: TextClause = text(
                "INSERT INTO messaging_template (id, type, content, is_enabled)"
                "VALUES (:id, :type, :content, :is_enabled)"
            )
            new_messaging_template: Dict[str, Any] = {
                "id": generate_record_id("autogenerated-mes"),
                "type": template_type,
                "content": template["content"],
                "is_enabled": False,
            }
            try:
                bind.execute(
                    insert_into_messaging_template_query.bindparams(
                        bindparam("content", type_=JSONB)
                    ),
                    new_messaging_template,
                )
            except IntegrityError as exc:
                raise Exception(
                    f"Fides attempted to create a new messaging_template but got error: {exc}. "
                )

    # STEP 3: Ensure all saved messaging templates are linked to the default property
    updated_templates: LegacyCursorResult = bind.execute(
        text("select id, type from messaging_template;")
    )
    for template in updated_templates:
        insert_into_messaging_template_to_property_query: TextClause = text(
            "INSERT INTO messaging_template_to_property (id, messaging_template_id, property_id)"
            "VALUES (:id, :messaging_template_id, :property_id)"
        )
        new_messaging_template_to_property: Dict[str, Any] = {
            "id": generate_record_id("mes"),
            "messaging_template_id": template["id"],
            "property_id": default_property_id,
        }
        bind.execute(
            insert_into_messaging_template_to_property_query,
            new_messaging_template_to_property,
        )
    # ### end Alembic commands ###


def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    # Reverse data migration: remove templates that were automatically created by the forward migration
    bind = op.get_bind()
    bind.execute(text("DELETE FROM messaging_template_to_property;"))
    bind.execute(text("DELETE FROM messaging_template WHERE id LIKE '%autogenerated%'"))
    bind.execute(
        text(
            "DELETE FROM plus_property WHERE name LIKE 'Default Property Autogenerated'"
        )
    )
    # ### end Alembic commands ###
