from __future__ import annotations

import logging
from typing import Set, Tuple, Dict, Optional

import safe_radix32
from clarabot_nano_common.common.config.extract.data_dir import DataDirExtractedConfig
from clarabot_nano_common.common.config.extract.drive_chroot import DriveChrootExtractedConfig
from clarabot_nano_common.common.config.extract.secrets import SecretsExtractedConfig
from clarabot_nano_common.utility.path.chroot import ChrootPath

from clarabot_nano.common.config.extract.admin_password import AdminPasswordExtractedConfig
from clarabot_nano.common.config.extract.serves import ServesExtractedConfig

logger = logging.getLogger(__name__)


class GatewayConfig(
    ServesExtractedConfig,
    DataDirExtractedConfig,
    SecretsExtractedConfig,
    AdminPasswordExtractedConfig,
    DriveChrootExtractedConfig,
):
    """
    Configurations relevant to the job of the gateway are extracted from the core-config here.
    NOTE: Extra configs are also selected for the few handlers that run in the gateway.
    """

    def __init__(self, config):
        ServesExtractedConfig.__init__(self, config)
        DataDirExtractedConfig.__init__(self, config)
        SecretsExtractedConfig.__init__(self, config)
        AdminPasswordExtractedConfig.__init__(self, config)
        DriveChrootExtractedConfig.__init__(self, config)
        self.name = config["remote"]["name"]  # str of moderate length
        self.backup_interval = config["remote"]["backup_interval"]
        self.room_blocks = config["remote"]["room_blocks"]
        self.vendor = config["state"].get("vendor")
        self.license = config["state"].get("license")
        self.device_id = config["state"].get("device_id")
        self.license_local_validity = config["state"].get("license_local_validity") or ""
        self.hook_vendor_data = config["local"].get("hook_vendor_data")

    def get_drive_backups(self) -> Dict[str, Tuple[int, Set[str]]]:
        served_room_ids = set(x["id"] for x in self.get_serves())
        # NOTE: in case multiple rooms are assigned to the same drive path, there is no difference in using any of
        # them for backup. (currently only the internal lock request checks for the room to be served at least)
        drive_backups = dict()  # path: (room_id, {backup_room_id, ...})
        for room_id, room_apis in self.rooms.items():
            for resource in room_apis.values():  # NOTE: all resources are drives currently, so no filtering
                backups = set()
                for packed_backup_id in resource.get("backups", []):
                    try:
                        backup_id = safe_radix32.decode(packed_backup_id)
                    except (UnicodeEncodeError, OverflowError):
                        logger.warning("Ignoring invalid room-id for backup: %s", packed_backup_id)
                        continue
                    if backup_id and backup_id not in served_room_ids:  # skip rooms that are served by current Nano
                        backups.add(safe_radix32.encode_fw(backup_id))
                if backups:
                    path = resource["path"]
                    if path not in drive_backups:
                        drive_backups[path] = (room_id, backups)
                    else:
                        drive_backups[path][1].update(backups)
        return drive_backups

    def resolve_chroot_of_room(self, room_id: int) -> Optional[ChrootPath]:
        if (
            (room_apis := self.rooms.get(room_id))
            and (drive := room_apis.get(self.effective_drive_api))
            and isinstance(drive_path := drive.get("path"), str)
        ):
            return self.resolve_chroot(drive_path)

    def _matches(self, other: GatewayConfig) -> bool:
        return (
            self.name == other.name
            and self.backup_interval == other.backup_interval
            and self.room_blocks == other.room_blocks
            and self.vendor == other.vendor
            and self.license == other.license
            and self.device_id == other.device_id
            and self.license_local_validity == other.license_local_validity
            and ServesExtractedConfig._matches(self, other)
            and DataDirExtractedConfig._matches(self, other)
            and SecretsExtractedConfig._matches(self, other)
            and AdminPasswordExtractedConfig._matches(self, other)
            and DriveChrootExtractedConfig._matches(self, other)
        )
