"""
EPİAŞ Market Data Client

This module provides a client for fetching electricity market data from EPİAŞ
(Turkish Energy Exchange) transparency platform.
"""

import requests
import pandas as pd
from datetime import datetime, timedelta
from typing import Union, Tuple, Optional, List, Dict


class EpiasClient:
    """
    Client for accessing EPİAŞ (Turkish Electricity Market) transparency data.
    
    This client handles authentication and data retrieval from the EPİAŞ
    transparency platform.
    
    Args:
        email (str): EPİAŞ account email
        password (str): EPİAŞ account password
    
    Example:
        >>> client = EpiasClient("your@email.com", "your_password")
        >>> data = client.get_market_clearing_price(period="today")
    """
    
    def __init__(self, email: str, password: str):
        """Initialize the EPİAŞ client with credentials."""
        self.email = email
        self.password = password
        self.base_url = "https://seffaflik.epias.com.tr/electricity-service/v1"
        self.auth_url = "https://giris.epias.com.tr/cas/v1/tickets"
        self.mcp_endpoint = f"{self.base_url}/markets/dam/data/mcp"
        self._tgt = None

    def _get_ticket_granting_ticket(self) -> Optional[str]:
        """
        Authenticate with EPİAŞ and obtain a TGT (Ticket Granting Ticket).
        
        Returns:
            str: The TGT token if authentication is successful, None otherwise.
        
        Raises:
            Exception: If authentication fails.
        """
        try:
            response = requests.post(
                self.auth_url,
                data={"username": self.email, "password": self.password},
                headers={
                    "Content-Type": "application/x-www-form-urlencoded",
                    "Accept": "text/plain"
                }
            )
            
            if response.status_code == 201:
                self._tgt = response.text
                return self._tgt
            else:
                raise Exception(
                    f"Authentication failed! Status Code: {response.status_code}"
                )
        except Exception as e:
            print(f"Authentication error: {e}")
            return None

    def _fetch_raw_data(
        self, 
        start_date: datetime, 
        end_date: datetime
    ) -> List[Dict]:
        """
        Fetch raw data from EPİAŞ for a specific date range.
        
        Fetches data day by day to avoid API limitations.
        
        Args:
            start_date (datetime): Start date for data retrieval
            end_date (datetime): End date for data retrieval
        
        Returns:
            list: List of raw data items from the API
        """
        if not self._tgt:
            self._get_ticket_granting_ticket()
        
        headers = {
            "TGT": self._tgt,
            "Content-Type": "application/json"
        }
        
        all_items = []
        current_date = start_date.replace(hour=0, minute=0, second=0, microsecond=0)
        end_date_normalized = end_date.replace(hour=0, minute=0, second=0, microsecond=0)
        
        # Fetch day by day
        while current_date <= end_date_normalized:
            # For today's partial data, use current time as end
            if current_date.date() == datetime.now().date():
                end_time = end_date.strftime("%Y-%m-%dT%H:%M:%S+03:00")
            else:
                end_time = current_date.strftime("%Y-%m-%dT23:59:59+03:00")
            
            payload = {
                "startDate": current_date.strftime("%Y-%m-%dT00:00:00+03:00"),
                "endDate": end_time,
                "page": {"number": 1, "size": 2000}
            }
            
            try:
                response = requests.post(
                    self.mcp_endpoint, 
                    json=payload, 
                    headers=headers
                )
                
                if response.status_code == 200:
                    items = response.json().get('items', [])
                    all_items.extend(items)
            except Exception as e:
                print(f"Data retrieval error for {current_date.date()}: {e}")
            
            current_date += timedelta(days=1)
        
        return all_items

    def download(
        self,
        period: Union[str, Tuple[datetime, datetime]] = "today",
        output_format: str = "dataframe"
    ) -> Union[pd.DataFrame, str, None]:
        """
        Download Market Clearing Price (MCP) data from EPİAŞ.
        
        Args:
            period (str or tuple): Time period for data retrieval.
                - "today": Latest available data (yesterday)
                - "week": Last 7 days
                - "month": Last 30 days
                - (start_date, end_date): Custom date range as tuple of datetime objects
            output_format (str): Output format - "dataframe" or "json"
        
        Returns:
            pd.DataFrame or str or None: Market data in requested format
        
        Raises:
            ValueError: If an invalid period is specified
        
        Example:
            >>> client = EpiasClient("email@example.com", "password")
            >>> # Get latest data
            >>> df = client.download(period="today")
            >>> # Get custom date range
            >>> from datetime import datetime
            >>> df = client.download(
            ...     period=(datetime(2024, 1, 1), datetime(2024, 1, 10))
            ... )
        """
        end_date = datetime.now()
        
        if period == "today":
            # Today's data up to current hour
            start_date = end_date.replace(hour=0, minute=0, second=0, microsecond=0)
        elif period == "week":
            # Last 7 full days
            end_date = end_date - timedelta(days=1)
            start_date = end_date - timedelta(days=6)
        elif period == "month":
            # Last 30 full days
            end_date = end_date - timedelta(days=1)
            start_date = end_date - timedelta(days=29)
        elif isinstance(period, tuple) and len(period) == 2:
            start_date, end_date = period
        else:
            raise ValueError(
                "Invalid period! Use 'today', 'week', 'month' or "
                "(start_date, end_date) tuple."
            )

        raw_items = self._fetch_raw_data(start_date, end_date)
        
        if not raw_items:
            return None

        # Process the data
        df = pd.DataFrame(raw_items)
        
        # Convert numeric columns
        numeric_cols = ['price', 'priceEur', 'priceUsd']
        for col in numeric_cols:
            if col in df.columns:
                df[col] = pd.to_numeric(df[col], errors='coerce')
        
        # Convert date column
        if 'date' in df.columns:
            df['date'] = pd.to_datetime(df['date']).dt.tz_localize(None)

        if output_format == "json":
            return df.to_json(orient='records', date_format='iso')
        
        return df

    def stats(
        self,
        period: Union[str, Tuple[datetime, datetime]] = "today"
    ) -> Optional[Dict[str, float]]:
        """
        Get statistical summary of market clearing prices.
        
        Args:
            period (str or tuple): Time period (same as download)
        
        Returns:
            dict: Dictionary containing statistical measures (mean, min, max, etc.)
        
        Example:
            >>> stats = client.stats(period="week")
            >>> print(f"Average price: {stats['mean_price_tl']:.2f} TL")
        """
        df = self.download(period=period, output_format="dataframe")
        
        if df is None or df.empty:
            return None
        
        stats = {
            'mean_price_tl': df['price'].mean(),
            'min_price_tl': df['price'].min(),
            'max_price_tl': df['price'].max(),
            'median_price_tl': df['price'].median(),
            'std_price_tl': df['price'].std(),
            'total_records': len(df)
        }
        
        if 'priceEur' in df.columns:
            stats['mean_price_eur'] = df['priceEur'].mean()
        
        if 'priceUsd' in df.columns:
            stats['mean_price_usd'] = df['priceUsd'].mean()
        
        return stats
