"""
Abstract base class for map-matching backends.

Defines a clean, high-level interface for map matching:
  1. match_trajectory -- Process a single raw GPS trajectory.
  2. match            -- Orchestrate processing across multiple trajectories.
"""

from abc import ABC, abstractmethod
import pandas as pd
from typing import Optional, TYPE_CHECKING

if TYPE_CHECKING:
    from ...core.network import Network


class BaseMatcher(ABC):
    """Interface that every map-matching backend must implement."""

    @property
    @abstractmethod
    def network(self) -> 'Network':
        """Return the walkingpandas Network that backs this matcher."""
        pass

    @abstractmethod
    def match_trajectory(
        self, trajectory: pd.DataFrame, walk_id: Optional[str] = None
    ) -> pd.DataFrame:
        """Map-match a single trajectory.

        Args:
            trajectory: DataFrame containing one walk/trajectory with at
                least ``lat``, ``lon`` columns.
            walk_id: Identifier assigned to this walk in the output.
                When ``None``, implementations should fall back to the
                ``walk_id`` column of *trajectory* if present, otherwise
                generate a unique ID automatically.

        Returns:
            DataFrame containing the snapped path in walkingpandas
            superset format (walk_id, sequence_idx, node_id, exit_edge).
        """
        pass

    def match(
        self,
        trajectories_df: pd.DataFrame,
        walk_id_col: str = "walk_id",
    ) -> pd.DataFrame:
        """Match multiple trajectories sequentially.

        Args:
            trajectories_df: DataFrame containing multiple raw GPS walks.
            walk_id_col: Column name distinguishing individual walks.

        Returns:
            Combined DataFrame of all successfully matched walks.
        """
        import logging

        logger = logging.getLogger(__name__)

        if trajectories_df.empty:
            return pd.DataFrame()

        groups = [group for _, group in trajectories_df.groupby(walk_id_col, sort=False)]
        results = []

        for group in groups:
            try:
                wid = str(group[walk_id_col].iloc[0])
                res = self.match_trajectory(group, walk_id=wid)
                if not res.empty:
                    results.append(res)
            except Exception as e:
                wid = group[walk_id_col].iloc[0]
                logger.warning(f"Failed to match walk '{wid}': {e}")

        if not results:
            return pd.DataFrame()

        return pd.concat(results, ignore_index=True)