#!/usr/bin/env python3
"""
Script to read and parse Powerball numbers from a CSV file.
"""

import csv
from datetime import datetime
from pathlib import Path
from typing import List, Dict, Tuple, Optional
from collections import Counter


def read_powerball_csv(filepath: str) -> List[Dict]:
    """
    Read a CSV file containing Powerball numbers.
    
    Expected CSV format:
    Draw Date,Winning Numbers,Multiplier
    09/26/2020,11 21 27 36 62 24,3
    
    Args:
        filepath: Path to the CSV file
        
    Returns:
        List of dictionaries containing parsed powerball data
    """
    powerball_data = []
    
    try:
        with open(filepath, 'r', encoding='utf-8') as csvfile:
            reader = csv.DictReader(csvfile)
            
            for row_num, row in enumerate(reader, start=2):  # start=2 because row 1 is header
                try:
                    # Skip empty rows
                    if not any(row.values()):
                        continue
                    
                    # Parse the row
                    draw_date = datetime.strptime(row['Draw Date'].strip(), '%m/%d/%Y')
                    
                    # Parse winning numbers - filter out empty strings
                    numbers_str = row['Winning Numbers'].strip()
                    winning_numbers = [int(n) for n in numbers_str.split() if n.strip()]
                    
                    multiplier = int(row['Multiplier'].strip())
                    
                    powerball_data.append({
                        'draw_date': draw_date,
                        'date_str': draw_date.strftime('%Y-%m-%d'),
                        'winning_numbers': winning_numbers,
                        'multiplier': multiplier
                    })
                except ValueError as e:
                    print(f"Warning: Skipping row {row_num} due to error: {e}")
                    print(f"  Row data: {row}")
                    continue
                
    except FileNotFoundError:
        print(f"Error: File '{filepath}' not found.")
        return []
    except Exception as e:
        print(f"Error: Failed to read CSV file - {e}")
        return []
    
    return powerball_data


def display_powerball_data(data: List[Dict]) -> None:
    """
    Display powerball data in a formatted table.
    
    Args:
        data: List of powerball data dictionaries
    """
    if not data:
        print("No data to display.")
        return
    
    print(f"{'Draw Date':<12} {'Winning Numbers':<30} {'Multiplier':<10}")
    print("-" * 52)
    
    for entry in data:
        numbers_str = ' '.join(map(str, entry['winning_numbers']))
        print(f"{entry['date_str']:<12} {numbers_str:<30} {entry['multiplier']:<10}")


def _get_unweighted_top_numbers(draws: List[Dict]) -> Tuple[List[int], Optional[int]]:
    """Return top 5 white balls and top Powerball from a draw list."""
    if not draws:
        return [], None

    white_counter = Counter()
    powerball_counter = Counter()

    for entry in draws:
        numbers = entry['winning_numbers']
        for number in numbers[:5]:
            white_counter[number] += 1

        if len(numbers) >= 6:
            powerball_counter[numbers[5]] += 1

    top_white = [number for number, _ in white_counter.most_common(5)]
    top_powerball = powerball_counter.most_common(1)[0][0] if powerball_counter else None

    return top_white, top_powerball


def _get_recency_weighted_top_numbers(draws: List[Dict]) -> Tuple[List[int], Optional[int]]:
    """Return top numbers weighted so newer draws contribute more."""
    if not draws:
        return [], None

    sorted_draws = sorted(draws, key=lambda x: x['draw_date'])
    total_draws = len(sorted_draws)

    white_scores = Counter()
    powerball_scores = Counter()

    for index, entry in enumerate(sorted_draws, start=1):
        weight = index / total_draws
        numbers = entry['winning_numbers']

        for number in numbers[:5]:
            white_scores[number] += weight

        if len(numbers) >= 6:
            powerball_scores[numbers[5]] += weight

    top_white = [number for number, _ in white_scores.most_common(5)]
    top_powerball = powerball_scores.most_common(1)[0][0] if powerball_scores else None

    return top_white, top_powerball


def _get_aggressive_recency_weighted_top_numbers(draws: List[Dict]) -> Tuple[List[int], Optional[int]]:
    """Return top numbers with an aggressive recency curve (cubic weighting)."""
    if not draws:
        return [], None

    sorted_draws = sorted(draws, key=lambda x: x['draw_date'])
    total_draws = len(sorted_draws)

    white_scores = Counter()
    powerball_scores = Counter()

    for index, entry in enumerate(sorted_draws, start=1):
        normalized_position = index / total_draws
        weight = normalized_position ** 3
        numbers = entry['winning_numbers']

        for number in numbers[:5]:
            white_scores[number] += weight

        if len(numbers) >= 6:
            powerball_scores[numbers[5]] += weight

    top_white = [number for number, _ in white_scores.most_common(5)]
    top_powerball = powerball_scores.most_common(1)[0][0] if powerball_scores else None

    return top_white, top_powerball


