#!/usr/bin/env python
"""
Standalone validation script for binary calendar data.

This script validates generated binary data against the calendar10k system
to ensure data integrity and accuracy.
"""

import sys
import os
from datetime import datetime, timedelta
from pathlib import Path

# Add Django project to path
project_root = Path(__file__).parent.parent.parent
sys.path.append(str(project_root))

import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'iching.settings')
django.setup()

from calendar10k.scripts.binary_calendar import BinaryCalendarGenerator
from calendar10k.scripts.binary_reader import BinaryCalendarReader
from iching.utils import bz, bz24, lunar_calendar


def validate_year(year, data_dir="calendar10k/data"):
    """Validate a specific year's binary data against calendar10k."""
    print(f"=== Validating {year} ===")
    
    # Initialize components
    generator = BinaryCalendarGenerator(data_dir)
    reader = BinaryCalendarReader(data_dir)
    
    # Generate binary data
    binary_file = os.path.join(data_dir, f"{year}.bin")
    if not os.path.exists(binary_file):
        print(f"Generating binary data for {year}...")
        generator.generate_year_file(year, "validation.1.0")
    
    # Load binary data
    try:
        version, binary_data = reader.get_year_data(year)
        print(f"Loaded {len(binary_data)} days from binary file (version {version})")
    except Exception as e:
        print(f"Failed to load binary data: {e}")
        return False
    
    # Get calendar10k data for comparison
    try:
        calendar10k_solar_terms = get_calendar10k_solar_terms(year)
        print(f"Found {len(calendar10k_solar_terms)} solar terms from calendar10k")
    except Exception as e:
        print(f"Failed to get calendar10k data: {e}")
        return False
    
    # Validation statistics
    stats = {
        'total_days': 0,
        'solar_term_matches': 0,
        'solar_term_errors': 0,
        'lunar_date_matches': 0,
        'lunar_date_errors': 0,
        'jian_chu_matches': 0,
        'jian_chu_errors': 0,
        'errors': []
    }
    
    # Validate Feb-Dec (calendar10k range)
    start_date = datetime(year, 2, 1)
    end_date = datetime(year, 12, 31)
    
    current_date = start_date
    while current_date <= end_date:
        stats['total_days'] += 1
        
        try:
            validate_single_day(current_date, binary_data, calendar10k_solar_terms, stats)
        except Exception as e:
            stats['errors'].append(f"{current_date.date()}: {e}")
            if len(stats['errors']) > 10:  # Limit errors
                break
        
        current_date += timedelta(days=1)
    
    # Report results
    print(f"\nValidation Results:")
    print(f"  Total days: {stats['total_days']}")
    print(f"  Solar terms: {stats['solar_term_matches']} matches, {stats['solar_term_errors']} errors")
    print(f"  Lunar dates: {stats['lunar_date_matches']} matches, {stats['lunar_date_errors']} errors")
    print(f"  Jian Chu: {stats['jian_chu_matches']} matches, {stats['jian_chu_errors']} errors")
    
    if stats['errors']:
        print(f"  First few errors:")
        for error in stats['errors'][:5]:
            print(f"    {error}")
    
    # Calculate accuracy percentages
    total_validations = stats['solar_term_matches'] + stats['solar_term_errors']
    if total_validations > 0:
        solar_accuracy = stats['solar_term_matches'] / total_validations * 100
        print(f"  Solar term accuracy: {solar_accuracy:.1f}%")
    
    total_lunar = stats['lunar_date_matches'] + stats['lunar_date_errors']
    if total_lunar > 0:
        lunar_accuracy = stats['lunar_date_matches'] / total_lunar * 100
        print(f"  Lunar date accuracy: {lunar_accuracy:.1f}%")
    
    total_jc = stats['jian_chu_matches'] + stats['jian_chu_errors']
    if total_jc > 0:
        jc_accuracy = stats['jian_chu_matches'] / total_jc * 100
        print(f"  Jian Chu accuracy: {jc_accuracy:.1f}%")
    
    return len(stats['errors']) == 0


