#!/usr/bin/env python3
"""
最終クリーン移行プログラム
不正な写真記録データを除外し、正確なGPS記録のみを移行する
"""

import os
import sys
import psycopg2
from datetime import datetime, time, timedelta
import pytz

def get_event_date(event_code):
    """イベントコードに基づいてイベント日付を返す"""
    event_dates = {
        '美濃加茂': datetime(2024, 5, 19),  # 修正済み
        '岐阜市': datetime(2024, 4, 28),
        '大垣２': datetime(2024, 4, 20),
        '各務原': datetime(2024, 3, 24),
        '下呂': datetime(2024, 3, 10),
        '中津川': datetime(2024, 3, 2),
        '揖斐川': datetime(2024, 2, 18),
        '高山': datetime(2024, 2, 11),
        '大垣': datetime(2024, 1, 27),
        '多治見': datetime(2024, 1, 20),
        # 2024年のその他のイベント
        '養老ロゲ': datetime(2024, 6, 1),
        '郡上': datetime(2024, 11, 3),  # 郡上イベント追加
    }
    return event_dates.get(event_code)

def convert_utc_to_jst(utc_timestamp):
    """UTC時刻をJST時刻に変換"""
    if not utc_timestamp:
        return None
    
    utc_tz = pytz.UTC
    jst_tz = pytz.timezone('Asia/Tokyo')
    
    # UTCタイムゾーン情報を付加
    if utc_timestamp.tzinfo is None:
        utc_timestamp = utc_tz.localize(utc_timestamp)
    
    # JSTに変換
    return utc_timestamp.astimezone(jst_tz).replace(tzinfo=None)

def parse_goal_time(goal_time_str, event_date_str):
    """goal_time文字列を適切なdatetimeに変換"""
    if not goal_time_str:
        return None
    
    try:
        # フルの日時形式の場合（UTCからJST変換）
        if len(goal_time_str) > 10:
            dt = datetime.fromisoformat(goal_time_str.replace('Z', '+00:00'))
            return convert_utc_to_jst(dt)
        
        # 時刻のみの場合（JSTとして扱う、1日前の問題を修正）
        elif ':' in goal_time_str:
            time_obj = datetime.strptime(goal_time_str, '%H:%M:%S').time()
            # イベント日の翌日の時刻として扱う（競技が翌日まで続くため）
            event_date = datetime.strptime(event_date_str, '%Y-%m-%d').date()
            next_day = datetime.combine(event_date, time_obj) + timedelta(days=1)
            return next_day
        
        return None
    except Exception as e:
        print(f"goal_time解析エラー: {goal_time_str} - {e}")
        return None

def create_rog_event_if_not_exists(cursor, event_code):
    """rog_eventレコードが存在しない場合は作成"""
    cursor.execute("SELECT COUNT(*) FROM rog_event WHERE event_name = %s", (event_code,))
    if cursor.fetchone()[0] == 0:
        event_date = get_event_date(event_code)
        if event_date:
            start_time = f"{event_date} 08:00:00"
            end_time = f"{event_date} 18:00:00"
            
            cursor.execute("""
                INSERT INTO rog_event (event_name, start_time, end_time)
                VALUES (%s, %s, %s)
            """, (event_code, start_time, end_time))
            print(f"rog_eventに{event_code}イベントを作成しました")

def create_rog_team_if_not_exists(cursor, zekken, event_code):
    """rog_teamレコードが存在しない場合は作成"""
    cursor.execute("""
        SELECT COUNT(*) FROM rog_team 
        WHERE team_number = %s AND event_name = %s
    """, (zekken, event_code))
    
    if cursor.fetchone()[0] == 0:
        cursor.execute("""
            INSERT INTO rog_team (team_number, event_name, team_name)
            VALUES (%s, %s, %s)
        """, (zekken, event_code, f"チーム{zekken}"))
        print(f"rog_teamに{zekken}チームを作成しました")

