"""Provide result classes for connection discovery data in CR3BP.

This module provides data structures for storing and presenting the results
of connection discovery between manifolds in the Circular Restricted Three-Body
Problem (CR3BP). It includes both individual connection result records and
collection classes with convenient access and formatting methods.

The result classes provide a clean interface for accessing connection data
including transfer types, Delta-V requirements, intersection points, and
full 6D state information at connection points.

All coordinates and velocities are in nondimensional CR3BP rotating-frame units.

See Also
--------
:mod:`~hiten.algorithms.connections.base`
    Main Connection class that produces these results.
:mod:`~hiten.algorithms.connections.engine`
    Connection engine that generates result objects.
:mod:`~hiten.algorithms.connections.backends`
    Backend algorithms that compute connection data.
"""

from dataclasses import dataclass
from typing import Iterator, Literal, Sequence, Tuple

import numpy as np


@dataclass
class _ConnectionResult:
    """Store an individual connection result between two manifolds.

    This dataclass stores all the information about a single discovered
    connection between source and target manifolds, including the transfer
    type, velocity change requirement, geometric location, and full state
    information at the connection point.

    Parameters
    ----------
    kind : {"impulsive", "ballistic"}
        Type of transfer. "ballistic" for very low Delta-V transfers
        (typically ||Delta-V|| <= ballistic_tol), "impulsive" for transfers
        requiring finite velocity changes.
    delta_v : float
        Magnitude of velocity change required for the transfer, in
        nondimensional CR3BP velocity units. ||v_source - v_target||.
    point2d : tuple of float
        2D coordinates (x, y) of the connection point on the synodic section
        plane, in nondimensional CR3BP distance units.
    state_u : ndarray, shape (6,)
        6D phase space state [x, y, z, vx, vy, vz] at the connection point
        on the source (typically unstable) manifold, in nondimensional
        CR3BP units.
    state_s : ndarray, shape (6,)
        6D phase space state [x, y, z, vx, vy, vz] at the connection point
        on the target (typically stable) manifold, in nondimensional
        CR3BP units.
    index_u : int
        Index of the connection point within the source manifold's
        section intersection data.
    index_s : int
        Index of the connection point within the target manifold's
        section intersection data.

    Notes
    -----
    Connection results are typically generated by the backend algorithms
    and sorted by increasing Delta-V requirement. The classification
    into "ballistic" vs "impulsive" is based on the ballistic_tol
    parameter in the search configuration.

    The 2D point coordinates correspond to the intersection location
    on the synodic section plane, while the 6D states provide the
    full phase space information needed for trajectory propagation.

    Examples
    --------
    >>> # Accessing connection data
    >>> print(f"Transfer type: {result.kind}")
    >>> print(f"Delta-V: {result.delta_v:.6f}")
    >>> print(f"Section point: {result.point2d}")
    >>> print(f"Source state: {result.state_u}")
    >>> print(f"Target state: {result.state_s}")

    See Also
    --------
    :class:`~hiten.algorithms.connections.results.ConnectionResults`
        Collection class for multiple connection results.
    :class:`~hiten.algorithms.connections.config._SearchConfig`
        Configuration that determines ballistic vs impulsive classification.
    """
    kind: Literal["impulsive", "ballistic"]
    delta_v: float
    point2d: Tuple[float, float]
    state_u: np.ndarray
    state_s: np.ndarray
    index_u: int
    index_s: int


