import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime, timedelta
import math
import os

class BifacialSolarSimulation:
    def __init__(self):
        # Panel specifications (Hanersun N-TOPCon 700W)
        self.panel_power = 700  # watts
        self.panel_efficiency = 0.225  # 22.5%
        self.bifaciality_factor = 0.80  # Average of 75-85%
        self.panel_area = 2.384 * 1.303  # m²
        
        # Location: Lago Brown, Chile
        self.latitude = -47.3919449
        self.longitude = -72.3174973
        self.elevation = 165  # meters
        
        # Simplified solar irradiance for Chilean Patagonia (kWh/m²/day)
        # Based on Global Solar Atlas data for southern Chile
        self.monthly_ghi = {
            1: 6.5,   # January
            2: 5.8,   # February  
            3: 4.2,   # March
            4: 2.8,   # April
            5: 1.8,   # May
            6: 1.3,   # June
            7: 1.5,   # July
            8: 2.2,   # August
            9: 3.5,   # September
            10: 4.8,  # October
            11: 6.0,  # November
            12: 6.8   # December
        }
        
        # Ground albedo (reflection factor)
        self.ground_albedo = 0.2  # Standard for grass/vegetation
        
    def solar_position(self, day_of_year, hour):
        """Calculate solar position for given day and hour"""
        # Solar declination
        declination = 23.45 * math.sin(math.radians(360 * (284 + day_of_year) / 365))
        
        # Hour angle
        hour_angle = 15 * (hour - 12)
        
        # Solar elevation angle
        elevation = math.asin(
            math.sin(math.radians(declination)) * math.sin(math.radians(self.latitude)) +
            math.cos(math.radians(declination)) * math.cos(math.radians(self.latitude)) * 
            math.cos(math.radians(hour_angle))
        )
        
        # Solar azimuth angle
        azimuth = math.atan2(
            math.sin(math.radians(hour_angle)),
            math.cos(math.radians(hour_angle)) * math.sin(math.radians(self.latitude)) - 
            math.tan(math.radians(declination)) * math.cos(math.radians(self.latitude))
        )
        
        return math.degrees(elevation), math.degrees(azimuth)
    
    def calculate_irradiance_on_tilted_surface(self, ghi, solar_elevation, solar_azimuth, 
                                             panel_tilt, panel_azimuth):
        """Calculate irradiance on tilted surface"""
        if solar_elevation <= 0:
            return 0, 0
        
        # Convert to radians
        se_rad = math.radians(solar_elevation)
        sa_rad = math.radians(solar_azimuth)
        pt_rad = math.radians(panel_tilt)
        pa_rad = math.radians(panel_azimuth)
        
        # Angle of incidence
        cos_incidence = (
            math.sin(se_rad) * math.cos(pt_rad) +
            math.cos(se_rad) * math.sin(pt_rad) * math.cos(sa_rad - pa_rad)
        )
        
        if cos_incidence < 0:
            cos_incidence = 0
        
        # Direct normal irradiance (simplified)
        dni = ghi * 0.8 if solar_elevation > 0 else 0
        
        # Direct irradiance on panel
        direct_irradiance = dni * cos_incidence
        
        # Diffuse irradiance (simplified sky model)
        diffuse_irradiance = ghi * 0.2 * (1 + math.cos(pt_rad)) / 2
        
        # Ground reflected irradiance
        ground_reflected = ghi * self.ground_albedo * (1 - math.cos(pt_rad)) / 2
        
        # Front side irradiance
        front_irradiance = direct_irradiance + diffuse_irradiance + ground_reflected
        
        # Back side irradiance (mainly ground reflected and diffuse)
        back_irradiance = (ground_reflected + diffuse_irradiance * 0.3) * self.bifaciality_factor
        
        return front_irradiance, back_irradiance
    
    def calculate_monthly_production(self, panel_tilt, panel_azimuth):
        """Calculate monthly energy production for given panel configuration"""
        monthly_production = {}
        
        for month in range(1, 13):
            # Get representative day of month
            day_of_year = month * 30 - 15  # Approximate middle of month
            
            daily_production = 0
            hourly_data = []
            
            for hour in range(6, 19):  # Daylight hours
                solar_elev, solar_az = self.solar_position(day_of_year, hour)
                
                if solar_elev > 0:
                    # Get hourly irradiance
                    hourly_ghi = self.monthly_ghi[month] / 10  # Simplified hourly distribution
                    
                    front_irr, back_irr = self.calculate_irradiance_on_tilted_surface(
                        hourly_ghi, solar_elev, solar_az, panel_tilt, panel_azimuth
                    )
                    
                    total_irradiance = front_irr + back_irr
                    
                    # Power calculation
                    power = (total_irradiance / 1000) * self.panel_power * self.panel_efficiency
                    
                    daily_production += power
                    hourly_data.append({
                        'hour': hour,
                        'solar_elevation': solar_elev,
                        'front_irradiance': front_irr,
                        'back_irradiance': back_irr,
                        'total_irradiance': total_irradiance,
                        'power': power
                    })
            
            # Days in month
            days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month-1]
            monthly_energy = daily_production * days_in_month / 1000  # kWh
            
            monthly_production[month] = {
                'energy_kwh': monthly_energy,
                'daily_production': daily_production,
                'hourly_data': hourly_data
            }
        
        return monthly_production
    
    def run_simulation(self):
        """Run simulation for all configurations"""
        configurations = [
            {'name': 'Vertical North-South', 'tilt': 90, 'azimuth': 180},
            {'name': 'Vertical East-West', 'tilt': 90, 'azimuth': 90},
            {'name': 'Tilt 20°', 'tilt': 20, 'azimuth': 180},
            {'name': 'Tilt 30°', 'tilt': 30, 'azimuth': 180},
            {'name': 'Tilt 45°', 'tilt': 45, 'azimuth': 180},
            {'name': 'Tilt 60°', 'tilt': 60, 'azimuth': 180},
            {'name': 'Tilt 70°', 'tilt': 70, 'azimuth': 180}
        ]
        
        results = {}
        
        for config in configurations:
            production = self.calculate_monthly_production(config['tilt'], config['azimuth'])
            results[config['name']] = production
        
        return results
    
    def create_visualizations(self, results):
        """Create charts and visualizations"""
        plt.style.use('seaborn-v0_8')
        
        # Monthly energy production comparison
        fig, axes = plt.subplots(2, 2, figsize=(15, 12))
        
        # 1. Monthly energy production by configuration
        months = list(range(1, 13))
        month_names = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
                      'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
        
        ax1 = axes[0, 0]
        for config_name, data in results.items():
            monthly_energy = [data[month]['energy_kwh'] for month in months]
            ax1.plot(month_names, monthly_energy, marker='o', label=config_name, linewidth=2)
        
        ax1.set_title('Monthly Energy Production Comparison', fontsize=14, fontweight='bold')
        ax1.set_xlabel('Month')
        ax1.set_ylabel('Energy Production (kWh)')
        ax1.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
        ax1.grid(True, alpha=0.3)
        
        # 2. Annual total comparison
        ax2 = axes[0, 1]
        annual_totals = {}
        for config_name, data in results.items():
            annual_total = sum(data[month]['energy_kwh'] for month in months)
            annual_totals[config_name] = annual_total
        
        bars = ax2.bar(range(len(annual_totals)), list(annual_totals.values()), 
                      color=['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD', '#98D8C8'])
        ax2.set_title('Annual Energy Production Comparison', fontsize=14, fontweight='bold')
        ax2.set_xlabel('Configuration')
        ax2.set_ylabel('Annual Energy (kWh)')
        ax2.set_xticks(range(len(annual_totals)))
        ax2.set_xticklabels(list(annual_totals.keys()), rotation=45, ha='right')
        
        # Add value labels on bars
        for bar, value in zip(bars, annual_totals.values()):
            ax2.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 5,
                    f'{value:.0f}', ha='center', va='bottom')
        
        # 3. Seasonal comparison
        ax3 = axes[1, 0]
        seasons = {
            'Summer': [12, 1, 2],
            'Autumn': [3, 4, 5],
            'Winter': [6, 7, 8],
            'Spring': [9, 10, 11]
        }
        
        seasonal_data = {}
        for config_name, data in results.items():
            seasonal_energy = {}
            for season, season_months in seasons.items():
                seasonal_energy[season] = sum(data[month]['energy_kwh'] for month in season_months)
            seasonal_data[config_name] = seasonal_energy
        
        x = np.arange(len(seasons))
        width = 0.1
        colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD', '#98D8C8']
        
        for i, (config_name, season_energy) in enumerate(seasonal_data.items()):
            values = list(season_energy.values())
            ax3.bar(x + i*width, values, width, label=config_name, color=colors[i])
        
        ax3.set_title('Seasonal Energy Production Comparison', fontsize=14, fontweight='bold')
        ax3.set_xlabel('Season')
        ax3.set_ylabel('Energy Production (kWh)')
        ax3.set_xticks(x + width * 3)
        ax3.set_xticklabels(list(seasons.keys()))
        ax3.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
        
        # 4. Hourly production example (January 15th)
        ax4 = axes[1, 1]
        jan_15_data = {}
        for config_name, data in results.items():
            hourly_powers = [h['power'] for h in data[1]['hourly_data']]
            hours = [h['hour'] for h in data[1]['hourly_data']]
            jan_15_data[config_name] = (hours, hourly_powers)
        
        for config_name, (hours, powers) in jan_15_data.items():
            ax4.plot(hours, powers, marker='o', label=config_name, linewidth=2)
        
        ax4.set_title('Hourly Power Production (January 15th)', fontsize=14, fontweight='bold')
        ax4.set_xlabel('Hour of Day')
        ax4.set_ylabel('Power Output (W)')
        ax4.legend()
        ax4.grid(True, alpha=0.3)
        
        plt.tight_layout()
        plt.savefig('/home/www/claudev.cz/orb3/solar_panel_comparison.png', dpi=300, bbox_inches='tight')
        plt.close()
        
        return annual_totals
    
    def create_data_tables(self, results):
        """Create data tables for the report"""
        months = list(range(1, 13))
        month_names = ['January', 'February', 'March', 'April', 'May', 'June',
                      'July', 'August', 'September', 'October', 'November', 'December']
        
        # Monthly production table
        monthly_table = pd.DataFrame()
        monthly_table['Month'] = month_names
        
        for config_name, data in results.items():
            monthly_energy = [data[month]['energy_kwh'] for month in months]
            monthly_table[config_name] = [f"{energy:.1f}" for energy in monthly_energy]
        
        # Annual totals
        annual_row = ['ANNUAL TOTAL']
        for config_name, data in results.items():
            annual_total = sum(data[month]['energy_kwh'] for month in months)
            annual_row.append(f"{annual_total:.1f}")
        
        monthly_table.loc[len(monthly_table)] = annual_row
        
        # Hourly production example tables for 15th of each month
        hourly_tables = {}
        for month in months:
            hourly_df = pd.DataFrame()
            # Get first configuration's hourly data as reference for hours
            first_config = list(results.keys())[0]
            hours = [h['hour'] for h in results[first_config][month]['hourly_data']]
            hourly_df['Hour'] = hours
            
            for config_name, data in results.items():
                powers = [h['power'] for h in data[month]['hourly_data']]
                hourly_df[config_name] = [f"{power:.1f}" for power in powers]
            
            hourly_tables[month_names[month-1]] = hourly_df
        
        return monthly_table, hourly_tables

def main():
    simulation = BifacialSolarSimulation()
    
    print("Starting bifacial solar panel simulation for Lago Brown, Chile...")
    print(f"Panel: Hanersun N-TOPCon 700W Bifacial")
    print(f"Location: {simulation.latitude}°, {simulation.longitude}°")
    print("Configurations: Vertical N-S, Vertical E-W, Tilted 20°-70°")
    print()
    
    # Run simulation
    results = simulation.run_simulation()
    
    # Create visualizations
    annual_totals = simulation.create_visualizations(results)
    
    # Create data tables
    monthly_table, hourly_tables = simulation.create_data_tables(results)
    
    print("Simulation completed!")
    print("\nAnnual Energy Production Summary:")
    for config, total in annual_totals.items():
        print(f"{config}: {total:.1f} kWh/year")
    
    return results, monthly_table, hourly_tables, annual_totals

if __name__ == "__main__":
    results, monthly_table, hourly_tables, annual_totals = main()