import math
import json
from datetime import datetime, timedelta
from astral import LocationInfo
from astral.sun import sun, elevation, azimuth
import pytz

class BackSideOnlySimulation:
    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.location = LocationInfo("Lago Brown", "Chile", "America/Santiago", -47.3919449, -72.3174973)
        self.timezone = pytz.timezone('America/Santiago')
        
        # Direct Normal Irradiance - constant when sun is shining (ideal conditions)
        self.dni_clear_sky = 900  # W/m² for clear sky conditions
        
        # Different albedo values for comparison
        self.albedo_types = {
            'clay': 0.15,      # jíl
            'grass': 0.25,     # tráva
            'sand': 0.35,      # písek
            'concrete': 0.40,  # beton
            'snow_old': 0.65,  # starý sníh
            'snow_fresh': 0.85 # čerstvý sníh
        }
        
    def get_solar_position(self, date, hour, minute=0):
        """Get precise solar position using astronomical calculations"""
        dt = datetime(date.year, date.month, date.day, hour, minute, 0)
        dt = self.timezone.localize(dt)
        
        # Get solar elevation and azimuth
        solar_elevation = elevation(self.location.observer, dt)
        solar_azimuth = azimuth(self.location.observer, dt)
        
        return solar_elevation, solar_azimuth
    
    def calculate_panel_self_shading(self, panel_tilt, solar_elevation):
        """Calculate how much panel shades its own ground reflection area"""
        if panel_tilt >= 90:  # Vertical panel - no self-shading
            return 1.0
        
        if solar_elevation <= 0:
            return 0.0
            
        # For tilted panels, calculate shadow effect
        # More tilted panel = more self-shading of ground
        shadow_angle = math.radians(panel_tilt)
        sun_angle = math.radians(solar_elevation)
        
        # Shadow length factor - how much ground is shaded
        if solar_elevation < panel_tilt:
            # Low sun - significant shading
            shadow_factor = 0.3 + 0.7 * (solar_elevation / panel_tilt)
        else:
            # High sun - less shading
            shadow_factor = 0.7 + 0.3 * math.cos(shadow_angle - sun_angle)
            
        return max(0.2, min(1.0, shadow_factor))  # Between 20-100%
    
    def calculate_diffuse_view_factor(self, panel_tilt):
        """Calculate view factor for diffuse sky radiation"""
        # View factor to sky for back side of tilted panel
        tilt_rad = math.radians(panel_tilt)
        
        if panel_tilt == 90:  # Vertical
            sky_view_factor = 0.5  # Sees half the sky
        else:
            # Tilted panel back side - sees more sky as it tilts away from ground
            sky_view_factor = (1 - math.cos(tilt_rad)) / 2
            
        return sky_view_factor
    
    def calculate_ground_view_factor(self, panel_tilt):
        """Calculate view factor for ground reflection"""
        tilt_rad = math.radians(panel_tilt)
        
        if panel_tilt == 90:  # Vertical
            ground_view_factor = 0.5  # Sees half the ground
        else:
            # Tilted panel back side - sees more ground as it tilts toward ground
            ground_view_factor = (1 + math.cos(tilt_rad)) / 2
            
        return ground_view_factor
    
    def calculate_back_side_irradiance(self, date, hour, minute, panel_tilt, ground_albedo):
        """Calculate irradiance on back side only - no direct light"""
        solar_elev, solar_az = self.get_solar_position(date, hour, minute)
        
        if solar_elev <= 0:
            return 0  # No sun
            
        # 1. DIFFUSE SKY RADIATION (isotropic sky model)
        diffuse_factor = 0.15  # 15% of total radiation is diffuse
        sky_diffuse = self.dni_clear_sky * diffuse_factor
        
        # View factor to sky for back side
        sky_view_factor = self.calculate_diffuse_view_factor(panel_tilt)
        back_diffuse = sky_diffuse * sky_view_factor
        
        # 2. GROUND REFLECTED RADIATION
        # Total ground irradiance (direct + diffuse)
        ground_irradiance = self.dni_clear_sky * math.sin(math.radians(solar_elev)) + sky_diffuse
        
        # Ground reflection
        ground_reflected = ground_irradiance * ground_albedo
        
        # View factor to ground for back side
        ground_view_factor = self.calculate_ground_view_factor(panel_tilt)
        
        # Self-shading effect - tilted panels shade their reflection area
        self_shading_factor = self.calculate_panel_self_shading(panel_tilt, solar_elev)
        
        back_ground = ground_reflected * ground_view_factor * self_shading_factor
        
        # Total back side irradiance
        total_back_irradiance = back_diffuse + back_ground
        
        # Apply bifacial efficiency
        back_irradiance_effective = total_back_irradiance * self.bifaciality_factor
        
        return back_irradiance_effective
    
    def calculate_monthly_production_back_only(self, panel_tilt, ground_albedo, albedo_name):
        """Calculate monthly energy production for back side only"""
        monthly_production = {}
        
        # Only winter months: May, June, July, August
        winter_months = [5, 6, 7, 8]
        
        for month in winter_months:
            # Representative date for month (15th day)
            date = datetime(2024, month, 15)
            
            daily_production = 0
            half_hourly_data = []
            
            # Calculate for each half hour of the day (48 intervals)
            for half_hour in range(48):
                hour = half_hour // 2
                minute = 0 if half_hour % 2 == 0 else 30
                
                back_irr = self.calculate_back_side_irradiance(date, hour, minute, panel_tilt, ground_albedo)
                
                if back_irr > 0:
                    # Power calculation: only from back side
                    power = (back_irr / 1000) * self.panel_power
                    
                    # Energy for 30 minutes (0.5 hour)
                    daily_production += power * 0.5
                    
                    time_str = f"{hour:02d}:{minute:02d}"
                    half_hourly_data.append({
                        'time': time_str,
                        'solar_elevation': round(self.get_solar_position(date, hour, minute)[0], 1),
                        'back_irradiance': round(back_irr, 2),
                        'power': round(power, 1)
                    })
                else:
                    time_str = f"{hour:02d}:{minute:02d}"
                    half_hourly_data.append({
                        'time': time_str,
                        'solar_elevation': 0,
                        'back_irradiance': 0,
                        'power': 0
                    })
            
            # 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': round(monthly_energy, 1),
                'daily_production': round(daily_production, 1),
                'half_hourly_data': half_hourly_data
            }
        
        return monthly_production
    
    def run_simulation(self):
        """Run back-side only simulation for all configurations and albedo types"""
        configurations = [
            {'name': 'Vertical North-South', 'tilt': 90},
            {'name': 'Tilt 20°', 'tilt': 20},
            {'name': 'Tilt 30°', 'tilt': 30},
            {'name': 'Tilt 45°', 'tilt': 45},
            {'name': 'Tilt 60°', 'tilt': 60},
            {'name': 'Tilt 70°', 'tilt': 70}
        ]
        
        results = {}
        
        for albedo_name, albedo_value in self.albedo_types.items():
            print(f"  Albedo {albedo_name} ({albedo_value}):")
            albedo_results = {}
            
            for config in configurations:
                print(f"    - {config['name']}")
                production = self.calculate_monthly_production_back_only(config['tilt'], albedo_value, albedo_name)
                albedo_results[config['name']] = production
            
            results[albedo_name] = albedo_results
        
        return results
    
    def generate_html_report(self, results):
        """Generate HTML report for back side only simulation"""
        
        months_czech = ['Květen', 'Červen', 'Červenec', 'Srpen']
        months_english = ['May', 'June', 'July', 'August']
        winter_months = [5, 6, 7, 8]
        
        html_content = f"""<!DOCTYPE html>
<html lang="cs">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Analýza POUZE zadní strany panelu - Zimní měsíce - Lago Brown, Chile</title>
    <style>
        body {{
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            line-height: 1.6;
            color: #333;
            max-width: 1400px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f8f9fa;
        }}
        
        .header {{
            background: linear-gradient(135deg, #6a1b9a 0%, #4a148c 100%);
            color: white;
            padding: 30px;
            border-radius: 10px;
            margin-bottom: 30px;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        }}
        
        .header h1 {{
            margin: 0;
            font-size: 2.5em;
            font-weight: 300;
        }}
        
        .header p {{
            margin: 10px 0 0 0;
            font-size: 1.1em;
            opacity: 0.9;
        }}
        
        .section {{
            background: white;
            margin-bottom: 30px;
            border-radius: 10px;
            overflow: hidden;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }}
        
        .section h2 {{
            background: #6a1b9a;
            color: white;
            margin: 0;
            padding: 20px 25px;
            font-size: 1.5em;
        }}
        
        .section-content {{
            padding: 25px;
        }}
        
        .albedo-table {{
            width: 100%;
            border-collapse: collapse;
            margin: 20px 0;
            font-size: 0.9em;
        }}
        
        .albedo-table th {{
            background: #6a1b9a;
            color: white;
            padding: 12px 8px;
            text-align: center;
            font-weight: 600;
        }}
        
        .albedo-table td {{
            padding: 10px 8px;
            border-bottom: 1px solid #e9ecef;
            text-align: center;
        }}
        
        .albedo-table tr:hover {{
            background-color: #f8f9fa;
        }}
        
        .albedo-header {{
            background: #e1bee7;
            font-weight: bold;
        }}
        
        .best-value {{
            background-color: #c8e6c9 !important;
            font-weight: bold;
        }}
        
        .note {{
            background: #fff3cd;
            border: 1px solid #ffeaa7;
            padding: 15px;
            border-radius: 8px;
            margin: 20px 0;
        }}
        
        .note h4 {{
            margin: 0 0 10px 0;
            color: #856404;
        }}
        
        .physics-info {{
            background: #e3f2fd;
            border: 1px solid #2196f3;
            padding: 20px;
            border-radius: 10px;
            margin: 20px 0;
        }}
        
        .physics-info h3 {{
            color: #1976d2;
            margin: 0 0 15px 0;
        }}
        
        .albedo-legend {{
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 15px;
            margin: 20px 0;
        }}
        
        .albedo-item {{
            background: #f8f9fa;
            padding: 15px;
            border-radius: 8px;
            border-left: 4px solid #6a1b9a;
        }}
        
        .albedo-item strong {{
            color: #6a1b9a;
        }}
    </style>
</head>
<body>
    <div class="header">
        <h1>🌅 Analýza POUZE zadní strany bifaciálního panelu</h1>
        <p>Hanersun N-TOPCon 700W • Zimní měsíce • Různá albeda povrchů</p>
    </div>
    
    <div class="physics-info">
        <h3>🔬 Fyzikální model zadní strany panelu</h3>
        <ul>
            <li><strong>Žádné přímé slunce</strong> - pouze difúzní světlo a odrazy</li>
            <li><strong>Difúzní záření:</strong> 15% z celkového záření, rozložené podle view faktoru</li>
            <li><strong>Ground reflection:</strong> závislé na albedu povrchu a self-shading efektu</li>
            <li><strong>Self-shading:</strong> nakloněné panely stíní část své odrazové plochy</li>
            <li><strong>Bifaciální efektivita:</strong> 80% výkonu přední strany</li>
            <li><strong>View faktory:</strong> kolik oblohy/země panel "vidí" ze zadní strany</li>
        </ul>
    </div>
    
    <div class="section">
        <h2>🌍 Albedo různých povrchů</h2>
        <div class="section-content">
            <div class="albedo-legend">"""
        
        albedo_descriptions = {
            'clay': 'Jíl (původní simulace)',
            'grass': 'Tráva (střední odrazivost)', 
            'sand': 'Písek (vyšší odrazivost)',
            'concrete': 'Beton (stavební povrch)',
            'snow_old': 'Starý sníh (vysoká odrazivost)',
            'snow_fresh': 'Čerstvý sníh (maximální odrazivost)'
        }
        
        for albedo_name, albedo_value in self.albedo_types.items():
            desc = albedo_descriptions.get(albedo_name, albedo_name)
            html_content += f"""
                <div class="albedo-item">
                    <strong>{albedo_name.title()}: {albedo_value}</strong><br>
                    <small>{desc}</small>
                </div>"""
        
        html_content += """
            </div>
        </div>
    </div>
    
    <div class="section">
        <h2>📊 Měsíční výroba - POUZE zadní strana panelu</h2>
        <div class="section-content">"""
        
        # Generate table for each month
        config_order = ['Vertical North-South', 'Tilt 20°', 'Tilt 30°', 'Tilt 45°', 'Tilt 60°', 'Tilt 70°']
        
        for month_idx, month in enumerate(winter_months):
            month_name = months_english[month_idx]
            month_czech = months_czech[month_idx]
            
            html_content += f"""
            <h3>{month_czech} ({month_name}) - Pouze zadní strana</h3>
            <table class="albedo-table">
                <thead>
                    <tr>
                        <th>Albedo / Konfigurace</th>"""
            
            for config in config_order:
                html_content += f"<th>{config}</th>"
            
            html_content += """
                    </tr>
                </thead>
                <tbody>"""
            
            # Add rows for each albedo type
            for albedo_name, albedo_value in self.albedo_types.items():
                html_content += f"""
                    <tr>
                        <td class="albedo-header">{albedo_name.title()}<br>({albedo_value})</td>"""
                
                # Find best value for highlighting
                month_values = []
                for config in config_order:
                    value = results[albedo_name][config][month]['energy_kwh']
                    month_values.append(value)
                
                best_value = max(month_values)
                
                for config in config_order:
                    value = results[albedo_name][config][month]['energy_kwh']
                    cell_class = 'best-value' if value == best_value else ''
                    html_content += f"<td class='{cell_class}'>{value}</td>"
                
                html_content += "</tr>"
            
            html_content += "</tbody></table>"
        
        html_content += """
            <div class="note">
                <h4>📝 Pozorování z tabulky:</h4>
                <ul>
                    <li><strong>Vertikální panely</strong> mají nejlepší výkon při vysokém albedu (sníh)</li>
                    <li><strong>Self-shading efekt</strong> - nakloněné panely ztrácí část ground reflection</li>
                    <li><strong>Albedo má obrovský vliv</strong> - čerstvý sníh vs jíl = 5-6x rozdíl</li>
                    <li><strong>Nejlepší konfigurace</strong> se mění podle albeda povrchu</li>
                </ul>
            </div>
        </div>
    </div>
    
    <div style="text-align: center; padding: 30px; color: #6c757d; border-top: 1px solid #e9ecef; margin-top: 50px;">
        <p>🌅 Simulace pouze zadní strany panelu s různými albedy<br>
        <small>Datum: 12.08.2025<br>
        Poznámka: Bez přímého slunce - pouze difúzní světlo a ground reflection</small></p>
    </div>
</body>
</html>"""
        
        with open('/home/www/claudev.cz/orb3/solar_panel_report_back_side_only.html', 'w', encoding='utf-8') as f:
            f.write(html_content)

def main():
    simulation = BackSideOnlySimulation()
    
    print("🌅 Spouštím simulaci POUZE zadní strany panelu...")
    print(f"Panel: Hanersun N-TOPCon 700W Bifacial") 
    print(f"Lokace: {simulation.location.latitude}°, {simulation.location.longitude}°")
    print("Zimní měsíce: Květen, Červen, Červenec, Srpen")
    print("Albeda: jíl, tráva, písek, beton, starý sníh, čerstvý sníh")
    print()
    
    # Run simulation
    print("Running back-side only simulation...")
    results = simulation.run_simulation()
    
    # Generate HTML report
    simulation.generate_html_report(results)
    print()
    print("✅ HTML report pouze zadní strany byl vytvořen: solar_panel_report_back_side_only.html")
    
    # Show comparison
    print()
    print("📊 Porovnání albeda pro červen, vertikální panel:")
    for albedo_name, albedo_value in simulation.albedo_types.items():
        energy = results[albedo_name]['Vertical North-South'][6]['energy_kwh']
        print(f"  {albedo_name} ({albedo_value}): {energy} kWh")

if __name__ == "__main__":
    results = main()