def clean_target_database(target_cursor):
    """ターゲットデータベースの既存データをクリーンアップ"""
    print("=== ターゲットデータベースのクリーンアップ ===")
    
    # 外部キー制約を一時的に無効化
    target_cursor.execute("SET session_replication_role = replica;")
    
    try:
        # 1. rog_gpscheckinデータを削除
        target_cursor.execute("DELETE FROM rog_gpscheckin")
        deleted_checkins = target_cursor.rowcount
        print(f"チェックインデータを削除: {deleted_checkins}件")
        
        # 2. 関連テーブルの削除
        target_cursor.execute("DELETE FROM rog_member")
        deleted_members = target_cursor.rowcount
        print(f"メンバーデータを削除: {deleted_members}件")
        
        target_cursor.execute("DELETE FROM rog_entry")
        deleted_entries = target_cursor.rowcount
        print(f"エントリーデータを削除: {deleted_entries}件")
        
        # 3. rog_teamデータを削除
        target_cursor.execute("DELETE FROM rog_team")
        deleted_teams = target_cursor.rowcount
        print(f"チームデータを削除: {deleted_teams}件")
        
        # 4. rog_eventデータを削除
        target_cursor.execute("DELETE FROM rog_event")
        deleted_events = target_cursor.rowcount
        print(f"イベントデータを削除: {deleted_events}件")
        
    finally:
        # 外部キー制約を再有効化
        target_cursor.execute("SET session_replication_role = DEFAULT;")

def migrate_gps_data(source_cursor, target_cursor):
    """GPS記録データのみを移行（写真記録データは除外）"""
    print("\n=== GPS記録データの移行 ===")
    
    # GPS記録のみを取得（不正な写真記録データを除外）
    source_cursor.execute("""
        SELECT 
            zekken_number,
            event_code,
            cp_number,
            create_at,
            goal_time,
            serial_number
        FROM gps_information 
        WHERE serial_number < 20000  -- GPS記録のみ（写真記録を除外）
        ORDER BY serial_number
    """)
    
    gps_records = source_cursor.fetchall()
    print(f"GPS記録取得: {len(gps_records)}件")
    
    migrated_count = 0
    skipped_count = 0
    error_count = 0
    
    for record in gps_records:
        zekken_number, event_code, cp_number, create_at, goal_time, serial_number = record
        
        try:
            # イベント日の取得
            event_date = get_event_date(event_code)
            if not event_date:
                print(f"未知のイベントコード: {event_code}")
                skipped_count += 1
                continue
            
            # イベントとチームの作成
            create_rog_event_if_not_exists(target_cursor, event_code)
            create_rog_team_if_not_exists(target_cursor, zekken_number, event_code)
            
            # 時刻の変換
            checkin_time = convert_utc_to_jst(create_at) if create_at else None
            record_time = checkin_time
            
            if checkin_time:
                # rog_gpscheckinに挿入
                target_cursor.execute("""
                    INSERT INTO rog_gpscheckin (
                        zekken, event_code, cp_number, checkin_time, 
                        record_time, serial_number
                    ) VALUES (%s, %s, %s, %s, %s, %s)
                """, (zekken_number, event_code, cp_number, checkin_time, record_time, serial_number))
                
                migrated_count += 1
            else:
                skipped_count += 1
                
        except Exception as e:
            print(f"移行エラー (Serial: {serial_number}): {e}")
            error_count += 1
    
    print(f"GPS移行完了: 成功 {migrated_count}件, スキップ {skipped_count}件, エラー {error_count}件")
    return migrated_count, skipped_count, error_count

