import os
import re
import json
import base64
from pathlib import Path
from typing import Optional, Tuple
from logger import mylog

# Register NetAlertX directories
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")

# Load MAC/device-type/icon rules from external file
MAC_TYPE_ICON_PATH = Path(f"{INSTALL_PATH}/back/device_heuristics_rules.json")
try:
    with open(MAC_TYPE_ICON_PATH, "r", encoding="utf-8") as f:
        MAC_TYPE_ICON_RULES = json.load(f)
    # Precompute base64-encoded icon_html once for each rule
    for rule in MAC_TYPE_ICON_RULES:
        icon_html = rule.get("icon_html", "")
        if icon_html:
            # encode icon_html to base64 string
            b64_bytes = base64.b64encode(icon_html.encode("utf-8"))
            rule["icon_base64"] = b64_bytes.decode("utf-8")
        else:
            rule["icon_base64"] = ""
except Exception as e:
    MAC_TYPE_ICON_RULES = []
    mylog("none", f"[guess_device_attributes] Failed to load device_heuristics_rules.json: {e}",)


# -----------------------------------------
# Match device type and base64-encoded icon using MAC prefix and vendor patterns.
def match_mac_and_vendor(
    mac_clean: str, vendor: str, default_type: str, default_icon: str
) -> Tuple[str, str]:
    """
    Match device type and base64-encoded icon using MAC prefix and vendor patterns.

    Args:
        mac_clean: Cleaned MAC address (uppercase, no colons).
        vendor: Normalized vendor name (lowercase).
        default_type: Fallback device type.
        default_icon: Fallback base64 icon.

    Returns:
        Tuple containing (device_type, base64_icon)
    """
    for rule in MAC_TYPE_ICON_RULES:
        dev_type = rule.get("dev_type")
        base64_icon = rule.get("icon_base64", "")
        patterns = rule.get("matching_pattern", [])

        for pattern in patterns:
            mac_prefix = pattern.get("mac_prefix", "").upper()
            vendor_pattern = pattern.get("vendor", "").lower()

            if mac_clean.startswith(mac_prefix):
                if not vendor_pattern or vendor_pattern in vendor:
                    mylog("debug", "[guess_device_attributes] Matched via MAC+Vendor")

                    type_ = dev_type
                    icon = base64_icon or default_icon
                    return type_, icon

    return default_type, default_icon


# ---------------------------------------------------
# Match device type and base64-encoded icon using vendor patterns.
def match_vendor(vendor: str, default_type: str, default_icon: str) -> Tuple[str, str]:
    vendor_lc = vendor.lower()

    for rule in MAC_TYPE_ICON_RULES:
        dev_type = rule.get("dev_type")
        base64_icon = rule.get("icon_base64", "")
        patterns = rule.get("matching_pattern", [])

        for pattern in patterns:
            # Only apply fallback when no MAC prefix is specified
            # mac_prefix = pattern.get("mac_prefix", "")
            vendor_pattern = pattern.get("vendor", "").lower()

            if vendor_pattern and vendor_pattern in vendor_lc:
                mylog("debug", "[guess_device_attributes] Matched via Vendor")

                icon = base64_icon or default_icon

                return dev_type, icon

    return default_type, default_icon


# ---------------------------------------------------
# Match device type and base64-encoded icon using name patterns.
def match_name(name: str, default_type: str, default_icon: str) -> Tuple[str, str]:
    """
    Match device type and base64-encoded icon using name patterns from global MAC_TYPE_ICON_RULES.

    Args:
        name: Normalized device name (lowercase).
        default_type: Fallback device type.
        default_icon: Fallback base64 icon.

    Returns:
        Tuple containing (device_type, base64_icon)
    """
    name_lower = name.lower() if name else ""

    for rule in MAC_TYPE_ICON_RULES:
        dev_type = rule.get("dev_type")
        base64_icon = rule.get("icon_base64", "")
        name_patterns = rule.get("name_pattern", [])

        for pattern in name_patterns:
            # Use regex search to allow pattern substrings
            if re.search(pattern, name_lower, re.IGNORECASE):
                mylog("debug", "[guess_device_attributes] Matched via Name")

                type_ = dev_type
                icon = base64_icon or default_icon
                return type_, icon

    return default_type, default_icon


