"""Read, write, and represent GMT (Gene Matrix Transposed) pathway files."""
from collections.abc import Sequence
from dataclasses import dataclass, field


@dataclass
class Pathway:
    """A single GMT term: ID, full name, and annotated genes."""

    id: str
    name: str
    genes: list = field(default_factory=list)


class GMT:
    """Ordered collection of Pathway objects, indexable by id or position."""

    def __init__(self, pathways=()):
        self._pathways = list(pathways)  # preserve insertion order
        self._index = {p.id: i for i, p in enumerate(self._pathways)}  # id -> position lookup
        if len(self._index) != len(self._pathways):
            raise ValueError("GMT term IDs must be unique")

    # ---- container interface --------------------------------------------------
    def __len__(self):
        return len(self._pathways)

    def __iter__(self):
        return iter(self._pathways)

    def __contains__(self, key):
        return key in self._index

    def __getitem__(self, key):
        # str -> single Pathway; int/slice/sequence -> Pathway or sub-GMT
        if isinstance(key, str):
            return self._pathways[self._index[key]]
        if isinstance(key, int) and not isinstance(key, bool):
            return self._pathways[key]
        if isinstance(key, slice):
            return GMT(self._pathways[key])
        if isinstance(key, Sequence):
            selected = []
            for k in key:
                if isinstance(k, str):
                    selected.append(self._pathways[self._index[k]])
                elif isinstance(k, int) and not isinstance(k, bool):
                    selected.append(self._pathways[k])
                else:
                    raise TypeError(f"Unsupported GMT index element: {type(k).__name__}")
            return GMT(selected)
        raise TypeError(f"Unsupported GMT index type: {type(key).__name__}")

    # ---- accessors ------------------------------------------------------------
    def ids(self):
        """Return term ids in insertion order (analogous to R's names(gmt))."""
        return [p.id for p in self._pathways]

    def names(self):
        """Return term display names in insertion order."""
        return [p.name for p in self._pathways]

    # ---- representation -------------------------------------------------------
    def __repr__(self):
        return f"GMT({len(self._pathways)} terms)"

    def __str__(self):
        lines = []
        for term in self._pathways:
            lines.append(f"{term.id} - {term.name}\n  {', '.join(term.genes)}")
        return "\n".join(lines)


def read_gmt(filename):
    """Read a GMT file and return a GMT object."""
    pathways = []
    with open(filename, "r", encoding="utf-8") as fh:
        for raw in fh:
            # Strip only line terminators; tabs separate fields, including possibly empty trailing genes.
            line = raw.rstrip("\r\n")
            if not line:
                continue
            parts = line.split("\t")
            if len(parts) < 2:
                raise ValueError(f"Malformed GMT line: {raw!r}")
            term_id = parts[0]
            name = parts[1]
            genes = [g for g in parts[2:] if g != ""]
            pathways.append(Pathway(id=term_id, name=name, genes=genes))
    return GMT(pathways)


def write_gmt(gmt, filename):
    """Write a GMT object to a tab-delimited GMT file."""
    if not is_gmt(gmt):
        raise TypeError("gmt is not a valid GMT object")
    with open(filename, "w", encoding="utf-8") as fh:
        for term in gmt:
            fh.write("\t".join([term.id, term.name, *term.genes]))
            fh.write("\n")


def is_gmt(obj):
    """Return True if obj is a GMT instance."""
    return isinstance(obj, GMT)


def make_background(gmt):
    """Return the union of all genes across all GMT terms (statistical universe)."""
    if not is_gmt(gmt):
        raise TypeError("gmt is not a valid GMT object")
    # dict.fromkeys deduplicates while preserving insertion order
    genes = [gene for term in gmt for gene in term.genes]
    return list(dict.fromkeys(genes))
