"""
Parser factory for automatic framework detection and parsing
"""

from typing import List, Optional, Union
from pathlib import Path
import json
import re

from .base_parser import BaseFactorParser
from .gpfactor_parser import GPFactorParser
from .masfactor_miner_parser import MasfactorMinerParser
from ..core.factor import MinedFactor
from ..core.config import AssetType, FactorType


def detect_framework(content: Union[dict, list]) -> str:
    """
    Auto-detect the source framework based on content structure.

    Args:
        content: Parsed JSON content

    Returns:
        Framework name: "gpfactor", "masfactorMiner", or "unknown"
    """
    # Convert to list for further checks
    if isinstance(content, dict):
        items = content.get("factors", content.get("results", [content]))
    else:
        items = content

    if not items or not isinstance(items[0], dict):
        return "unknown"

    sample = items[0]

    # Check for MAS Factor Miner characteristics
    if "name" in sample and "logic" in sample:
        return "masfactorMiner"

    # Check for period-based metrics used by MAS Factor Miner
    if "metrics" in sample:
        metrics = sample["metrics"]
        if isinstance(metrics, dict) and any(
            k.startswith("period_") for k in metrics.keys()
        ):
            return "masfactorMiner"

    # Check expression format for gpfactor (C++ style)
    expression = sample.get("expression", "")
    if expression:
        # gpfactor uses func(arg1)(arg2) pattern
        if re.search(r'\w+\([^()]*\)\([^()]*\)', expression):
            return "gpfactor"

        # masfactorMiner uses ts_/cs_ prefixes
        if re.search(r'\b(ts_|cs_)\w+', expression):
            return "masfactorMiner"

    # Check for gpfactor-specific fields
    if "fitness" in sample and "depth" in sample and "length" in sample:
        return "gpfactor"


    # Check for masfactorMiner-specific fields
    if "score" in sample and "direction" in sample:
        return "masfactorMiner"

    return "unknown"


def get_parser(
    framework: str,
    asset_type: AssetType = AssetType.STOCK,
    factor_type: FactorType = FactorType.DAILY
) -> BaseFactorParser:
    """
    Get the appropriate parser for a framework.

    Args:
        framework: Framework name ("gpfactor", "masfactorMiner")
        asset_type: Asset type for factors
        factor_type: Factor frequency type

    Returns:
        Parser instance

    Raises:
        ValueError: If framework is not supported
    """
    parsers = {
        "gpfactor": GPFactorParser,
        "masfactorMiner": MasfactorMinerParser,
    }

    if framework not in parsers:
        raise ValueError(
            f"Unsupported framework: {framework}. "
            f"Supported: {list(parsers.keys())}"
        )

    return parsers[framework](asset_type=asset_type, factor_type=factor_type)


def parse_factor_file(
    file_path: Union[str, Path],
    framework: Optional[str] = None,
    asset_type: AssetType = AssetType.STOCK,
    factor_type: FactorType = FactorType.DAILY
) -> List[MinedFactor]:
    """
    Parse factors from a file, auto-detecting framework if not specified.

    Args:
        file_path: Path to the JSON file
        framework: Framework name (optional, will auto-detect if not provided)
        asset_type: Asset type for factors
        factor_type: Factor frequency type

    Returns:
        List of MinedFactor instances
    """
    file_path = Path(file_path)
    if not file_path.exists():
        raise FileNotFoundError(f"Factor file not found: {file_path}")

    with open(file_path, 'r', encoding='utf-8') as f:
        content = json.load(f)

    # Auto-detect framework if not specified
    if framework is None:
        framework = detect_framework(content)
        if framework == "unknown":
            raise ValueError(
                f"Could not auto-detect framework for file: {file_path}. "
                "Please specify the framework explicitly."
            )

    parser = get_parser(framework, asset_type, factor_type)
    return parser.parse_file_content(content)


def parse_multiple_files(
    file_paths: List[Union[str, Path]],
    framework: Optional[str] = None,
    asset_type: AssetType = AssetType.STOCK,
    factor_type: FactorType = FactorType.DAILY
) -> List[MinedFactor]:
    """
    Parse factors from multiple files.

    Args:
        file_paths: List of paths to JSON files
        framework: Framework name (optional, will auto-detect for each file)
        asset_type: Asset type for factors
        factor_type: Factor frequency type

    Returns:
        Combined list of MinedFactor instances from all files
    """
    all_factors = []
    for file_path in file_paths:
        factors = parse_factor_file(
            file_path,
            framework=framework,
            asset_type=asset_type,
            factor_type=factor_type
        )
        all_factors.extend(factors)
    return all_factors