def generate_migration_statistics(target_cursor):
    """移行統計情報を生成"""
    print("\n" + "="*60)
    print("📊 移行統計情報")
    print("="*60)
    
    # 1. イベント別統計
    target_cursor.execute("""
        SELECT 
            event_code,
            COUNT(*) as total_records,
            COUNT(DISTINCT zekken) as unique_teams,
            MIN(checkin_time) as earliest_checkin,
            MAX(checkin_time) as latest_checkin
        FROM rog_gpscheckin
        GROUP BY event_code
        ORDER BY total_records DESC
    """)
    
    events_stats = target_cursor.fetchall()
    print("\n📋 イベント別統計:")
    print("イベント名     記録数   チーム数  開始時刻              終了時刻")
    print("-" * 75)
    total_records = 0
    total_teams = 0
    for event, records, teams, start, end in events_stats:
        print(f"{event:<12} {records:>6}件 {teams:>6}組  {start}  {end}")
        total_records += records
        total_teams += teams
    
    print(f"\n✅ 合計: {total_records:,}件のチェックイン記録, {total_teams}チーム")
    
    # 2. 時間帯分析（美濃加茂イベント）
    print("\n⏰ 美濃加茂イベントの時間帯分析:")
    target_cursor.execute("""
        SELECT 
            EXTRACT(HOUR FROM checkin_time) as hour,
            COUNT(*) as count
        FROM rog_gpscheckin 
        WHERE event_code = '美濃加茂'
        GROUP BY EXTRACT(HOUR FROM checkin_time)
        ORDER BY hour
    """)
    
    hourly_stats = target_cursor.fetchall()
    print("時間  件数")
    print("-" * 15)
    for hour, count in hourly_stats:
        if hour is not None:
            hour_int = int(hour)
            bar = "█" * min(int(count/50), 20)
            print(f"{hour_int:>2}時 {count:>5}件 {bar}")
    
    # 3. データ品質確認
    print("\n🔍 データ品質確認:")
    
    # 0時台データの確認
    target_cursor.execute("""
        SELECT COUNT(*) 
        FROM rog_gpscheckin 
        WHERE EXTRACT(HOUR FROM checkin_time) = 0
    """)
    zero_hour_count = target_cursor.fetchone()[0]
    print(f"0時台データ: {zero_hour_count}件")
    
    # タイムゾーン確認
    target_cursor.execute("""
        SELECT 
            EXTRACT(TIMEZONE FROM checkin_time) as tz_offset,
            COUNT(*) as count
        FROM rog_gpscheckin 
        GROUP BY EXTRACT(TIMEZONE FROM checkin_time)
        ORDER BY tz_offset
    """)
    
    tz_stats = target_cursor.fetchall()
    print("タイムゾーン分布:")
    for tz_offset, count in tz_stats:
        if tz_offset is not None:
            tz_hours = int(tz_offset) // 3600
            tz_name = "JST" if tz_hours == 9 else f"UTC{tz_hours:+d}"
            print(f"  {tz_name}: {count}件")
    
    # 4. MF5-204 サンプル確認
    print("\n🎯 MF5-204 サンプルデータ:")
    target_cursor.execute("""
        SELECT 
            cp_number,
            checkin_time,
            EXTRACT(HOUR FROM checkin_time) as hour
        FROM rog_gpscheckin 
        WHERE zekken = 'MF5-204'
        ORDER BY checkin_time
        LIMIT 10
    """)
    
    mf5_samples = target_cursor.fetchall()
    if mf5_samples:
        print("CP    時刻                     JST時")
        print("-" * 40)
        for cp, time, hour in mf5_samples:
            hour_int = int(hour) if hour is not None else 0
            print(f"CP{cp:<3} {time} {hour_int:>2}")
    else:
        print("MF5-204のデータが見つかりません")

