import datetime
import logging
from typing import TYPE_CHECKING, Any

import msgspec
import sentry_sdk
import structlog
from csirtmu.core.temporal import TemporalClient
from nats.aio.msg import Msg

from csirtmu.beta.config import AppConfig
from csirtmu.beta.redis_structs import RedisStruct
from csirtmu.beta.rt_queue import Queue
from csirtmu.beta_temporal.constants import WorkflowReference
from csirtmu.beta_temporal.external_services.rt.structs import TransactionStruct, TransactionTypeEnum

if TYPE_CHECKING:
    from redis import Redis


logger: logging.Logger = structlog.getLogger()


class Handler:
    queue_map: dict[str, Queue]

    def __init__(self, queue_map: dict[str, Queue], client: TemporalClient, redis: "Redis[bytes]") -> None:
        self.queue_map = queue_map
        self.tc = client
        self.redis = redis
        self.config = AppConfig.get()

    def subscribe_rule_to_queue(
        self, queue_name: str, transaction_type: TransactionTypeEnum, transaction_rule: WorkflowReference
    ) -> bool:
        if queue := self.queue_map.get(queue_name):
            queue.add_rule(transaction_type, transaction_rule)
            return True
        return False

    async def process_transaction(self, msg: Msg) -> None:
        if self.config.transaction_retention.use_jetstream:
            await msg.ack()
        subject = msg.subject
        reply = msg.reply
        data = msg.data.decode()
        transaction = msgspec.json.decode(data, type=TransactionStruct)
        if (queue := self.queue_map.get(transaction.queue)) is not None and (
            workflows := queue.transaction_rules.get(transaction.type, [])
        ) != []:
            logger.debug(f"Received a message on queue={transaction.queue} subject={subject} reply={reply}: {data}")
            await self.apply_rules_on_queue(transaction.type, transaction, workflows, queue.temporal_queue)

    async def apply_rules_on_queue(
        self,
        transaction_type: TransactionTypeEnum,
        transaction: TransactionStruct,
        workflows: list[tuple[WorkflowReference, dict[str, Any] | None]],
        temporal_queue: str,
    ) -> None:
        try:
            await self.tc.start_workflow(
                WorkflowReference.HANDLE_TRANSACTION,
                args=(transaction, workflows),
                id=f"{transaction.id}|{transaction_type.name}",
                task_queue=temporal_queue,
            )
        except Exception as e:
            if self.config.monitoring is not None:
                sentry_sdk.capture_exception(e)
            logger.exception(e)
            if self.config.transaction_retention.store_failed_transactions:
                self.redis.set(
                    str(transaction.id),
                    msgspec.json.encode(
                        RedisStruct(
                            transaction=transaction,
                            timestamp=datetime.datetime.now(datetime.UTC),
                            error=str(e),
                            workflows=workflows,
                            task_queue=temporal_queue,
                        )
                    ),
                )
