"""Vulnerability mixin for MLD-related issues.

Analyzes MLDv1/v2 behavior from captured traffic to flag risks.
"""
from __future__ import annotations

import csv
import re

from ptnetinspector.utils.ip_utils import has_additional_data, is_global_unicast_ipv6


class MLDMixin:
    def store_mld_vulnerability(self) -> None:
        role_dict = self.read_role_dict(self.role_file)
        if not role_dict or not getattr(self.ipver, "ipv6", False):
            return

        if not self.time_all_file or not has_additional_data(self.time_all_file):
            return

        mode = self._filter_modes("a,a+")
        if not mode:
            return

        existing_rows = self._load_existing_rows()

        mac_pattern = re.compile(r"^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$")

        def contains_global_ipv6(packet_str: str) -> bool:
            for ipcand in re.findall(r"(?:src|dst)\s*=\s*([0-9A-Fa-f:]+)", packet_str):
                if ":" not in ipcand or mac_pattern.match(ipcand):
                    continue
                if is_global_unicast_ipv6(ipcand):
                    return True
            return False

        v1_data = {"query_found": False, "first_idx": -1, "resp_macs": set(), "glip_macs": set()}
        v2_data = {"query_found": False, "first_idx": -1, "resp_macs": set(), "glip_macs": set()}

        try:
            with open(self.time_all_file, "r", newline="") as csvfile:
                reader = csv.DictReader(csvfile)
                current_index = 0

                for row in reader:
                    packet = row.get("packet", "")
                    mac = row.get("MAC", "").strip()

                    if "version = 6" not in packet:
                        current_index += 1
                        continue

                    if not v1_data["query_found"] and "MLD - Multicast Listener Query" in packet and mac == self.smac:
                        v1_data["query_found"] = True
                        v1_data["first_idx"] = current_index

                    if not v2_data["query_found"] and "MLDv2 - Multicast Listener Query" in packet and mac == self.smac:
                        v2_data["query_found"] = True
                        v2_data["first_idx"] = current_index

                    if v1_data["query_found"] and current_index > v1_data["first_idx"]:
                        if ("MLD - Multicast Listener Report" in packet) or ("MLD - Multicast Listener Done" in packet):
                            if mac in role_dict.values():
                                v1_data["resp_macs"].add(mac)
                                if contains_global_ipv6(packet):
                                    v1_data["glip_macs"].add(mac)

                    if v2_data["query_found"] and current_index > v2_data["first_idx"]:
                        if "MLDv2 - Multicast Listener Report" in packet:
                            if mac in role_dict.values():
                                v2_data["resp_macs"].add(mac)
                                if contains_global_ipv6(packet):
                                    v2_data["glip_macs"].add(mac)

                    current_index += 1
        except (FileNotFoundError, KeyError):
            return

        new_rows = []

        self._add_mld_vulnerabilities(role_dict, v1_data, "1", mode, existing_rows, new_rows)
        self._add_mld_vulnerabilities(role_dict, v2_data, "2", mode, existing_rows, new_rows)

        self._write_vulnerabilities(new_rows)

    def _add_mld_vulnerabilities(self, role_dict, data, version: str, mode: str, existing_rows, new_rows) -> None:
        query_found = data["query_found"]
        resp_macs = data["resp_macs"]
        glip_macs = data["glip_macs"]

        device_codes = {
            "1": ("PTV-NET-IDENT-ACTIVE-6-MLDV1DEV", "PTV-NET-NET-MISCONF-6-MLD1GLIPDEV"),
            "2": ("PTV-NET-IDENT-ACTIVE-6-MLDV2DEV", "PTV-NET-NET-MISCONF-6-MLD2GLIPDEV"),
        }
        net_codes = {
            "1": ("PTV-NET-IDENT-ACTIVE-6-MLDV1", "PTV-NET-NET-MISCONF-6-MLD1GLIP"),
            "2": ("PTV-NET-IDENT-ACTIVE-6-MLDV2", "PTV-NET-NET-MISCONF-6-MLD2GLIP"),
        }

        dev_code, dev_glip_code = device_codes[version]
        net_code, net_glip_code = net_codes[version]

        if not query_found:
            for device_id, mac in role_dict.items():
                rows = [
                    (
                        str(device_id),
                        mac,
                        mode,
                        "6",
                        dev_code,
                        f"Device responds to illegitimate MLDv{version} queries",
                        "2",
                    ),
                    (
                        str(device_id),
                        mac,
                        mode,
                        "6",
                        dev_glip_code,
                        f"Device responds to MLDv{version} messages from a global address",
                        "2",
                    ),
                ]
                for row in rows:
                    self._add_vuln_if_new(row, existing_rows, new_rows)

            net_rows = [
                (
                    "Network",
                    "",
                    mode,
                    "6",
                    net_code,
                    f"Network allows the delivery of illegitimate MLDv{version} queries",
                    "2",
                ),
                (
                    "Network",
                    "",
                    mode,
                    "6",
                    net_glip_code,
                    f"Network allows the delivery of MLDv{version} messages from a global address",
                    "2",
                ),
            ]
            for row in net_rows:
                self._add_vuln_if_new(row, existing_rows, new_rows)
        else:
            for device_id, mac in role_dict.items():
                rows = [
                    (
                        str(device_id),
                        mac,
                        mode,
                        "6",
                        dev_code,
                        f"Device responds to illegitimate MLDv{version} queries",
                        "1" if mac in resp_macs else "0",
                    ),
                    (
                        str(device_id),
                        mac,
                        mode,
                        "6",
                        dev_glip_code,
                        f"Device responds to MLDv{version} messages from a global address",
                        "1" if mac in glip_macs else "0",
                    ),
                ]
                for row in rows:
                    self._add_vuln_if_new(row, existing_rows, new_rows)

            net_rows = [
                (
                    "Network",
                    "",
                    mode,
                    "6",
                    net_code,
                    f"Network allows the delivery of illegitimate MLDv{version} queries",
                    "1" if resp_macs else "0",
                ),
                (
                    "Network",
                    "",
                    mode,
                    "6",
                    net_glip_code,
                    f"Network allows the delivery of MLDv{version} messages from a global address",
                    "1" if glip_macs else "0",
                ),
            ]
            for row in net_rows:
                self._add_vuln_if_new(row, existing_rows, new_rows)
