"""
Generate Binary Calendar Data

Django management command to generate binary calendar files.
Supports multiple formats and batch generation.
"""

import os
from datetime import datetime
from django.core.management.base import BaseCommand, CommandError
from django.conf import settings

from calendar10k.scripts.binary_calendar import PositionalBinaryCalendarGenerator, OptimizedBinaryCalendarGenerator


class Command(BaseCommand):
    help = 'Generate binary calendar data files for specified years. Use --all to generate all supported years (1550-2648).'

    def add_arguments(self, parser):
        parser.add_argument('years', nargs='*', type=int,
                           help='Years to generate (default: current year)')
        
        parser.add_argument('--all', action='store_true',
                           help='Generate files for all supported years (1550-2648)')
        
        parser.add_argument('--data-dir', type=str,
                           default='calendar10k/data',
                           help='Output directory for binary files')
        
        parser.add_argument('--overwrite', action='store_true',
                           help='Overwrite existing files')
        
        parser.add_argument('--format', choices=['positional', 'optimized', 'original'],
                           default='positional',
                           help='Binary format to use (default: positional - latest version)')
        
        parser.add_argument('--format-version', type=str,
                           help='File format version (defaults to latest for selected format)')
        
        parser.add_argument('--range', type=str,
                           help='Year range in format "start:end" (e.g., "2020:2030")')
        
        parser.add_argument('--list-existing', action='store_true',
                           help='List existing binary files and exit')

    def handle(self, *args, **options):
        # Handle list existing files
        if options['list_existing']:
            self.list_existing_files(options['data_dir'])
            return

        # Validate conflicting options
        if options.get('all') and (options['years'] or options.get('range')):
            raise CommandError('Cannot use --all with specific years or --range')

        # Determine years to generate
        years = self.get_years_to_generate(options)
        
        if not years:
            self.stdout.write(self.style.ERROR('No years specified'))
            return

        # Filter existing files unless overwrite is specified
        years_to_generate = self.filter_existing_years(years, options['data_dir'], options['overwrite'])
        
        if not years_to_generate:
            self.stdout.write('No new files to generate.')
            return

        # Initialize generator based on format
        generator = self.get_generator(options['format'], options['data_dir'])
        
        # Determine version
        version = self.get_format_version(options['format'], options.get('format_version'))

        # Generate files
        self.generate_files(generator, years_to_generate, version, options['format'])

    def get_years_to_generate(self, options):
        """Determine which years to generate."""
        years = []
        
        if options.get('all'):
            # Generate all supported years
            years = list(range(1550, 2649))  # 1550-2648 inclusive
            self.stdout.write(f'Generating files for all supported years: {min(years)}-{max(years)} ({len(years)} files)')
        elif options.get('range'):
            start_str, end_str = options['range'].split(':')
            start_year = int(start_str)
            end_year = int(end_str)
            years = list(range(start_year, end_year + 1))
        elif options['years']:
            years = options['years']
        else:
            years = [datetime.now().year]
        
        # Validate year range
        valid_years = [y for y in years if 1550 <= y <= 2648]
        invalid_years = [y for y in years if y not in valid_years]
        
        if invalid_years:
            self.stdout.write(
                self.style.WARNING(f'Skipping invalid years: {invalid_years}')
            )
        
        return valid_years

    def filter_existing_years(self, years, data_dir, overwrite):
        """Filter out years that already have files unless overwrite is enabled."""
        if overwrite:
            return years
        
        existing_years = []
        new_years = []
        
        for year in years:
            filepath = os.path.join(data_dir, f"{year}.bin")
            if os.path.exists(filepath):
                existing_years.append(year)
            else:
                new_years.append(year)
        
        if existing_years:
            self.stdout.write(f'Found existing files for years: {existing_years}')
            self.stdout.write('Use --overwrite to overwrite existing files')
        
        return new_years

    def get_generator(self, format_type, data_dir):
        """Get the appropriate generator for the format."""
        if format_type == 'positional':
            return PositionalBinaryCalendarGenerator(data_dir)
        elif format_type == 'optimized':
            return OptimizedBinaryCalendarGenerator(data_dir)
        else:
            raise CommandError(f'Unsupported format: {format_type}')

    def get_format_version(self, format_type, custom_version):
        """Determine the version string to use."""
        if custom_version:
            return custom_version
        
        # Use latest versions by default
        version_map = {
            'positional': '4.0.0',  # Latest positional format with HH:MM:SS precision
            'optimized': '1.0.0',
            'original': '0.1.0'
        }
        
        return version_map.get(format_type, '4.0.0')

    def generate_files(self, generator, years, version, format_type):
        """Generate binary files for the specified years."""
        self.stdout.write('')
        self.stdout.write('=' * 60)
        self.stdout.write(self.style.SUCCESS(f'POSITIONAL Binary Calendar Generator (Version {version})'))
        self.stdout.write('=' * 60)
        self.stdout.write(f'Output directory: {os.path.abspath(generator.data_dir)}')
        self.stdout.write(f'Format: {format_type.title()} (~60% smaller than previous formats)')
        self.stdout.write(f'Files to generate: {len(years)}')
        self.stdout.write('')

        success_count = 0
        error_count = 0
        total_old_size = 0
        total_new_size = 0

        for i, year in enumerate(years, 1):
            try:
                self.stdout.write(f'[{i:3d}/{len(years):3d}] Generating {year}.bin... ', ending='')
                
                # Generate the file
                if format_type == 'positional':
                    filename = generator.generate_positional_year_file(year, version)
                elif format_type == 'optimized':
                    filename = generator.generate_optimized_year_file(year, version)
                
                # Get file size for reporting
                file_size = os.path.getsize(filename)
                total_new_size += file_size
                
                # Calculate old format size for comparison
                days_in_year = 366 if self.is_leap_year(year) else 365
                old_size = 8 + (days_in_year * 8)  # Original format size
                total_old_size += old_size
                
                reduction_percent = (old_size - file_size) / old_size * 100
                
                self.stdout.write(self.style.SUCCESS(f'✓ {file_size:,} bytes ({reduction_percent:.1f}% reduction)'))
                success_count += 1
                
            except Exception as e:
                self.stdout.write(self.style.ERROR(f'✗ Error: {str(e)}'))
                error_count += 1

        # Summary
        self.stdout.write('')
        self.stdout.write('=' * 60)
        self.stdout.write(self.style.SUCCESS('GENERATION SUMMARY'))
        self.stdout.write('=' * 60)
        self.stdout.write(f'Success: {success_count} files')
        self.stdout.write(f'Errors:  {error_count} files')
        
        if total_new_size > 0:
            self.stdout.write('')
            self.stdout.write('Size Analysis:')
            self.stdout.write(f'  Original format size: {total_old_size:,} bytes ({total_old_size/1024/1024:.1f} MB)')
            self.stdout.write(f'  Positional file size: {total_new_size:,} bytes ({total_new_size/1024/1024:.1f} MB)')
            total_saved = total_old_size - total_new_size
            total_reduction = total_saved / total_old_size * 100
            self.stdout.write(f'  Space saved:          {total_saved:,} bytes ({total_reduction:.1f}% reduction)')
            avg_size = total_new_size // max(success_count, 1)
            self.stdout.write(f'  Average per file:     {avg_size:,} bytes')

        if success_count > 0:
            self.stdout.write('')
            self.stdout.write(self.style.SUCCESS('🎉 All files generated successfully!'))
            self.stdout.write('')
            self.stdout.write('Next steps:')
            sample_year = years[0] if years else 2025
            self.stdout.write(f'  • Test with: python calendar10k/scripts/calendar_viewer.py --date {sample_year}-01-05')
            self.stdout.write(f'  • View data: python calendar10k/scripts/calendar_viewer.py --year {sample_year}')
            self.stdout.write(f'  • Files are in: {os.path.abspath(generator.data_dir)}')

    def list_existing_files(self, data_dir):
        """List existing binary calendar files."""
        if not os.path.exists(data_dir):
            self.stdout.write(f'Data directory does not exist: {data_dir}')
            return

        files = [f for f in os.listdir(data_dir) if f.endswith('.bin') and f[:-4].isdigit()]
        
        if not files:
            self.stdout.write(f'No binary calendar files found in: {data_dir}')
            return

        files.sort()
        years = [int(f[:-4]) for f in files]
        
        self.stdout.write(f'Found {len(files)} binary calendar files:')
        self.stdout.write(f'Directory: {os.path.abspath(data_dir)}')
        self.stdout.write('')
        
        total_size = 0
        for filename in files[:10]:  # Show first 10
            filepath = os.path.join(data_dir, filename)
            size = os.path.getsize(filepath)
            total_size += size
            year = int(filename[:-4])
            self.stdout.write(f'  {filename}: {size:,} bytes')
        
        if len(files) > 10:
            self.stdout.write(f'  ... and {len(files) - 10} more files')
            # Estimate total size
            for filename in files[10:]:
                filepath = os.path.join(data_dir, filename)
                total_size += os.path.getsize(filepath)
        
        self.stdout.write('')
        self.stdout.write(f'Year range: {min(years)} - {max(years)}')
        self.stdout.write(f'Total size: {total_size:,} bytes ({total_size/1024/1024:.1f} MB)')

    def is_leap_year(self, year):
        """Check if year is a leap year."""
        return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) 