class ConnectionResults:
    """Provide a collection of connection results with convenient access and formatting.

    This class provides a read-only sequence-like interface over a collection
    of :class:`~hiten.algorithms.connections.results._ConnectionResult` objects, with enhanced formatting capabilities
    for analysis and presentation. It behaves like a standard Python sequence
    while providing specialized string representations optimized for connection
    data.

    Parameters
    ----------
    results : sequence of :class:`~hiten.algorithms.connections.results._ConnectionResult` or None
        Collection of connection results to wrap. If None or empty,
        creates an empty results collection.

    Notes
    -----
    This class implements the sequence protocol, supporting:
    
    - Length queries: ``len(results)``
    - Iteration: ``for result in results``
    - Indexing: ``results[i]``
    - Boolean evaluation: ``bool(results)``
    
    The string representation provides a formatted table showing key
    connection information in a compact, readable format suitable for
    analysis and reporting.

    Examples
    --------
    >>> # Create results collection
    >>> results = ConnectionResults(connection_list)
    >>> print(f"Found {len(results)} connections")
    >>> 
    >>> # Access individual results
    >>> best_connection = results[0]  # Lowest Delta-V
    >>> print(f"Best Delta-V: {best_connection.delta_v:.6f}")
    >>> 
    >>> # Iterate over results
    >>> for result in results:
    ...     if result.kind == "ballistic":
    ...         print(f"Ballistic connection: {result.delta_v:.3e}")
    >>> 
    >>> # Formatted display
    >>> print(results)  # Shows formatted table

    See Also
    --------
    :class:`~hiten.algorithms.connections.results._ConnectionResult`
        Individual connection result data structure.
    :class:`~hiten.algorithms.connections.base.Connection`
        Main class that produces these result collections.
    """

    def __init__(self, results: Sequence[_ConnectionResult] | None):
        self._results: list[_ConnectionResult] = list(results) if results else []

    # Sequence-like methods
    def __len__(self) -> int:
        return len(self._results)

    def __iter__(self) -> Iterator[_ConnectionResult]:
        return iter(self._results)

    def __getitem__(self, idx: int) -> _ConnectionResult:
        return self._results[idx]

    def __bool__(self) -> bool:
        return bool(self._results)

    def __repr__(self) -> str:
        n_total = len(self._results)
        n_ballistic = sum(1 for r in self._results if r.kind == "ballistic")
        n_impulsive = n_total - n_ballistic
        return (
            f"ConnectionResults(n={n_total}, ballistic={n_ballistic}, impulsive={n_impulsive})"
        )

    def __str__(self) -> str:
        """Return a formatted table representation of all connection results.

        Returns
        -------
        str
            Multi-line formatted table showing connection details including
            index, transfer type, Delta-V, manifold indices, and state vectors.
            Returns "<no connection results>" if the collection is empty.

        Notes
        -----
        The table format includes:
        
        - Index: Sequential numbering of results
        - Kind: Transfer type ("ballistic" or "impulsive")
        - Delta-V: Velocity change magnitude in scientific notation
        - idx_u/idx_s: Manifold intersection indices
        - state_u/state_s: 6D state vectors with limited precision
        
        State vectors are formatted with 6 decimal places for readability
        while maintaining sufficient precision for analysis.
        """
        if not self._results:
            return "<no connection results>"

        # Header
        headers = ("#", "kind", "Delta-V", "idx_u", "idx_s", "state_u", "state_s")
        rows: list[tuple[str, ...]] = [headers]

        # Build rows with limited precision for readability
        for i, r in enumerate(self._results):
            su = np.asarray(r.state_u).ravel()
            ss = np.asarray(r.state_s).ravel()
            su_str = "[" + ", ".join(f"{v:.6f}" for v in su) + "]"
            ss_str = "[" + ", ".join(f"{v:.6f}" for v in ss) + "]"
            rows.append(
                (
                    str(i),
                    r.kind,
                    f"{r.delta_v:.3e}",
                    str(r.index_u),
                    str(r.index_s),
                    su_str,
                    ss_str,
                )
            )

        # Compute column widths
        col_widths = [max(len(row[c]) for row in rows) for c in range(len(headers))]

        def fmt_row(row: tuple[str, ...]) -> str:
            return "  ".join(cell.rjust(col_widths[i]) for i, cell in enumerate(row))

        # Assemble table
        lines = [fmt_row(rows[0])]
        lines.append("  ".join("-" * w for w in col_widths))
        for row in rows[1:]:
            lines.append(fmt_row(row))

        return "\n".join(lines)