# -------------------------------------------------------------------------------
#
def match_ip(ip: str, default_type: str, default_icon: str) -> Tuple[str, str]:
    """
    Match device type and base64-encoded icon using IP regex patterns from global JSON.

    Args:
        ip: Device IP address as string.
        default_type: Fallback device type.
        default_icon: Fallback base64 icon.

    Returns:
        Tuple containing (device_type, base64_icon)
    """
    if not ip:
        return default_type, default_icon

    for rule in MAC_TYPE_ICON_RULES:
        ip_patterns = rule.get("ip_pattern", [])
        dev_type = rule.get("dev_type")
        base64_icon = rule.get("icon_base64", "")

        for pattern in ip_patterns:
            if re.match(pattern, ip):
                mylog("debug", "[guess_device_attributes] Matched via IP")

                type_ = dev_type
                icon = base64_icon or default_icon
                return type_, icon

    return default_type, default_icon


# -------------------------------------------------------------------------------
# Guess device attributes such as type of device and associated device icon
def guess_device_attributes(
    vendor: Optional[str],
    mac: Optional[str],
    ip: Optional[str],
    name: Optional[str],
    default_icon: str,
    default_type: str,
) -> Tuple[str, str]:

    mylog("debug", f"[guess_device_attributes] Guessing attributes for (vendor|mac|ip|name): ('{vendor}'|'{mac}'|'{ip}'|'{name}')",)

    # --- Normalize inputs ---
    vendor = str(vendor).lower().strip() if vendor else "unknown"
    mac = str(mac).upper().strip() if mac else "00:00:00:00:00:00"
    ip = str(ip).strip() if ip else "169.254.0.0"
    name = str(name).lower().strip() if name else "(unknown)"
    mac_clean = mac.replace(":", "").replace("-", "").upper()

    # # Internet shortcut
    # if mac == "INTERNET":
    #     return ICONS.get("globe", default_icon), DEVICE_TYPES.get("Internet", default_type)

    type_ = None
    icon = None

    # --- Strict MAC + vendor rule matching from external file ---
    type_, icon = match_mac_and_vendor(mac_clean, vendor, default_type, default_icon)

    # --- Loose Vendor-based fallback ---
    if not type_ or type_ == default_type:
        type_, icon = match_vendor(vendor, default_type, default_icon)

    # --- Loose Name-based fallback ---
    if not type_ or type_ == default_type:
        type_, icon = match_name(name, default_type, default_icon)

    # --- Loose IP-based fallback ---
    if (not type_ or type_ == default_type) or (not icon or icon == default_icon):
        type_, icon = match_ip(ip, default_type, default_icon)

    # Final fallbacks
    type_ = type_ or default_type
    icon = icon or default_icon

    mylog("debug", f"[guess_device_attributes] Guessed attributes (icon|type_): ('{icon}'|'{type_}')",)
    return icon, type_


# Deprecated functions with redirects (To be removed once all calls for these have been adjusted to use the updated function)
def guess_icon(
    vendor: Optional[str],
    mac: Optional[str],
    ip: Optional[str],
    name: Optional[str],
    default: str,
) -> str:
    """
    [DEPRECATED] Guess the appropriate FontAwesome icon for a device based on its attributes.
    Use guess_device_attributes instead.

    Args:
        vendor: Device vendor name.
        mac: Device MAC address.
        ip: Device IP address.
        name: Device name.
        default: Default icon to return if no match is found.

    Returns:
        str: Base64-encoded FontAwesome icon HTML string.
    """

    icon, _ = guess_device_attributes(vendor, mac, ip, name, default, "unknown_type")
    return icon


def guess_type(
    vendor: Optional[str],
    mac: Optional[str],
    ip: Optional[str],
    name: Optional[str],
    default: str,
) -> str:
    """
    [DEPRECATED] Guess the device type based on its attributes.
    Use guess_device_attributes instead.

    Args:
        vendor: Device vendor name.
        mac: Device MAC address.
        ip: Device IP address.
        name: Device name.
        default: Default type to return if no match is found.

    Returns:
        str: Device type.
    """

    _, type_ = guess_device_attributes(vendor, mac, ip, name, "unknown_icon", default)
    return type_


# Handler for when this is run as a program instead of called as a module.
if __name__ == "__main__":
    mylog("error", "This module is not intended to be run directly.")
