r"""
Script: 02_harvest_imminent_weekly.py
Purpose: WEEKLY MONITORING - Run WEEKLY/DAILY to get real-time harvest status for all fields

This script runs on RECENT CI data (typically last 300 days) to predict whether each field
is approaching harvest. Use this for operational decision-making and real-time alerts.

RUN FREQUENCY: Weekly (or daily if required)
INPUT: 
  - harvest.xlsx (baseline from scripts 22+23 - contains last harvest date per field)
    Location: laravel_app/storage/app/{project}/Data/harvest.xlsx
  - ci_data_for_python.csv (complete CI data from R script)
    Location: laravel_app/storage/app/{project}/Data/extracted_ci/ci_data_for_python/ci_data_for_python.csv
OUTPUT: 
  - reports/kpis/field_stats/{project}_harvest_imminent_week_{WW}_{YYYY}.csv (weekly probabilities: field, imminent_prob, detected_prob, week, year)

Workflow:
1. Load harvest.xlsx to find last harvest date (season_end) per field
2. Load ci_data_for_python.csv (complete CI data)
3. For each field, extract all CI data AFTER last harvest (complete current season)
4. Run Model 307 inference on full season sequence (last timestep probabilities)
5. Export week_WW_YYYY.csv with probabilities

Output Columns:
  - field: Field ID
  - sub_field: Sub-field identifier
  - imminent_prob: Probability field will be harvestable in next 28 days (0.0-1.0)
  - detected_prob: Probability field is currently being harvested (0.0-1.0)
  - week: ISO week number
  - year: Year
  - as_of_date: Latest date in dataset
  - num_days: Number of days of history used

Use Cases:
  - Alert when imminent_prob > 0.7 (prepare harvest operations)
  - Alert when detected_prob > 0.6 (field is being harvested)
  - Track trends over weeks to validate baseline predictions
  - Feed into 09b script for weekly dashboard reports

Usage:
  python python_app/31_harvest_imminent_weekly.py angata
  
  Examples:
    python python_app/31_harvest_imminent_weekly.py angata
    python python_app/31_harvest_imminent_weekly.py esa
    python python_app/31_harvest_imminent_weekly.py chemba
  
  If no project specified, defaults to 'angata'
"""

import pandas as pd
import numpy as np
import torch
import subprocess
import sys
from pathlib import Path
from datetime import datetime, timedelta
from harvest_date_pred_utils import (
    load_model_and_config,
    extract_features,
)

 
def load_harvest_dates(harvest_file):
    """Load last harvest end dates from harvest.xlsx (output from scripts 22+23)."""
    print("[1/5] Loading harvest data for season boundaries...")
    
    if not Path(harvest_file).exists():
        print(f"  ERROR: {harvest_file} not found")
        print(f"  harvest.xlsx is required to determine current season boundaries")
        return None
    
    try:
        harvest_df = pd.read_excel(harvest_file)
        print(f"  Loaded {len(harvest_df)} season records")
        
        # season_end contains the last harvest date for each season
        harvest_df['season_end'] = pd.to_datetime(harvest_df['season_end'])
        harvest_df['field'] = harvest_df['field'].astype(str).str.strip()
        
        # Group by field and get the LATEST season_end_date (most recent harvest)
        # This marks the start of the current season
        harvest_dates = {}
        for field_id, group in harvest_df.groupby('field'):
            latest_harvest = group['season_end'].max()
            harvest_dates[field_id] = latest_harvest
        
        print(f"  Successfully mapped {len(harvest_dates)} fields")
        print(f"  Last harvest dates range: {min(harvest_dates.values()).date()} to {max(harvest_dates.values()).date()}")
        return harvest_dates
    except Exception as e:
        print(f"  ERROR loading harvest.xlsx: {e}")
        return None


def run_rds_to_csv_conversion():
    """Run R script to convert RDS to CSV."""
    print("\n[2/5] Converting RDS to CSV (daily interpolation)...")
    r_script = Path("02b_convert_rds_to_csv.R")
    
    if not r_script.exists():
        print(f"  ERROR: {r_script} not found")
        return False
    
    # Use full path to Rscript on Windows
    rscript_exe = r"C:\Program Files\R\R-4.4.3\bin\x64\Rscript.exe"
    
    try:
        result = subprocess.run(
            [rscript_exe, str(r_script)],
            capture_output=True,
            text=True,
            timeout=300
        )
        
        if result.returncode != 0:
            print(f"  ERROR running R script:\n{result.stderr}")
            return False
        
        # Show last few lines of output
        lines = result.stdout.strip().split('\n')
        for line in lines[-5:]:
            if line.strip():
                print(f"  {line}")
        
        return True
    except Exception as e:
        print(f"  ERROR: {e}")
        return False