def analyze_most_frequent_numbers(data: List[Dict]) -> Dict:
    """
    Analyze and display the most frequent number in each slot position.
    
    Args:
        data: List of powerball data dictionaries
    """
    if not data:
        print("No data to analyze.")
        return
    
    # Initialize lists to hold numbers for each position
    num_positions = len(data[0]['winning_numbers'])
    position_numbers = [[] for _ in range(num_positions)]
    
    # Collect all numbers for each position
    for entry in data:
        for position, number in enumerate(entry['winning_numbers']):
            position_numbers[position].append(number)
    
    # Find most common number in each position
    print("\n" + "=" * 60)
    print("Most Frequent Numbers by Slot Position")
    print("=" * 60)
    
    most_frequent = []
    for position, numbers in enumerate(position_numbers, start=1):
        counter = Counter(numbers)
        most_common_num, frequency = counter.most_common(1)[0]
        most_frequent.append(most_common_num)
        percentage = (frequency / len(numbers)) * 100
        print(f"Position {position}: {most_common_num:<3} (appears {frequency} times, {percentage:.1f}%)")
    
    print("-" * 60)
    print(f"Most frequent numbers combined: {' '.join(map(str, most_frequent))}")
    
    # Analyze most frequent number across first 5 positions combined
    print("\n" + "=" * 60)
    print("Additional Analysis")
    print("=" * 60)
    
    # Combine all numbers from positions 1-5
    first_five_combined = []
    for i in range(min(5, num_positions)):
        first_five_combined.extend(position_numbers[i])
    
    top_five_numbers = []
    top_ten_numbers = []
    if first_five_combined:
        counter_first_five = Counter(first_five_combined)
        top_five_numbers = counter_first_five.most_common(5)
        top_ten_numbers = counter_first_five.most_common(10)
        print("Top 5 most drawn numbers in first 5 positions combined:")
        for rank, (number, frequency) in enumerate(top_five_numbers, start=1):
            percentage = (frequency / len(first_five_combined)) * 100
            print(f"  {rank}. {number:<3} (appears {frequency} times, {percentage:.1f}%)")
    
    # Analyze the sixth position (Powerball)
    powerball_ranked = []
    most_common_sixth = None
    if num_positions >= 6:
        sixth_position_numbers = position_numbers[5]
        counter_sixth = Counter(sixth_position_numbers)
        powerball_ranked = counter_sixth.most_common(5)
        most_common_sixth, freq_sixth = counter_sixth.most_common(1)[0]
        percentage_sixth = (freq_sixth / len(sixth_position_numbers)) * 100
        print(f"\nMost drawn number in position 6 (Powerball): {most_common_sixth} (appears {freq_sixth} times, {percentage_sixth:.1f}%)")

    # Third strategy: most frequent numbers from the most recent 104 draws
    sorted_by_date = sorted(data, key=lambda x: x['draw_date'])
    recent_104_draws = sorted_by_date[-104:]
    recent_104_top_five, recent_104_powerball = _get_unweighted_top_numbers(recent_104_draws)

    # Fourth strategy: weighted-by-recency across all draws
    weighted_top_five, weighted_powerball = _get_recency_weighted_top_numbers(data)

    # Fifth strategy: aggressive weighted-by-recency across all draws
    aggressive_weighted_top_five, aggressive_weighted_powerball = _get_aggressive_recency_weighted_top_numbers(data)
    
    # Return statistics for prediction
    return {
        'top_five_combined': [num for num, freq in top_five_numbers] if first_five_combined else [],
        'top_ten_combined': [num for num, freq in top_ten_numbers],
        'most_common_powerball': most_common_sixth,
        'powerball_ranked': [num for num, freq in powerball_ranked],
        'recent_104_top_five': recent_104_top_five,
        'recent_104_powerball': recent_104_powerball,
        'weighted_top_five': weighted_top_five,
        'weighted_powerball': weighted_powerball,
        'aggressive_weighted_top_five': aggressive_weighted_top_five,
        'aggressive_weighted_powerball': aggressive_weighted_powerball
    }


