import argparse
from typing import Dict, Any, List
from importlib import import_module
from .pipeline import Pipeline
from .base import Source, Transformer, Sink
from .config_loader import load_config
from ..common.logger import get_logger


def build_component(type_name: str, params: Dict[str, Any], kind: str):
    mapping = {
        "source": {
            "http": "drip_etl.sources.http_source:HttpSource",
            "mt5": "drip_etl.sources.mt5_source:Mt5Source",
            "mt5stream": "drip_etl.sources.mt5stream_source:Mt5StreamSource",
            "okx": "drip_etl.sources.okx_source:OkxSource",
            "binance": "drip_etl.sources.binance_source:BinanceSource",
            "websocket": "drip_etl.sources.websocket_source:WebsocketSource",
        },
        "transformer": {
            "normalize": "drip_etl.transformers.normalize_transformer:NormalizeTransformer",
            "mapping": "drip_etl.transformers.mapping_transformer:MappingTransformer",
            "filter": "drip_etl.transformers.filter_transformer:FilterTransformer",
            "calculate": "drip_etl.transformers.calculate_transformer:CalculateTransformer",
        },
        "sink": {
            "csv": "drip_etl.sinks.csv_sink:CsvSink",
            "parquet": "drip_etl.sinks.parquet_sink:ParquetSink",
            "postgresql": "drip_etl.sinks.postgresql_sink:PostgreSqlSink",
            "mysql": "drip_etl.sinks.mysql_sink:MysqlSink",
        },
    }
    target = mapping[kind].get(type_name)
    if not target:
        raise ValueError(f"unknown {kind} type: {type_name}")
    module_name, cls_name = target.split(":")
    module = import_module(module_name)
    cls = getattr(module, cls_name)
    return cls(**(params or {}))


def run_from_dict(cfg: Dict[str, Any]) -> None:
    logger = get_logger("drip_etl.engine")
    source_cfg = cfg["source"]
    t_cfgs: List[Dict[str, Any]] = cfg.get("transformers", [])
    sink_cfg = cfg["sink"]
    runtime = cfg.get("runtime", {})
    source: Source = build_component(source_cfg["type"], source_cfg.get("params", {}), "source")
    transformers: List[Transformer] = []
    for tc in t_cfgs:
        transformers.append(build_component(tc["type"], tc.get("params", {}), "transformer"))
    sink: Sink = build_component(sink_cfg["type"], sink_cfg.get("params", {}), "sink")
    batch_size = int(runtime.get("batch_size", 500))
    p = Pipeline(source, transformers, sink, batch_size=batch_size)
    logger.info("run_from_config")
    p.run()


def run_from_config(path: str) -> None:
    cfg = load_config(path)
    run_from_dict(cfg)


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("-c", "--config", required=True)
    args = parser.parse_args()
    run_from_config(args.config)