def load_ci_data(csv_file):
    """Load CI data."""
    print("\n[3/5] Loading CI data...")
    
    if not Path(csv_file).exists():
        print(f"  ERROR: {csv_file} not found")
        return None
    
    ci_data = pd.read_csv(csv_file)
    ci_data['Date'] = pd.to_datetime(ci_data['Date'])
    
    print(f"  Loaded {len(ci_data)} daily rows for {ci_data['field'].nunique()} fields")
    print(f"  Date range: {ci_data['Date'].min().date()} to {ci_data['Date'].max().date()}")
    
    return ci_data


def extract_seasonal_data(field_id, harvest_date, ci_data):
    """
    Extract CI data from harvest date to latest for a specific field.
    Returns dataframe sorted by date, or None if insufficient data.
    """
    # field_id is int, ci_data['field'] is also int
    field_data = ci_data[ci_data['field'] == field_id].copy()
    
    if len(field_data) == 0:
        return None
    
    # Filter from harvest date onwards
    field_data = field_data[field_data['Date'] >= harvest_date].sort_values('Date')
    
    # Need at least 30 days of data for meaningful inference
    if len(field_data) < 30:
        return None
    
    return field_data


def run_inference_on_season(field_data, model, config, scalers, device, ci_column='FitData'):
    """
    Run Model 307 inference on recent field CI history.
    Predicts probability that field will be ready to harvest in next 28 days.
    Uses last timestep from the provided data sequence.
    Returns (imminent_prob, detected_prob) for prediction.
    """
    try:
        # Use last 300 days of data for inference (enough history for meaningful patterns, 
        # avoids training data seasonality mismatch)
        if len(field_data) > 300:
            field_data = field_data.iloc[-300:]
        
        # Extract features 
        features_array = extract_features(field_data, config['features'], ci_column)
        
        if features_array.shape[0] < 10:
            return None, None
        
        # Scale features using per-feature scalers (CRITICAL: same as Phase 1 in harvest_date_pred_utils.py)
        # Scalers is a list of StandardScaler objects, one per feature
        if scalers and isinstance(scalers, list):
            for fi, scaler in enumerate(scalers):
                try:
                    features_array[:, fi] = scaler.transform(features_array[:, fi].reshape(-1, 1)).flatten()
                except Exception:
                    pass
        
        # Run inference
        with torch.no_grad():
            x_tensor = torch.tensor(features_array, dtype=torch.float32).unsqueeze(0).to(device)
            out_imm, out_det = model(x_tensor)
            
            # Get last timestep probabilities
            imminent_prob = out_imm.squeeze(0)[-1].cpu().item()
            detected_prob = out_det.squeeze(0)[-1].cpu().item()
        
        return round(imminent_prob, 4), round(detected_prob, 4)
    
    except Exception as e:
        return None, None


