"""
Parser for gpfactor (C++ GP algorithm) mining results
"""

from typing import List, Dict, Any

from .base_parser import BaseFactorParser
from ..core.factor import MinedFactor
from ..core.config import AssetType, FactorType


class GPFactorParser(BaseFactorParser):
    """
    Parser for gpfactor mining results.

    Expected file format (JSON array):
    [
        {
            "expression": "sub(close)(low)",
            "fitness": 0.096,
            "depth": 1,
            "length": 1,
            "ic_mean": -0.142,
            "ic_ir": -0.612,
            "rank_ic_mean": -0.098,
            "rank_ic_ir": -0.380,
            "turnover_mean": 0.481,
            "long_short_return": -0.003,
            "factor_coverage": 1.0
        },
        ...
    ]
    """

    @property
    def framework_name(self) -> str:
        return "gpfactor"

    def parse_factor(self, data: Dict[str, Any]) -> MinedFactor:
        """Parse a single gpfactor result"""
        return MinedFactor.from_gpfactor(
            data,
            asset_type=self.asset_type,
            factor_type=self.factor_type
        )

    def parse_file_content(self, content: Dict[str, Any] | List[Dict[str, Any]]) -> List[MinedFactor]:
        """
        Parse gpfactor file content.

        The file is expected to be a JSON array of factor objects.
        """
        # gpfactor results are typically a list
        if isinstance(content, list):
            factors_data = content
        elif isinstance(content, dict):
            # Handle case where it might be wrapped in a dict
            factors_data = content.get("factors", content.get("results", []))
            if isinstance(factors_data, dict):
                factors_data = [factors_data]
        else:
            raise ValueError(f"Unexpected content type: {type(content)}")

        factors = []
        for data in factors_data:
            if not isinstance(data, dict):
                continue
            if "expression" not in data:
                continue
            try:
                factor = self.parse_factor(data)
                factors.append(factor)
            except Exception as e:
                # Skip invalid factors but log the error
                print(f"Warning: Failed to parse factor: {e}")

        return factors

    def sort_by_fitness(self, factors: List[MinedFactor], descending: bool = True) -> List[MinedFactor]:
        """Sort factors by fitness score"""
        return sorted(
            factors,
            key=lambda f: f.get_metric("fitness", 0),
            reverse=descending
        )

    def sort_by_ic(self, factors: List[MinedFactor], use_rank_ic: bool = False) -> List[MinedFactor]:
        """Sort factors by absolute IC (descending)"""
        key = "rank_ic_mean" if use_rank_ic else "ic_mean"
        return sorted(
            factors,
            key=lambda f: abs(f.get_metric(key, 0)),
            reverse=True
        )
