#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Richards S-Curve Fit for 2010-2025 Data with K=4298

Fits Richards model to 2010-2025 launch data with carrying capacity
constrained to K=4298 (close to physical limit of 3650).
"""

import pandas as pd
import numpy as np
from scipy.optimize import curve_fit
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')

plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False


def richards(t, K, r, t0, v):
    """Richards curve (generalized logistic model)"""
    exp_term = np.exp(-r * (t - t0))
    exp_term = np.clip(exp_term, 1e-10, 1e10)
    return K / np.power(1 + exp_term, 1/v)


def richards_fixed_K(t, r, t0, v):
    """Richards curve with fixed K=4298"""
    K = 4298
    exp_term = np.exp(-r * (t - t0))
    exp_term = np.clip(exp_term, 1e-10, 1e10)
    return K / np.power(1 + exp_term, 1/v)


def load_data(filepath="rocket_launch_counts.csv"):
    """Load rocket launch data"""
    df = pd.read_csv(filepath)
    df = df.rename(columns={"YDate": "year", "Total": "launches"})
    df["year"] = pd.to_numeric(df["year"], errors="coerce")
    df["launches"] = pd.to_numeric(df["launches"], errors="coerce")
    df = df.dropna(subset=["year", "launches"])
    df = df[(df["year"] >= 1957) & (df["year"] <= 2025)]
    df = df.astype({"year": int, "launches": int})
    df = df.sort_values("year").reset_index(drop=True)
    return df


def main():
    print("=" * 60)
    print("Richards S-Curve Fit (2010-2025 Data, K=4298)")
    print("=" * 60)
    
    # Load data
    df = load_data()
    
    # Filter 2010-2025
    start_year = 2010
    end_year = 2025
    data = df[(df["year"] >= start_year) & (df["year"] <= end_year)].copy()
    years = data["year"].values
    launches = data["launches"].values
    
    print(f"Data range: {start_year} - {end_year}")
    print(f"Data points: {len(data)}")
    print(f"\nHistorical data:")
    for y, l in zip(years, launches):
        print(f"  {y}: {l} launches")
    
    # Fit with fixed K=4298
    K_fixed = 4298
    t = (years - start_year).astype(float)
    
    p0 = [0.2, 20.0, 2.0]  # r, t0, v
    bounds = ([0.01, 5, 0.5], [1.0, 100, 10.0])
    
    try:
        popt, pcov = curve_fit(richards_fixed_K, t, launches, p0=p0, bounds=bounds, maxfev=100000)
        r, t0, v = popt
        
        # Calculate R²
        y_pred = richards_fixed_K(t, *popt)
        ss_res = np.sum((launches - y_pred) ** 2)
        ss_tot = np.sum((launches - np.mean(launches)) ** 2)
        r_squared = 1 - (ss_res / ss_tot)
        
        print(f"\nFitted Parameters (K fixed at {K_fixed}):")
        print(f"  K (carrying capacity) = {K_fixed} launches/year (FIXED)")
        print(f"  r (growth rate)       = {r:.4f}")
        print(f"  t0 (inflection point) = {start_year + t0:.1f}")
        print(f"  v (shape parameter)   = {v:.3f}")
        print(f"  R²                    = {r_squared:.4f}")
        
    except Exception as e:
        print(f"Fitting error: {e}")
        return
    
    # Physical limit
    physical_max = 3650
    print(f"\nPhysical limit: {physical_max} (10 sites × 365 days)")
    print(f"K / Physical limit = {K_fixed/physical_max:.2f}x")
    
    # ========== Create Visualization ==========
    # 缩小图片尺寸（比例不变），使字体相对更大
    fig, ax = plt.subplots(figsize=(8, 4.67))
    
    # 中低饱和度配色
    color_data = '#5D6D7E'       # 灰蓝色 - 数据点
    color_curve = '#52796F'      # 暗绿色 - S曲线
    color_target = '#7B9EA8'     # 灰蓝绿色 - 2050标记
    
    # Historical data points
    ax.scatter(years, launches, color=color_data, s=80, alpha=0.85, 
               label='Historical Data (2010-2025)', zorder=4, edgecolor='white', linewidth=1)
    
    # Generate smooth S-curve
    years_smooth = np.linspace(start_year, 2055, 500)
    t_smooth = years_smooth - start_year
    pred_smooth = richards(t_smooth, K_fixed, r, t0, v)
    
    # S-curve prediction
    ax.plot(years_smooth, pred_smooth, color=color_curve, lw=2.5, 
            label=f'Richards Model (K={K_fixed}, R²={r_squared:.3f})', zorder=2)
    
    # K=4298 saturation line
    ax.axhline(K_fixed, color=color_curve, ls=':', lw=1.5, alpha=0.6,
               label=f'K = {K_fixed}')
    
    # Mark 2050 line only
    ax.axvline(2050, color=color_target, ls=':', lw=1.5, alpha=0.7)
    ax.text(2050.5, K_fixed*0.83, '2050\n(Target)', fontsize=9, color=color_target)
    
    # Only show 2050 prediction point
    t_2050 = 2050 - start_year
    pred_2050 = richards(t_2050, K_fixed, r, t0, v)
    ax.scatter([2050], [pred_2050], color=color_target, s=60, marker='D', zorder=4)
    ax.annotate(f'{pred_2050:.0f}', xy=(2050, pred_2050), 
                xytext=(2050.5, pred_2050+180),
                fontsize=9, color=color_target, fontweight='bold')
    
    # Formatting
    ax.set_xlabel('Year', fontsize=11)
    ax.set_ylabel('Annual Launches', fontsize=11)
    ax.legend(loc='upper left', fontsize=9)
    ax.grid(True, alpha=0.25)
    ax.set_xlim(2010, 2055)
    ax.set_ylim(0, K_fixed * 1.15)
    
    plt.tight_layout()
    plt.savefig('richards_curve_2010_K4298.png', dpi=150, bbox_inches='tight')
    plt.close()
    
    print("\nPlot saved: richards_curve_2010_K4298.png")
    print("=" * 60)


if __name__ == "__main__":
    main()