def main():
    # Get project name from command line or use default
    project_name = sys.argv[1] if len(sys.argv) > 1 else "angata"
    
    # Construct paths - work from either python_app/ or root smartcane/ directory
    # Try root first (laravel_app/...), then fall back to ../ (running from python_app/)
    if Path("laravel_app/storage/app").exists():
        base_storage = Path("laravel_app/storage/app") / project_name / "Data"
    else:
        base_storage = Path("../laravel_app/storage/app") / project_name / "Data"
    
    ci_data_dir = base_storage / "extracted_ci" / "ci_data_for_python"
    CI_DATA_FILE = ci_data_dir / "ci_data_for_python.csv"
    HARVEST_FILE = base_storage / "harvest.xlsx"  # Output from scripts 22+23
    
    # Determine week and year from current date for timestamped export
    today = datetime.now()
    week_num = int(today.strftime('%V'))
    year_num = int(today.strftime('%Y'))
    
    # Output directory: reports/kpis/field_stats/
    reports_dir = base_storage.parent / "reports" / "kpis" / "field_stats"
    reports_dir.mkdir(parents=True, exist_ok=True)
    OUTPUT_CSV = reports_dir / f"{project_name}_harvest_imminent_week_{week_num:02d}_{year_num}.csv"
    
    print("="*80)
    print(f"HARVEST IMMINENT PROBABILITY - WEEKLY MONITORING ({project_name})")
    print("="*80)
    
    # [1] Load harvest dates (required to determine season boundaries)
    harvest_dates = load_harvest_dates(HARVEST_FILE)
    if harvest_dates is None or len(harvest_dates) == 0:
        print(f"ERROR: Cannot run without harvest.xlsx - required to determine current season boundaries")
        print(f"  Please run scripts 22 (baseline prediction) and 23 (format conversion) first")
        return
    
    # [2] Load CI data
    print(f"\n[2/5] Loading CI data...")
    print(f"  From: {CI_DATA_FILE}")
    
    if not CI_DATA_FILE.exists():
        print(f"  ERROR: {CI_DATA_FILE} not found")
        print(f"  Expected at: {CI_DATA_FILE.resolve()}")
        print(f"\n  Run 02b_convert_rds_to_csv.R first to generate this file:")
        print(f"  Rscript r_app/02b_convert_ci_rds_to_csv.R {project_name}")
        return
    
    ci_data = load_ci_data(CI_DATA_FILE)
    if ci_data is None:
        print("ERROR: Could not load CI data")
        return
    
    # [3] Load model (from python_app directory)
    print("\n[3/5] Loading Model 307...")
    model_dir = Path("python_app")  # Model files located in python_app/ directory
    model, config, scalers = load_model_and_config(model_dir)
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    print(f"  Device: {device}")
    
    # [4] Run inference per field
    print("\n[4/5] Running seasonal inference...")
    
    results_list = []
    ci_column = config['data']['ci_column']
    
    # Get field metadata
    field_meta = ci_data.groupby('field').agg({
        'sub_field': 'first',
        'Date': 'max'
    }).reset_index()
    field_meta.columns = ['field', 'sub_field', 'latest_date']
    
    count = 0
    for field_id in ci_data['field'].unique():
        # Convert field_id to string for consistency
        field_id_str = str(field_id).strip()
        
        # Get metadata
        meta = field_meta[field_meta['field'] == field_id]
        if len(meta) == 0:
            continue
        
        sub_field = meta['sub_field'].iloc[0]
        latest_date = meta['latest_date'].iloc[0]
        
        # Get last harvest date for this field (start of current season)
        last_harvest = harvest_dates.get(field_id_str)
        if last_harvest is None:
            continue
        
        # Extract all CI data AFTER last harvest (complete current season)
        field_data = ci_data[ci_data['field'] == field_id].copy()
        field_data = field_data.sort_values('Date')
        field_data = field_data[field_data['Date'] > last_harvest]  # After last harvest
        
        # Need at least 30 days of data since planting
        if len(field_data) < 30:
            continue
        
        # Run inference on full current season to predict next 28 days
        imminent_prob, detected_prob = run_inference_on_season(
            field_data, model, config, scalers, device, ci_column
        )
        
        if imminent_prob is None:
            continue
        
        week = int(latest_date.strftime('%V'))
        year = int(latest_date.strftime('%Y'))
        
        results_list.append({
            'field': field_id,
            'sub_field': sub_field,
            'imminent_prob': imminent_prob,
            'detected_prob': detected_prob,
            'week': week,
            'year': year,
            'as_of_date': latest_date,
            'num_days': len(field_data),
        })
        
        count += 1
    
    print(f"  Completed inference for {count} fields")
    
    # Build output DataFrame
    df = pd.DataFrame(results_list)
    df.to_csv(OUTPUT_CSV, index=False)
    
    print(f"\n[5/5] Exporting results...")
    print(f"✓ Exported {len(df)} fields to {OUTPUT_CSV}")
    print(f"  Output location: {OUTPUT_CSV.resolve()}")
    
    if len(df) > 0:
        print(f"\nSample rows:")
        print(df[['field', 'sub_field', 'imminent_prob', 'detected_prob', 'num_days', 'week', 'year']].head(15).to_string(index=False))
        
        # Show alert summary
        high_imminent = len(df[df['imminent_prob'] > 0.7])
        high_detected = len(df[df['detected_prob'] > 0.6])
        print(f"\n⚠ ALERTS:")
        print(f"  Fields with imminent_prob > 0.70: {high_imminent}")
        print(f"  Fields with detected_prob > 0.60: {high_detected}")
    else:
        print(f"  WARNING: No results exported - check CI data availability")
    
    print(f"\nStorage structure:")
    print(f"  Input harvest: laravel_app/storage/app/{project_name}/Data/harvest.xlsx")
    print(f"  Input CI:      laravel_app/storage/app/{project_name}/Data/extracted_ci/ci_data_for_python/")
    print(f"  Output:        laravel_app/storage/app/{project_name}/reports/kpis/field_stats/")
    print(f"  Filename:      {project_name}_harvest_imminent_week_{week_num:02d}_{year_num}.csv")
    print(f"\nReady to load into 80_calculate_kpis.R")


if __name__ == "__main__":
    main()
