from datetime import datetime
import json
import logging

log = logging.getLogger(__name__)

def date_to_utc(date_val):
    return datetime.combine(
        date_val,
        datetime.min.time(),
    )


def hours_between(start: datetime, end: datetime) -> int:
    return int((end - start).total_seconds() // 3600)


def year_window_utc(year: int):
    start = datetime(year, 1, 1, 0, 0, 0)
    end = datetime(year + 1, 1, 1, 0, 0, 0)
    return start, end


def calculate_eaf(
        available_hours: float,
        period_hours: float,
        forced_outage_hours:float,
        seasonal_outage_hours: float,
        plot_data = None
    ):
        """
        Calculate EAF using the time-based method from PLN document
        EAF = [AH - (EFDH + EMDH + EPDH + ESEDH)] / PH × 100%

        Args:
            available_hours: Available Hours (AH)
            period_hours: Period Hours (PH)
            max_capacity_mw: Maximum capacity in MW
            actual_production: Actual production
            ideal_production: Ideal production

        Returns:
            Dictionary with EAF result and breakdown
        """

        try:
                # Calculate lost production
                max_capacity = 550
                # Calculate total equivalent derate and outage hours
                edh = calculate_equivalent_derate_hours(plot_data, max_flow_rate=max_capacity)
                # Calculate EAF
                eaf = ((available_hours - edh) / period_hours) * 100
                efor = ((forced_outage_hours + edh)/period_hours) * 100
                sof = (seasonal_outage_hours/period_hours)*100
                
                
                return eaf, efor, sof, edh
        except Exception as e:
                print("Error calculating EAF:", e)
                raise

def calculate_eaf_konkin(
    plot_data, periode_time, konkin_offset
):
    maxHours = periode_time
    hourly_data = create_time_series_data(plot_data, max_hours=maxHours)
    filtered_data = filter_by_month(hourly_data, start_month=konkin_offset, end_month=(12+konkin_offset))
    
    if not filtered_data:
        return {
            'total_hours': 0,
            'uptime_hours': 0,
            'downtime_hours': 0,
            'uptime_percentage': 0,
            'downtime_percentage': 0
        }

    total_hours = len(filtered_data)
    downtime_hours = 0
    uptime_hours = 0
    
    for data in filtered_data:
        if data['flowrate'] == 0:
            downtime_hours += 1
        else:
            uptime_hours += 1

    availability = uptime_hours / total_hours
    derating = calculate_equivalent_derate_hours(plot_data)
    
    eaf = ((uptime_hours - derating)/total_hours) * 100
    
    return eaf

def create_time_series_data(chart_data, max_hours=8760):
    # Sort data by cumulative time
    sorted_data = sorted(chart_data, key=lambda x: x['cumulativeTime'])
    
    hourly_data = []
    current_state_index = 0
    current_flow_rate = sorted_data[0]['flowRate']
    
    for hour in range(1, max_hours + 1):
        # Check if we need to advance to the next state
        while (current_state_index < len(sorted_data) - 1 and 
               hour >= int(sorted_data[current_state_index + 1]['cumulativeTime'])):
            current_state_index += 1
            current_flow_rate = sorted_data[current_state_index]['flowRate']
        
        # Add hourly data point
        hourly_data.append({
            'hour': hour,
            'flowrate': current_flow_rate
        })
    
    return hourly_data

def filter_by_month(hourly_data, start_month, end_month):
    """
    Filter hourly data by month range
    
    Args:
        hourly_data: List of dictionaries with 'hour' and 'flowrate' keys
        start_month: Starting month (1-based)
        end_month: Ending month (1-based)
    
    Returns:
        Filtered list of hourly data
    """
    # Convert months to hours (assuming 730 hours per month on average)
    hours_per_month = 730  # 24 * 30.4 (average days per month)
    total_hours = len(hourly_data)
    
    start_hour = (start_month - 1) * hours_per_month + 1
    end_hour = end_month * hours_per_month
    
    if end_hour >= total_hours:
        end_hour = total_hours
    
    filtered_data = []
    for data in hourly_data:
        if start_hour <= data['hour'] <= end_hour:
            filtered_data.append(data)
    
    return filtered_data


def calculate_equivalent_derate_hours(data_list, max_flow_rate: float = 660) -> float:
    """
    Calculate Equivalent Forced Derated Hours (EFDH).
    
    Each data point represents the start of a period with that flow rate,
    valid until the next cumulativeTime.
    """
    sorted_data = sorted(data_list, key=lambda x: x['cumulativeTime'])
    total_equivalent_derate_hours = 0.0

    for i in range(len(sorted_data) - 1):
        current = sorted_data[i]
        next_ = sorted_data[i + 1]

        time_interval = next_['cumulativeTime'] - current['cumulativeTime']
        derating = max_flow_rate - current['flowRate']
    

        if derating > 0 and derating < max_flow_rate:  # Only count when capacity is reduced
            log.info(f"Time Interval: {time_interval}, Derating: {derating}, Max Flow Rate: {max_flow_rate}")
            total_equivalent_derate_hours += (time_interval * derating / max_flow_rate)

    return total_equivalent_derate_hours



def stream_large_array(filepath, key):
    with open(filepath, "r") as f:
        buffer = ""
        depth = 0
        inside_string = False
        in_target_array = False

        while True:
            chunk = f.read(8192)
            if not chunk:
                break

            for char in chunk:
                buffer += char

                if not in_target_array and f'"{key}": [' in buffer:
                    in_target_array = True
                    buffer = ""
                    continue

                if in_target_array:
                    if char == '"' and (len(buffer) < 2 or buffer[-2] != '\\'):
                        inside_string = not inside_string
                    if not inside_string:
                        if char == '{':
                            depth += 1
                        elif char == '}':
                            depth -= 1
                            if depth == 0:
                                try:
                                    obj = json.loads(buffer)
                                    yield obj
                                except Exception:
                                    pass
                                buffer = ""

                    if char == ']' and depth == 0:
                        return