from typing import Iterable, Any, Dict, Optional
from datetime import datetime
from ..core.base import Source
from ..common.logger import get_logger
import platform
import pandas as pd

logger = get_logger(__name__)


class Mt5Source(Source):
    def __init__(self,
                 symbol: str = "XAUUSD",
                 period: str = "M1",
                 start_time: Optional[str] = None,
                 end_time: Optional[str] = None,
                 limit: int = 100,
                 login: int = 0,
                 password: str = "",
                 server: str = "") -> None:
        self.symbol = symbol
        self.period = period
        self.start_time = start_time
        self.end_time = end_time
        self.limit = limit
        self.login = login
        self.password = password
        self.server = server
        self.platform = platform.system()
        
        if self.platform != "Windows":
             logger.warning("MT5 source usually requires Windows to run the terminal.")

    def read(self) -> Iterable[Dict[str, Any]]:
        try:
            # Delayed import to avoid hard dependency on non-Windows
            from ..common.mt5_conn import MT5Connector, MT5Config, MT5Period
        except ImportError as e:
            logger.error(f"Required dependencies for MT5 not found: {e}. Please install 'drip-etl[mt5]'.")
            raise

        config = MT5Config(
            login=self.login,
            password=self.password,
            server=self.server
        )

        try:
            with MT5Connector(config) as conn:
                # Map period string to MT5Period enum
                try:
                    period_enum = getattr(MT5Period, self.period)
                    period_value = period_enum.value
                except AttributeError:
                    logger.warning(f"Unknown period {self.period}, defaulting to M1")
                    from ..common.mt5_conn import MT5Period # ensure it's available
                    period_value = MT5Period.M1.value

                if self.start_time:
                    # Range mode
                    try:
                        date_from = datetime.fromisoformat(self.start_time)
                        now = datetime.now()
                        if self.end_time:
                            date_to = min(datetime.fromisoformat(self.end_time), now)
                        else:
                            date_to = now
                            
                        df = conn.get_kline_range(self.symbol, period=period_value, date_from=date_from, date_to=date_to)
                    except ValueError as e:
                        logger.error(f"Invalid date format (use 'YYYY-MM-DD HH:MM:SS'): {e}")
                        raise
                else:
                    # Count mode (fallback)
                    df = conn.get_kline_data(self.symbol, period=period_value, count=self.limit)
                
                if df is not None and not df.empty:
                    logger.info(f"Retrieved {len(df)} records from MT5 for {self.symbol}")
                    # Convert time to datetime if it's integer timestamp
                    if pd.api.types.is_integer_dtype(df['time']):
                        df['time'] = pd.to_datetime(df['time'], unit='s')
                    
                    records = df.to_dict('records')
                    for record in records:
                        # Inject period if missing
                        if 'period' not in record:
                            record['period'] = self.period
                        yield record
                else:
                    logger.warning(f"No data received for {self.symbol}")

        except Exception as e:
            logger.error(f"Error reading from MT5: {e}")
            raise
