import json
import logging

from paho.mqtt.client import MQTTMessage, Client

from MQTTClient import MQTTClient
from ESP32Cam import ESP32Cam
from Notifier import Notifier


class Controller:
    def __init__(self, mqtt_client: MQTTClient, telegram_token: str) -> None:
        self.client = mqtt_client
        self.telegram_token = telegram_token
        self.cameras: dict[str, ESP32Cam] = {}
        self.notifiers: dict[str, Notifier] = {}
        self.client.set_on_message_callback(self.on_message)


    def on_message(self, client: Client, userdata,  message: MQTTMessage) -> None:
        topic = message.topic
        
        if topic == self.client.MQTT_ADD_TOPIC:
            
            data = json.loads(message.payload.decode())
            camera_id = data["camera_id"]
            chat_id = data["chat_id"]
            if camera_id not in self.cameras:
                self.cameras[camera_id] = ESP32Cam(camera_id)
                topic = "esp32/" + camera_id + "/image"
                self.client.subscribe(topic)
                self.client.subscribe_observer(self.cameras[camera_id], topic)
                self.notifiers[camera_id] = Notifier(self.telegram_token, self.cameras[camera_id].get_frame, camera_id=camera_id)
            self.notifiers[camera_id].add_chat(chat_id)
            
            self.client.publish(self.client.MQTT_OK_TOPIC, json.dumps({"camera_id": camera_id, "chat_id": chat_id, "action": "add"}))
            logging.info("Received add notification to camera %s for chat %s", camera_id, chat_id)

        elif topic == self.client.MQTT_REMOVE_TOPIC:
            data = json.loads(message.payload.decode())
            camera_id = data["camera_id"]
            chat_id = data["chat_id"]
            if camera_id in self.notifiers:
                self.notifiers[camera_id].remove_chat(chat_id)
                if len(self.notifiers[camera_id]._chat_ids) == 0:
                    self.client.unsubscribe_observer(self.cameras[camera_id], "esp32/" + camera_id + "/image")
                    self.client.unsubscribe("esp32/" + camera_id + "/image")
                    self.notifiers[camera_id].stop()
                    del self.notifiers[camera_id]
                    del self.cameras[camera_id]
                    logging.info("Removed camera %s from notifiers", camera_id)

            self.client.publish(self.client.MQTT_OK_TOPIC, json.dumps({"camera_id": camera_id, "chat_id": chat_id, "action": "remove"}))
            logging.info("Received remove notification from camera %s for chat %s", camera_id, chat_id)
        
        elif topic == self.client.MQTT_PING_TOPIC:
            logging.info("Received ping notification")
            data = {}
            for camera_id, notifier in self.notifiers.items():
                data[camera_id] = notifier._chat_ids
            self.client.publish(self.client.MQTT_PONG_TOPIC, json.dumps(data))
        else:
            self.client.notify_observers(client, message, topic)


    def run(self) -> None:
        try:
            self.client.connect()
        except ConnectionRefusedError:
            logging.error("Connection refused. Check your MQTT broker settings.")



    