def validate_single_day(target_date, binary_data, calendar10k_solar_terms, stats):
    """Validate a single day against calendar10k."""
    target_date_only = target_date.date()
    
    # Find binary data for this date
    binary_day = None
    for day_data in binary_data:
        if day_data['normal_date'].date() == target_date_only:
            binary_day = day_data
            break
    
    if not binary_day:
        raise ValueError(f"Binary data not found for {target_date_only}")
    
    # Validate solar terms
    if target_date_only in calendar10k_solar_terms:
        expected_jq = calendar10k_solar_terms[target_date_only]
        binary_jq = binary_day['jq_index']
        
        if binary_jq == expected_jq['index']:
            stats['solar_term_matches'] += 1
        else:
            stats['solar_term_errors'] += 1
            stats['errors'].append(
                f"{target_date_only}: Solar term mismatch - "
                f"binary: {binary_jq}, expected: {expected_jq['index']}"
            )
    
    # Validate lunar date
    try:
        lunar_info = lunar_calendar.convertLunar(target_date)
        expected_lunar = datetime(lunar_info['y'], lunar_info['m'], lunar_info['d']).date()
        binary_lunar = binary_day['lunar_date'].date()
        
        date_diff = abs((binary_lunar - expected_lunar).days)
        if date_diff <= 1:  # Allow 1-day tolerance
            stats['lunar_date_matches'] += 1
        else:
            stats['lunar_date_errors'] += 1
            stats['errors'].append(
                f"{target_date_only}: Lunar date mismatch - "
                f"binary: {binary_lunar}, expected: {expected_lunar}"
            )
    except ValueError:
        # Skip invalid lunar dates
        pass
    
    # Validate Jian Chu
    try:
        bzdate = bz.getCalendar10kGodEarthStem(
            target_date.year, target_date.month, target_date.day, 0, 0, 0
        )
        expected_jc = bz.calcJianChu12God(
            target_date.year, target_date.month, target_date.day, bzdate
        )
        binary_jc = binary_day['jc_index']
        
        if binary_jc == expected_jc:
            stats['jian_chu_matches'] += 1
        else:
            stats['jian_chu_errors'] += 1
            stats['errors'].append(
                f"{target_date_only}: Jian Chu mismatch - "
                f"binary: {binary_jc}, expected: {expected_jc}"
            )
    except Exception:
        # Skip if calculation fails
        pass


def get_calendar10k_solar_terms(year):
    """Get solar terms from calendar10k system."""
    cycles = bz24.calc24CycleAdjustedYear(year, groupByMonth=True)
    solar_terms = {}
    
    for month_index, cycle in enumerate(cycles):
        for key, val in cycle.items():
            if key in ['first', 'second']:
                jq_index = month_index * 2 + (0 if key == 'first' else 1)
                jq_date = datetime.strptime(val['d'], '%Y-%m-%d %H:%M:%S')
                
                # Apply UTC+8 timezone adjustment
                jq_date_utc8 = jq_date + timedelta(hours=8)
                
                if jq_date_utc8.year == year:
                    solar_terms[jq_date_utc8.date()] = {
                        'index': jq_index,
                        'time': jq_date_utc8.strftime('%H:%M:%S'),
                        'name': val['c']
                    }
    
    return solar_terms


def main():
    """Main validation script."""
    if len(sys.argv) < 2:
        print("Usage: python validate_binary_data.py <year> [data_dir]")
        print("Example: python validate_binary_data.py 2024")
        print("Example: python validate_binary_data.py 2025 ../data")
        sys.exit(1)
    
    year = int(sys.argv[1])
    data_dir = sys.argv[2] if len(sys.argv) > 2 else "calendar10k/data"
    
    if not (1550 <= year <= 2648):
        print(f"Year {year} is outside supported range (1550-2648)")
        sys.exit(1)
    
    try:
        success = validate_year(year, data_dir)
        if success:
            print(f"\n✅ Validation PASSED for {year}")
            sys.exit(0)
        else:
            print(f"\n❌ Validation FAILED for {year}")
            sys.exit(1)
    except Exception as e:
        print(f"\n💥 Validation ERROR for {year}: {e}")
        sys.exit(1)


if __name__ == "__main__":
    main() 