def generate_suggested_combination(stats: dict) -> None:
    """
    Generate a suggested combination based on statistical analysis.
    
    Args:
        stats: Dictionary containing statistical analysis results
    """
    print("\n" + "=" * 60)
    print("Suggested Combination Based on Statistical Analysis")
    print("=" * 60)
    
    print("\n⚠️  DISCLAIMER: This is purely statistical analysis.")
    print("Past results do NOT predict future outcomes.")
    print("Each draw is random and independent.\n")
    
    top_five = stats['top_five_combined']
    top_ten = stats.get('top_ten_combined', [])
    powerball = stats['most_common_powerball']
    ranked_powerballs = stats.get('powerball_ranked', [])
    recent_104_top_five = stats.get('recent_104_top_five', [])
    recent_104_powerball = stats.get('recent_104_powerball')
    weighted_top_five = stats.get('weighted_top_five', [])
    weighted_powerball = stats.get('weighted_powerball')
    aggressive_weighted_top_five = stats.get('aggressive_weighted_top_five', [])
    aggressive_weighted_powerball = stats.get('aggressive_weighted_powerball')
    
    if len(top_five) >= 5 and powerball:
        # Use the top 5 most frequent numbers from positions 1-5
        primary_numbers = sorted(top_five[:5])
        primary_powerball = powerball

        # Build a secondary set from the next-most-frequent historical numbers.
        secondary_pool = top_ten[5:10]
        if len(secondary_pool) < 5:
            secondary_pool = top_ten[:5]

        secondary_numbers = sorted(secondary_pool[:5])
        secondary_powerball = ranked_powerballs[1] if len(ranked_powerballs) > 1 else primary_powerball

        third_numbers = sorted(recent_104_top_five[:5]) if len(recent_104_top_five) >= 5 else []
        third_powerball = recent_104_powerball

        fourth_numbers = sorted(weighted_top_five[:5]) if len(weighted_top_five) >= 5 else []
        fourth_powerball = weighted_powerball

        fifth_numbers = sorted(aggressive_weighted_top_five[:5]) if len(aggressive_weighted_top_five) >= 5 else []
        fifth_powerball = aggressive_weighted_powerball
        
        print("Primary set (most frequent):")
        print(f"  White balls: {' '.join(map(str, primary_numbers))}")
        print(f"  Powerball: {primary_powerball}")

        print("\nSecondary set (next-most frequent):")
        print(f"  White balls: {' '.join(map(str, secondary_numbers))}")
        print(f"  Powerball: {secondary_powerball}")

        if third_numbers and third_powerball:
            print("\nThird set (most frequent in recent 104 draws):")
            print(f"  White balls: {' '.join(map(str, third_numbers))}")
            print(f"  Powerball: {third_powerball}")

        if fourth_numbers and fourth_powerball:
            print("\nFourth set (weighted by recency):")
            print(f"  White balls: {' '.join(map(str, fourth_numbers))}")
            print(f"  Powerball: {fourth_powerball}")

        if fifth_numbers and fifth_powerball:
            print("\nFifth set (aggressive recency weighting):")
            print(f"  White balls: {' '.join(map(str, fifth_numbers))}")
            print(f"  Powerball: {fifth_powerball}")
        print()
        print(f"Primary combination: {' '.join(map(str, primary_numbers))} + {primary_powerball}")
        print(f"Secondary combination: {' '.join(map(str, secondary_numbers))} + {secondary_powerball}")

        if third_numbers and third_powerball:
            print(f"Third combination: {' '.join(map(str, third_numbers))} + {third_powerball}")

        if fourth_numbers and fourth_powerball:
            print(f"Fourth combination: {' '.join(map(str, fourth_numbers))} + {fourth_powerball}")

        if fifth_numbers and fifth_powerball:
            print(f"Fifth combination: {' '.join(map(str, fifth_numbers))} + {fifth_powerball}")
        print()
        print("These combinations are based on:")
        print("  • The 5 most frequently drawn numbers across all white ball positions")
        print(f"  • The most frequently drawn Powerball number ({primary_powerball})")
        print("  • A secondary set from the next-most-frequent white ball and Powerball values")
        print("  • Recent-window frequency (last 104 draws)")
        print("  • Recency-weighted scoring where newer draws count more")
        print("  • Aggressive recency weighting using a cubic curve")
    else:
        print("Insufficient data to generate a suggestion.")


def main():
    """Main function."""
    # Default to 'powerball_numbers.csv' in the same directory as the script
    script_dir = Path(__file__).parent
    csv_file = script_dir / 'powerball_numbers.csv'
    
    # You can also specify a different file path here
    # csv_file = Path('path/to/your/file.csv')
    
    print(f"Reading Powerball numbers from: {csv_file}\n")
    
    powerball_data = read_powerball_csv(str(csv_file))
    
    if powerball_data:
        print(f"Successfully loaded {len(powerball_data)} records.\n")
        display_powerball_data(powerball_data)
        stats = analyze_most_frequent_numbers(powerball_data)
        generate_suggested_combination(stats)
    else:
        print("Failed to load powerball data.")


if __name__ == '__main__':
    main()
