"""Multiwfn command operators for descriptor calculation."""

from .utils import operator_combine


class MultiwfnOperators:
    """Collection of Multiwfn command operators."""

    @staticmethod
    def esp(acc: str) -> bytes:
        """Operator for ESP analysis."""
        if acc == "normal":
            return operator_combine(("12", "3", "0.5", "0", "11", "n", "-1", "-1", "q"))
        elif acc == "high":
            return operator_combine(("12", "0", "11", "n", "-1", "-1", "q"))
        elif acc == "ultra":
            return operator_combine(("12", "3", "0.1", "0", "11", "n", "-1", "-1", "q"))
        else:
            raise ValueError("ESP accuracy must be 'normal', 'high', or 'ultra'.")

    @staticmethod
    def alie(acc: str) -> bytes:
        """Operator for ALIE (average local ionization energy) analysis."""
        if acc == "normal":
            return operator_combine(("12", "2", "2", "3", "0.5", "0", "-1", "-1", "q"))
        elif acc == "high":
            return operator_combine(("12", "2", "2", "0", "-1", "-1", "q"))
        elif acc == "ultra":
            return operator_combine(("12", "2", "2", "3", "0.1", "0", "-1", "-1", "q"))

    @staticmethod
    def leae(acc: str) -> bytes:
        """Operator for LEAE (local electron affinity energy) analysis."""
        if acc == "normal":
            return operator_combine(("12", "2", "4", "3", "0.5", "0", "-1", "-1", "q"))
        elif acc == "high":
            return operator_combine(("12", "2", "4", "0", "-1", "-1", "q"))
        elif acc == "ultra":
            return operator_combine(("12", "2", "4", "3", "0.1", "0", "-1", "-1", "q"))

    @staticmethod
    def lea(acc: str) -> bytes:
        """Operator for LEA (local electron affinity) analysis."""
        if acc == "normal":
            return operator_combine(("12", "2", "-4", "3", "0.5", "0", "-1", "-1", "q"))
        elif acc == "high":
            return operator_combine(("12", "2", "-4", "0", "-1", "-1", "q"))
        elif acc == "ultra":
            return operator_combine(("12", "2", "-4", "3", "0.1", "0", "-1", "-1", "q"))

    @staticmethod
    def cdft(cdft_command: str, spin_state: str, dft_calc: bool = False) -> bytes:
        """
        Operator for CDFT (conceptual DFT) analysis.

        Args:
            calc_command: Calculation command (e.g., "b3lyp/def2tzvp")
            spin_state: Spin state string
            use_existing_wfn: Whether to use existing .wfn files
        """
        if cdft_command == "xtb" or not dft_calc:
            return operator_combine(("22", "2", "0", "q"))  # "1", f"{calc_command}", f"{spin_state}", "n",
        else:
            return operator_combine(("22", "1", f"{cdft_command}", f"{spin_state}", "y", "2", "0", "q"))

    @staticmethod
    def shape(acc: str) -> bytes:
        """Operator for molecular shape analysis."""
        if acc == "normal":
            return operator_combine((12, 2, -1, 3, 0.5, 0, 10, "g", -1, 6, -1, -1, 100, 21, "size", 0, "q", 0, "q"))
        elif acc == "high":
            return operator_combine((12, 2, -1, 0, 10, "g", -1, 6, -1, -1, 100, 21, "size", 0, "q", 0, "q"))
        elif acc == "ultra":
            return operator_combine((12, 2, -1, 3, 0.1, 0, 10, "g", -1, 6, -1, -1, 100, 21, "size", 0, "q", 0, "q"))

    @staticmethod
    def internal_coordinate() -> bytes:
        """Operator for internal coordinate extraction."""
        return operator_combine(("300", "7", "-1", "opt_struct.txt\n", "-10", "0", "q"))

    @staticmethod
    def orbit() -> bytes:
        """Operator for orbital analysis."""
        return operator_combine((0, 8, 3, "h", "l", 0, -10, "q"))

    # Atom property operators
    @staticmethod
    def hirshfeld_charge() -> bytes:
        """Operator for Hirshfeld charge analysis."""
        return operator_combine((7, 1, 1, "y", 0, "q"))

    @staticmethod
    def mulliken_charge() -> bytes:
        """Operator for Mulliken charge analysis."""
        return operator_combine((7, 5, 1, "y", 0, 0, "q"))

    @staticmethod
    def scp_charge() -> bytes:
        """Operator for SCP (stockholder partitioning) charge analysis."""
        return operator_combine((7, 7, "y", 0, "q"))

    @staticmethod
    def modified_mulliken_charge() -> bytes:
        """Operator for Modified Mulliken charge analysis."""
        return operator_combine((7, 9, "y", 0, "q"))

    @staticmethod
    def becke_charge() -> bytes:
        """Operator for Becke charge analysis."""
        return operator_combine((7, 10, 0, "y", 0, "q"))

    @staticmethod
    def adch_charge() -> bytes:
        """Operator for ADCH (atomic dipole corrected Hirshfeld) charge analysis."""
        return operator_combine((7, 11, 1, "y", 0, "q"))

    @staticmethod
    def chelg_esp_charge() -> bytes:
        """Operator for CHELPG ESP charge analysis."""
        return operator_combine((7, 12, 1, "y", 0, 0, "q"))

    @staticmethod
    def mk_esp_charge() -> bytes:
        """Operator for Merz-Singh-Kollman (MK) ESP charge analysis."""
        return operator_combine((7, 13, 1, "y", 0, 0, "q"))

    @staticmethod
    def cm5_charge() -> bytes:
        """Operator for CM5 charge analysis."""
        return operator_combine((7, 16, 1, "y", 0, "q"))

    @staticmethod
    def resp_charge() -> bytes:
        """Operator for RESP charge analysis."""
        return operator_combine((7, 18, 1, "y", 0, 0, "q"))

    @staticmethod
    def loba_oxidation() -> bytes:
        """Operator for LOBA oxidation state analysis."""
        return operator_combine((19, 1, 8, 100, 50, 0, -10, "q"))

    @staticmethod
    def atom_dipole() -> bytes:
        """Operator for atomic dipole moment analysis."""
        return operator_combine((15, 2, 2, 0, "q"))

    @staticmethod
    def total_esp(atoms: list) -> bytes:
        """
        Operator for total ESP at specific atoms.

        Args:
            atoms: List of atom indices (0-based)
        """
        return operator_combine((1,) + tuple(["a" + str(atom + 1) for atom in atoms]) + ("q", "q"))

    @staticmethod
    def energy_index(atoms: list) -> bytes:
        """
        Operator for energy index calculation at specific atoms.

        Args:
            atoms: List of atom indices (0-based)
        """
        return operator_combine((200, 12) + tuple([str(atom + 1) for atom in atoms]) + (0, 0, "q"))

    # Bond order operators
    @staticmethod
    def mayer_bond_order() -> bytes:
        """Operator for Mayer bond order analysis."""
        return operator_combine((9, 1, "y", 0, "q"))

    @staticmethod
    def wiberg_bond_order() -> bytes:
        """Operator for Wiberg bond order analysis."""
        return operator_combine((9, 3, "y", 0, "q"))

    @staticmethod
    def mulliken_bond_order() -> bytes:
        """Operator for Mulliken bond order analysis."""
        return operator_combine((9, 4, "y", 0, "q"))

    @staticmethod
    def fuzzy_bond_order() -> bytes:
        """Operator for fuzzy bond order analysis."""
        return operator_combine((9, 7, "y", 0, "q"))

    @staticmethod
    def laplacian_bond_order() -> bytes:
        """Operator for Laplacian bond order analysis."""
        return operator_combine((9, 8, "y", 0, "q"))

    @staticmethod
    def bond_dipole(bonds: list) -> bytes:
        """
        Operator for bond dipole moment calculation.

        Args:
            bond_list: List of bond tuples [(i, j), ...]
            atoms: List of atom indices to convert to 1-based
        """
        bond_pairs = [f"{b[0] + 1},{b[1] + 1}" for b in bonds]
        return operator_combine((200, 2, 2) + tuple(bond_pairs) + ("q", 0, 0, "q"))

    @staticmethod
    def wfn_convert(name: str) -> bytes:
        """Operator for wavefunction file conversion."""
        return operator_combine((100, 2, 5, f"{name}.wfn", 0, "q"))
