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

from temporalio import activity, workflow

from csirtmu.beta_temporal.commons.actions import RTCommonActions
from csirtmu.beta_temporal.commons.actions.alpha_common_actions import AlphaCommonActions
from csirtmu.beta_temporal.commons.actions.temporal_common_actions import TemporalCommonActions
from csirtmu.beta_temporal.commons.conditions import CommonConditions
from csirtmu.beta_temporal.config import AppConfig
from csirtmu.beta_temporal.constants import WorkflowReference
from csirtmu.beta_temporal.external_services.alpha.client import AlphaClient
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_custom_field_value_from_transaction
from csirtmu.beta_temporal.workflows.logging import LoggedActivities, LoggedWorkflow


class SetNetworkInfoOnIpCustomFieldActivities(LoggedActivities):
    def __init__(self, ignored_users: list[str]) -> None:
        self.ignored_users = ignored_users

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

    @activity.defn
    async def is_from_beta(self, transaction: structs.TransactionStruct) -> bool:
        return any(user == transaction.creator.id for user in self.ignored_users)

    @activity.defn
    async def get_ip_custom_field_diff(
        self, transaction: structs.TransactionStruct
    ) -> tuple[list[str], dict[str, list[str]]]:
        if (ip_cf := get_custom_field_value_from_transaction(transaction, "IP")) is not None:
            if (
                (ip_mapping_cf := get_custom_field_value_from_transaction(transaction, "IP Mapping")) is None
                or ip_mapping_cf.values == []
                or (networks := get_custom_field_value_from_transaction(transaction, "Networks")) is None
                or networks.values == []
                or (departments := get_custom_field_value_from_transaction(transaction, "Departments")) is None
                or departments.values == []
            ):
                return ip_cf.values, {}
            res: dict[str, list[str]] = {}
            added_ips = [ip for ip in ip_cf.values if all(ip not in mapping for mapping in ip_mapping_cf.values)]
            new_mappings = [mapping for mapping in ip_mapping_cf.values if any(ip in mapping for ip in ip_cf.values)]
            if set(new_mappings) != set(ip_mapping_cf.values) and new_mappings != []:
                res["IP Mapping"] = new_mappings
            new_departments = [dep for dep in departments.values if any(dep in mapping for mapping in new_mappings)] + [
                dep
                for mapping in ip_mapping_cf.values
                if (dep := mapping.split(",")[2].removeprefix("\tDepartment: ")) not in departments.values
            ]
            new_networks = [net for net in networks.values if any(net in mapping for mapping in new_mappings)] + [
                net
                for mapping in ip_mapping_cf.values
                if (net := mapping.split(",")[1].removeprefix("\tNetwork: ")) not in networks.values
            ]
            if set(new_networks) != set(networks.values) and new_networks != []:
                res["Networks"] = new_networks
            if set(new_departments) != set(departments.values) and new_departments != []:
                res["Departments"] = new_departments
            return added_ips, res
        return [], {}


class SetNetworkInfoOnIpCustomField(
    LoggedWorkflow, workflow_name=WorkflowReference.SET_NETWORK_INFO_ON_IP_CUSTOM_FIELD
):
    @classmethod
    def get_activities(cls) -> Sequence[Callable[..., Awaitable[Any]]]:
        config = AppConfig.get()
        network_activities = SetNetworkInfoOnIpCustomFieldActivities(config.rt.ignored_users)
        common_activities = CommonConditions(RTClient(config.rt))
        alpha_activities = AlphaCommonActions(AlphaClient(config.alpha))
        temporal_activities = TemporalCommonActions(config.temporal)
        return [
            *network_activities.get_activities(),
            *common_activities.get_activities(),
            *alpha_activities.get_activities(),
            *temporal_activities.get_activities(),
        ]

    @workflow.run
    async def run(self, transaction: structs.TransactionStruct) -> None:
        self.log_workflow()
        config = AppConfig.get()
        if not (
            await workflow.execute_activity(
                SetNetworkInfoOnIpCustomFieldActivities.is_from_beta,
                args=(transaction,),
                retry_policy=config.temporal.retry_policy,
                start_to_close_timeout=config.temporal.activity_timeout,
            )
        ) and (
            await workflow.execute_activity(
                CommonConditions.custom_field_changed_or_set,
                args=("IP", transaction),
                retry_policy=config.temporal.retry_policy,
                start_to_close_timeout=config.temporal.activity_timeout,
            )
        ):
            added_ips: list[str]
            custom_fields: dict[str, list[str]]
            added_ips, custom_fields = await workflow.execute_activity(
                SetNetworkInfoOnIpCustomFieldActivities.get_ip_custom_field_diff,
                args=(transaction,),
                retry_policy=config.temporal.retry_policy,
                start_to_close_timeout=config.temporal.activity_timeout,
            )
            if added_ips:
                for name, values in (
                    await workflow.execute_activity(
                        AlphaCommonActions.get_netbox_network_info,
                        args=(added_ips,),
                        retry_policy=config.temporal.retry_policy,
                        start_to_close_timeout=config.temporal.activity_timeout,
                    )
                ).items():
                    if name in custom_fields:
                        custom_fields[name] += values
                    else:
                        custom_fields[name] = values
            if custom_fields != {}:
                await workflow.execute_activity(
                    RTCommonActions.set_ip_related_custom_fields,
                    args=(
                        transaction,
                        custom_fields,
                    ),
                    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,
            )
