import re
from collections.abc import Awaitable, Callable, Sequence
from typing import Any

from temporalio import activity, workflow

from csirtmu.beta_temporal.commons.actions import MattermostActions, RTCommonActions
from csirtmu.beta_temporal.commons.actions.temporal_common_actions import TemporalCommonActions
from csirtmu.beta_temporal.config import AppConfig
from csirtmu.beta_temporal.constants import WorkflowReference
from csirtmu.beta_temporal.external_services.mattermost.client import MattermostClient
from csirtmu.beta_temporal.external_services.rt import structs
from csirtmu.beta_temporal.external_services.rt.client import RTClient
from csirtmu.beta_temporal.external_services.rt.utils import get_prominent_contacts
from csirtmu.beta_temporal.workflows.logging import LoggedActivities, LoggedWorkflow


class HandleProminentContactActivities(LoggedActivities):
    def __init__(self, rt_client: RTClient) -> None:
        self.rt_client = rt_client

    def get_activities(self) -> Sequence[Callable[..., Any]]:
        return [self.is_prominent_contact]

    def search(self, template: str, value: str) -> bool:
        data = value.split(" <")
        value = value.replace(">", "").strip()
        if len(data) != 2:
            return bool(re.fullmatch(re.escape(template), value, re.IGNORECASE))
        val = data[1].replace(">", "").strip()
        return bool(re.fullmatch(re.escape(template), val, re.IGNORECASE))

    def test(self, template: str, values: str | list[str]) -> bool:
        if isinstance(values, str) and ", " in values:
            values = values.split(", ")
        if isinstance(values, str):
            return self.search(template, values)
        return any(self.search(template, value) for value in values)

    @activity.defn
    async def is_prominent_contact(self, transaction: structs.TransactionStruct) -> bool:
        self.log_activity()
        if transaction.interface is None or transaction.interface != "Email":
            return False
        if (ticket_data := self.rt_client.get_ticket_data(transaction)) is None:
            return False
        prominent_contacts = get_prominent_contacts()
        requesters: str | list[str]
        default: list[str] = []
        if not (requesters := ticket_data.headers.get("From", default)):
            return False
        reply_tos: str | list[str] = ticket_data.headers.get("Reply-To", default)
        ccs: str | list[str] = ticket_data.headers.get("CC", default)

        for contacts in prominent_contacts.values():
            for contact in contacts:
                if self.test(contact, requesters) or self.test(contact, reply_tos) or self.test(contact, ccs):
                    return True
        return False


class HandleProminentContact(LoggedWorkflow, workflow_name=WorkflowReference.HANDLE_PROMINENT_CONTACT):
    @classmethod
    def get_activities(cls) -> Sequence[Callable[..., Awaitable[Any]]]:
        config = AppConfig.get()
        rt_client = RTClient(config.rt)
        handle_prominent_contact = HandleProminentContactActivities(rt_client)
        mm_actions = MattermostActions(config.rt.base_url, MattermostClient(config.mattermost))
        rt_actions = RTCommonActions(rt_client)
        temporal_actions = TemporalCommonActions(config.temporal)
        return [
            *handle_prominent_contact.get_activities(),
            *mm_actions.get_activities(),
            *rt_actions.get_activities(),
            *temporal_actions.get_activities(),
        ]

    @workflow.run
    async def run(self, transaction: structs.TransactionStruct, params: dict[str, str]) -> None:
        self.log_workflow()
        config = AppConfig.get()
        if not (channel := params.get("mm_channel")):
            raise ValueError("No channel given!")
        if await workflow.execute_activity(
            HandleProminentContactActivities.is_prominent_contact,
            args=(transaction,),
            retry_policy=config.temporal.retry_policy,
            start_to_close_timeout=config.temporal.activity_timeout,
        ):
            await workflow.execute_activity(
                MattermostActions.notify_mattermost_on_priority_ticket,
                args=(
                    transaction,
                    channel,
                ),
                retry_policy=config.temporal.retry_policy,
                start_to_close_timeout=config.temporal.activity_timeout,
            )
            if transaction.type == structs.TransactionTypeEnum.CREATE:
                await workflow.execute_activity(
                    RTCommonActions.set_ticket_priority,
                    args=(1, transaction),
                    retry_policy=config.temporal.retry_policy,
                    start_to_close_timeout=config.temporal.activity_timeout,
                )
        elif config.temporal.allow_internal_cancels:
            await workflow.execute_activity(
                TemporalCommonActions.cancel_workflow,
                args=(workflow.info().workflow_id, workflow.info().task_queue),
                retry_policy=config.temporal.retry_policy,
                start_to_close_timeout=config.temporal.activity_timeout,
            )