def run_verification_tests(target_cursor):
    """移行結果の検証テスト"""
    print("\n" + "="*60)
    print("🧪 移行結果検証テスト")
    print("="*60)
    
    tests_passed = 0
    tests_total = 0
    
    # テスト1: 0時台データが存在しないこと
    tests_total += 1
    target_cursor.execute("""
        SELECT COUNT(*) 
        FROM rog_gpscheckin 
        WHERE EXTRACT(HOUR FROM checkin_time) = 0
    """)
    zero_hour_count = target_cursor.fetchone()[0]
    if zero_hour_count == 0:
        print("✅ テスト1: 0時台データ除去 - 成功")
        tests_passed += 1
    else:
        print(f"❌ テスト1: 0時台データ除去 - 失敗 ({zero_hour_count}件残存)")
    
    # テスト2: MF5-204のデータが正常な時間帯に分散
    tests_total += 1
    target_cursor.execute("""
        SELECT 
            MIN(EXTRACT(HOUR FROM checkin_time)) as min_hour,
            MAX(EXTRACT(HOUR FROM checkin_time)) as max_hour,
            COUNT(DISTINCT EXTRACT(HOUR FROM checkin_time)) as hour_variety
        FROM rog_gpscheckin 
        WHERE zekken = 'MF5-204'
    """)
    mf5_stats = target_cursor.fetchone()
    if mf5_stats and mf5_stats[0] >= 9 and mf5_stats[1] <= 23 and mf5_stats[2] >= 3:
        print("✅ テスト2: MF5-204時間分散 - 成功")
        tests_passed += 1
    else:
        print(f"❌ テスト2: MF5-204時間分散 - 失敗 (範囲: {mf5_stats})")
    
    # テスト3: GPS記録のみが存在すること
    tests_total += 1
    target_cursor.execute("""
        SELECT 
            MIN(serial_number::integer) as min_serial,
            MAX(serial_number::integer) as max_serial
        FROM rog_gpscheckin
    """)
    serial_range = target_cursor.fetchone()
    if serial_range and serial_range[1] < 20000:
        print("✅ テスト3: GPS記録のみ - 成功")
        tests_passed += 1
    else:
        print(f"❌ テスト3: GPS記録のみ - 失敗 (Serial範囲: {serial_range})")
    
    # テスト4: 全データがJST時刻であること
    tests_total += 1
    target_cursor.execute("""
        SELECT COUNT(*) 
        FROM rog_gpscheckin 
        WHERE EXTRACT(TIMEZONE FROM checkin_time) != 32400  -- JST以外
    """)
    non_jst_count = target_cursor.fetchone()[0]
    if non_jst_count == 0:
        print("✅ テスト4: JST時刻統一 - 成功")
        tests_passed += 1
    else:
        print(f"❌ テスト4: JST時刻統一 - 失敗 ({non_jst_count}件が非JST)")
    
    print(f"\n🏆 検証結果: {tests_passed}/{tests_total} テスト成功")
    
    if tests_passed == tests_total:
        print("🎉 すべてのテストに合格しました！")
        return True
    else:
        print("⚠️  一部のテストに失敗しました")
        return False

def main():
    """メイン実行関数"""
    print("🚀 最終クリーン移行プログラム開始")
    print("="*60)
    
    try:
        # データベース接続
        print("データベースに接続中...")
        
        source_conn = psycopg2.connect(
            host='postgres-db',
            database='gifuroge',
            user=os.environ.get('POSTGRES_USER'),
            password=os.environ.get('POSTGRES_PASS')
        )
        
        target_conn = psycopg2.connect(
            host='postgres-db',
            database='rogdb',
            user=os.environ.get('POSTGRES_USER'),
            password=os.environ.get('POSTGRES_PASS')
        )
        
        source_cursor = source_conn.cursor()
        target_cursor = target_conn.cursor()
        
        # 1. ターゲットデータベースのクリーンアップ
        clean_target_database(target_cursor)
        target_conn.commit()
        
        # 2. GPS記録データの移行
        migrated, skipped, errors = migrate_gps_data(source_cursor, target_cursor)
        target_conn.commit()
        
        # 3. 統計情報生成
        generate_migration_statistics(target_cursor)
        
        # 4. 検証テスト実行
        verification_passed = run_verification_tests(target_cursor)
        
        # 5. 最終レポート
        print("\n" + "="*60)
        print("📋 最終移行レポート")
        print("="*60)
        print(f"✅ 移行成功: {migrated}件")
        print(f"⏭️  スキップ: {skipped}件")
        print(f"❌ エラー: {errors}件")
        print(f"🧪 検証: {'合格' if verification_passed else '不合格'}")
        print("")
        
        if verification_passed and errors == 0:
            print("🎉 移行プロジェクト完全成功！")
            print("✨ 「あり得ない通過データ」問題が根本解決されました")
        else:
            print("⚠️  移行に問題があります。ログを確認してください")
        
        source_conn.close()
        target_conn.close()
        
    except Exception as e:
        print(f"❌ 移行処理中にエラーが発生しました: {e}")
        return 1
    
    return 0

if __name__ == "__main__":
    sys.exit(main())
