from dataclasses import dataclass
from pathlib import Path
from typing import Any, Dict, List, cast

from fugue.lib import types
from fugue.lib.interface import Interface
from fugue.lib.peer import Peer


@dataclass
class Tunnel:
    """
    An interface and list of peers.
    """

    name: str
    interface: Interface
    peers: List[Peer]

    @staticmethod
    def from_conf(fp_conf: Path) -> "Tunnel":
        """
        Return a Tunnel object from an INI configuration file.
        """
        with open(fp_conf, "r") as fd:
            conf_lines = fd.readlines()

        # init vars
        interface: Interface
        peers: List[Peer] = []

        # parsing
        in_peer_section = False
        in_interface_section = False
        # accumulator
        acc: Dict[str, Any] = {}

        conf_lines_cleaned = [
            line.replace("\n", "").strip()  # newline chars & strip
            for line in conf_lines
        ]
        conf_lines_cleaned = [
            line
            for line in conf_lines_cleaned
            # empty lines & comments, except for Name line
            if line and (not line.startswith("#") or line.startswith("# Name"))
        ]

        for line in conf_lines_cleaned:
            # init
            if not in_interface_section and not in_peer_section:
                if line.startswith("[Interface]"):
                    in_interface_section = True

                elif line.startswith("[Peer]"):
                    in_peer_section = True

            # parse an interface section
            elif in_interface_section:
                # error: finding another interface section
                if line.startswith("[Interface]"):
                    raise RuntimeError(
                        f"error: tunnel file: '{fp_conf}' defines multiple interface sections"
                    )

                # stop condition: entering [Peer] section
                elif line.startswith("[Peer]"):
                    # build previous interface from accumulator
                    interface = Interface.from_dict(
                        cast(types.InterfaceDict, acc)
                    )
                    # reset accumulator
                    acc = {}
                    # update boolean flags
                    in_peer_section = True
                    in_interface_section = False

                # error: unrecognised section
                elif line.startswith("["):
                    raise RuntimeError(
                        f"error: unrecognised section: '{line}' in interface file: '{fp_conf}'"
                    )

                else:
                    # update accumulator with [Interface] section line
                    acc = Interface.parse_conf_line(acc, line)

            # parse a peer section
            elif in_peer_section:
                # stop condition: entering an [Interface] section
                if line.startswith("[Interface]"):
                    # build previous peer from accumulator
                    peers.append(Peer.from_dict(cast(types.PeerDict, acc)))
                    # reset accumulator
                    acc = {}
                    # update boolean flags
                    in_peer_section = False
                    in_interface_section = True

                # stop condition: entering [Peer] section
                elif line.startswith("[Peer]"):
                    # build previous peer from accumulator
                    peers.append(Peer.from_dict(cast(types.PeerDict, acc)))
                    # reset accumulator
                    acc = {}
                    # update boolean flags
                    in_peer_section = True
                    in_interface_section = False

                # error: unrecognised section
                elif line.startswith("["):
                    raise RuntimeError(
                        f"error: unrecognised section: '{line}' in interface file: '{fp_conf}'"
                    )

                else:
                    # update accumulator with [Peer] section line
                    acc = Peer.parse_conf_line(acc, line)

        if in_interface_section:
            interface = Interface.from_dict(cast(types.InterfaceDict, acc))

        elif in_peer_section:
            peers.append(Peer.from_dict(cast(types.PeerDict, acc)))

        return Tunnel(interface.name, interface, peers)

    def as_ini(self) -> str:
        ini_str: str = self.interface.as_ini(print_header=False)

        for peer in self.peers:
            ini_str += "\n"
            ini_str += peer.as_ini(print_header=False)

        return ini_str

    def add_peer(self, peer: Peer) -> None:
        self.peers.append(peer)
        # TODO: is materialized? If so, concatenate to file? Or rewrite the file?
        # TODO: is running?

    # TODO: Anything to do with symlink should be moved to <class FS()>
    # I think this covers ONLY "promoting" tunnels; if they are NOT written
    # under a "/<wg>/fugue" directory, in which case the whole promoting thing
    # would be moot.
    def is_promoted(self) -> bool:
        raise NotImplementedError
        # A tunnel is considered promoted if there's a symlink to it under
        # /etc/wireguard/, e.g. it can be managed with wg-quick.
        # return os.path.islink(
        #     Path(self.config.WG_ROOT, f"{self.name}.conf")
        # )

    def promote(self) -> None:
        raise NotImplementedError
        # if not self.is_promoted():
        #     os.symlink(
        #         self.fp_conf,
        #         Path(self.config.WG_ROOT, f"{self.name}.conf"),
        #     )

    def demote(self) -> None:
        raise NotImplementedError
        # if self.is_promoted():
        #     os.unlink(Path(self.config.WG_ROOT, f"{self.name}.conf"))

    def is_enabled(self) -> bool:
        raise NotImplementedError

    def is_running(self) -> bool:
        raise NotImplementedError
        # status: str = wg.status()
        # interface_lines: List[str] = [
        #     line.strip() for line in status.split("\n")
        # ]

        # # NOTE: we don't wanna use systemctl to check if a tunnel is up anymore, see todo notes
        # # status: str = systemctl.status(f'wg-quick@{self.name}.service')
        # # status_lines: List[str] = [line.strip() for line in status.split('\n')]
        # # active_line: str = [line for line in status_lines if line.startswith('Active:')].pop()

        # return f"interface: {self.name}" in interface_lines

    def start(self):
        raise NotImplementedError
        # if self.is_running():
        #     return

        # if not self.is_promoted():
        #     self.promote()

        # # NOTE: we don't wanna use systemctl to start/stop tunnels, but wg-quick
        # # systemctl.start(f"wg-quick@{self.name}.service")
        # wg.quick("up", f"{self.name}")

    def stop(self):
        raise NotImplementedError
        # if self.is_running():
        #     # NOTE: we don't wanna use systemctl to start/stop tunnels, but wg-quick
        #     # systemctl.stop(f"wg-quick@{self.name}.service")
        #     wg.quick("down", f"{self.name}")

    def enable(self):
        raise NotImplementedError
        # if not self.is_promoted(self.config):
        #     self.promote()
        # systemctl.enable(f"wg-quick@{self.name}.service", now=True)

    def disable(self):
        raise NotImplementedError
        # if not self.is_enabled():
        #     return

        # if not self.is_promoted():
        #     self.promote()
        # systemctl.disable(f"wg-quick@{self.name}.service", now=True)
