#!/usr/bin/env python
"""
ローカル画像をS3に移行するスクリプト

使用方法:
    python migrate_local_images_to_s3.py

機能:
- GoalImagesのローカル画像をS3に移行
- CheckinImagesのローカル画像をS3に移行  
- 標準画像（start/goal/rule/map）も移行対象（存在する場合）
- 移行後にデータベースのパスを更新
- バックアップとロールバック機能付き
"""

import os
import sys
import django
from pathlib import Path
import json
from datetime import datetime
import shutil
import traceback

# Django settings setup
BASE_DIR = Path(__file__).resolve().parent
sys.path.append(str(BASE_DIR))
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
django.setup()

from django.conf import settings
from rog.models import GoalImages, CheckinImages
from rog.services.s3_service import S3Service
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile
import logging

# ロギング設定
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(f'migration_log_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

class ImageMigrationService:
    """画像移行サービス"""
    
    def __init__(self):
        self.s3_service = S3Service()
        self.migration_stats = {
            'total_goal_images': 0,
            'total_checkin_images': 0,
            'successfully_migrated_goal': 0,
            'successfully_migrated_checkin': 0,
            'failed_migrations': [],
            'migration_details': []
        }
        self.backup_file = f'migration_backup_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json'
        
    def backup_database_state(self):
        """移行前のデータベース状態をバックアップ"""
        logger.info("データベース状態をバックアップ中...")
        
        backup_data = {
            'goal_images': [],
            'checkin_images': [],
            'migration_timestamp': datetime.now().isoformat()
        }
        
        # GoalImages のバックアップ
        for goal_img in GoalImages.objects.all():
            backup_data['goal_images'].append({
                'id': goal_img.id,
                'original_path': str(goal_img.goalimage) if goal_img.goalimage else None,
                'user_id': goal_img.user.id,
                'team_name': goal_img.team_name,
                'event_code': goal_img.event_code,
                'cp_number': goal_img.cp_number
            })
            
        # CheckinImages のバックアップ
        for checkin_img in CheckinImages.objects.all():
            backup_data['checkin_images'].append({
                'id': checkin_img.id,
                'original_path': str(checkin_img.checkinimage) if checkin_img.checkinimage else None,
                'user_id': checkin_img.user.id,
                'team_name': checkin_img.team_name,
                'event_code': checkin_img.event_code,
                'cp_number': checkin_img.cp_number
            })
        
        with open(self.backup_file, 'w', encoding='utf-8') as f:
            json.dump(backup_data, f, ensure_ascii=False, indent=2)
            
        logger.info(f"バックアップ完了: {self.backup_file}")
        return backup_data
    
    def migrate_goal_images(self):
        """ゴール画像をS3に移行"""
        logger.info("=== ゴール画像の移行開始 ===")
        
        goal_images = GoalImages.objects.filter(goalimage__isnull=False).exclude(goalimage='')
        self.migration_stats['total_goal_images'] = goal_images.count()
        
        logger.info(f"移行対象のゴール画像: {self.migration_stats['total_goal_images']}件")
        
        for goal_img in goal_images:
            try:
                logger.info(f"処理中: GoalImage ID={goal_img.id}, Path={goal_img.goalimage}")
                
                # ローカルファイルパスの構築
                local_file_path = os.path.join(settings.MEDIA_ROOT, str(goal_img.goalimage))
                
                if not os.path.exists(local_file_path):
                    logger.warning(f"ファイルが見つかりません: {local_file_path}")
                    self.migration_stats['failed_migrations'].append({
                        'type': 'goal',
                        'id': goal_img.id,
                        'reason': 'File not found',
                        'path': local_file_path
                    })
                    continue
                
                # ファイルを読み込み
                with open(local_file_path, 'rb') as f:
                    file_content = f.read()
                
                # ContentFileとして準備
                file_name = os.path.basename(local_file_path)
                content_file = ContentFile(file_content, name=file_name)
                
                # S3にアップロード（ゴール画像として扱う）
                s3_url = self.s3_service.upload_checkin_image(
                    image_file=content_file,
                    event_code=goal_img.event_code,
                    team_code=goal_img.team_name,
                    cp_number=goal_img.cp_number,
                    is_goal=True  # ゴール画像フラグ
                )
                
                if s3_url:
                    # データベースを更新（S3パスを保存）
                    original_path = str(goal_img.goalimage)
                    goal_img.goalimage = s3_url.replace(f'https://{settings.AWS_S3_CUSTOM_DOMAIN}/', '')
                    goal_img.save()
                    
                    self.migration_stats['successfully_migrated_goal'] += 1
                    self.migration_stats['migration_details'].append({
                        'type': 'goal',
                        'id': goal_img.id,
                        'original_path': original_path,
                        'new_s3_url': s3_url,
                        'local_file': local_file_path
                    })
                    
                    logger.info(f"✅ 成功: {file_name} -> {s3_url}")
                    
                else:
                    raise Exception("S3アップロードが失敗しました")
                    
            except Exception as e:
                logger.error(f"❌ エラー: GoalImage ID={goal_img.id}, Error={str(e)}")
                logger.error(traceback.format_exc())
                self.migration_stats['failed_migrations'].append({
                    'type': 'goal',
                    'id': goal_img.id,
                    'reason': str(e),
                    'path': str(goal_img.goalimage)
                })
    
    def migrate_checkin_images(self):
        """チェックイン画像をS3に移行"""
        logger.info("=== チェックイン画像の移行開始 ===")
        
        checkin_images = CheckinImages.objects.filter(checkinimage__isnull=False).exclude(checkinimage='')
        self.migration_stats['total_checkin_images'] = checkin_images.count()
        
        logger.info(f"移行対象のチェックイン画像: {self.migration_stats['total_checkin_images']}件")
        
        for checkin_img in checkin_images:
            try:
                logger.info(f"処理中: CheckinImage ID={checkin_img.id}, Path={checkin_img.checkinimage}")
                
                # ローカルファイルパスの構築
                local_file_path = os.path.join(settings.MEDIA_ROOT, str(checkin_img.checkinimage))
                
                if not os.path.exists(local_file_path):
                    logger.warning(f"ファイルが見つかりません: {local_file_path}")
                    self.migration_stats['failed_migrations'].append({
                        'type': 'checkin',
                        'id': checkin_img.id,
                        'reason': 'File not found',
                        'path': local_file_path
                    })
                    continue
                
                # ファイルを読み込み
                with open(local_file_path, 'rb') as f:
                    file_content = f.read()
                
                # ContentFileとして準備
                file_name = os.path.basename(local_file_path)
                content_file = ContentFile(file_content, name=file_name)
                
                # S3にアップロード
                s3_url = self.s3_service.upload_checkin_image(
                    image_file=content_file,
                    event_code=checkin_img.event_code,
                    team_code=checkin_img.team_name,
                    cp_number=checkin_img.cp_number
                )
                
                if s3_url:
                    # データベースを更新（S3パスを保存）
                    original_path = str(checkin_img.checkinimage)
                    checkin_img.checkinimage = s3_url.replace(f'https://{settings.AWS_S3_CUSTOM_DOMAIN}/', '')
                    checkin_img.save()
                    
                    self.migration_stats['successfully_migrated_checkin'] += 1
                    self.migration_stats['migration_details'].append({
                        'type': 'checkin',
                        'id': checkin_img.id,
                        'original_path': original_path,
                        'new_s3_url': s3_url,
                        'local_file': local_file_path
                    })
                    
                    logger.info(f"✅ 成功: {file_name} -> {s3_url}")
                    
                else:
                    raise Exception("S3アップロードが失敗しました")
                    
            except Exception as e:
                logger.error(f"❌ エラー: CheckinImage ID={checkin_img.id}, Error={str(e)}")
                logger.error(traceback.format_exc())
                self.migration_stats['failed_migrations'].append({
                    'type': 'checkin',
                    'id': checkin_img.id,
                    'reason': str(e),
                    'path': str(checkin_img.checkinimage)
                })
    
    def migrate_standard_images(self):
        """規定画像をS3に移行（存在する場合）"""
        logger.info("=== 規定画像の移行チェック開始 ===")
        
        standard_types = ['start', 'goal', 'rule', 'map']
        media_root = Path(settings.MEDIA_ROOT)
        
        # 各イベントフォルダをチェック
        events_found = set()
        
        # GoalImagesとCheckinImagesから一意のイベントコードを取得
        goal_events = set(GoalImages.objects.values_list('event_code', flat=True))
        checkin_events = set(CheckinImages.objects.values_list('event_code', flat=True))
        all_events = goal_events.union(checkin_events)
        
        logger.info(f"検出されたイベント: {all_events}")
        
        for event_code in all_events:
            # 各標準画像タイプをチェック
            for image_type in standard_types:
                # 一般的な画像拡張子をチェック
                for ext in ['.jpg', '.jpeg', '.png', '.JPG', '.JPEG', '.PNG']:
                    # 複数の可能なパスパターンをチェック
                    possible_paths = [
                        media_root / f'{event_code}_{image_type}{ext}',
                        media_root / event_code / f'{image_type}{ext}',
                        media_root / 'standards' / event_code / f'{image_type}{ext}',
                        media_root / f'{image_type}_{event_code}{ext}',
                    ]
                    
                    for possible_path in possible_paths:
                        if possible_path.exists():
                            try:
                                logger.info(f"規定画像発見: {possible_path}")
                                
                                # ファイルを読み込み
                                with open(possible_path, 'rb') as f:
                                    file_content = f.read()
                                
                                # ContentFileとして準備
                                content_file = ContentFile(file_content, name=possible_path.name)
                                
                                # S3にアップロード
                                s3_url = self.s3_service.upload_standard_image(
                                    image_file=content_file,
                                    event_code=event_code,
                                    image_type=image_type
                                )
                                
                                if s3_url:
                                    self.migration_stats['migration_details'].append({
                                        'type': 'standard',
                                        'event_code': event_code,
                                        'image_type': image_type,
                                        'original_path': str(possible_path),
                                        'new_s3_url': s3_url
                                    })
                                    
                                    logger.info(f"✅ 規定画像移行成功: {possible_path.name} -> {s3_url}")
                                    break  # 同じタイプの画像が見つかったら他のパスはスキップ
                                    
                            except Exception as e:
                                logger.error(f"❌ 規定画像移行エラー: {possible_path}, Error={str(e)}")
                                self.migration_stats['failed_migrations'].append({
                                    'type': 'standard',
                                    'event_code': event_code,
                                    'image_type': image_type,
                                    'reason': str(e),
                                    'path': str(possible_path)
                                })
    
    def generate_migration_report(self):
        """移行レポートを生成"""
        logger.info("=== 移行レポート生成 ===")
        
        report = {
            'migration_timestamp': datetime.now().isoformat(),
            'summary': {
                'total_goal_images': self.migration_stats['total_goal_images'],
                'successfully_migrated_goal': self.migration_stats['successfully_migrated_goal'],
                'total_checkin_images': self.migration_stats['total_checkin_images'],
                'successfully_migrated_checkin': self.migration_stats['successfully_migrated_checkin'],
                'total_failed': len(self.migration_stats['failed_migrations']),
                'success_rate_goal': (
                    self.migration_stats['successfully_migrated_goal'] / max(self.migration_stats['total_goal_images'], 1) * 100
                ),
                'success_rate_checkin': (
                    self.migration_stats['successfully_migrated_checkin'] / max(self.migration_stats['total_checkin_images'], 1) * 100
                )
            },
            'details': self.migration_stats['migration_details'],
            'failures': self.migration_stats['failed_migrations']
        }
        
        # レポートファイルの保存
        report_file = f'migration_report_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json'
        with open(report_file, 'w', encoding='utf-8') as f:
            json.dump(report, f, ensure_ascii=False, indent=2)
        
        # コンソール出力
        print("\n" + "="*60)
        print("🎯 画像S3移行レポート")
        print("="*60)
        print(f"📊 ゴール画像: {report['summary']['successfully_migrated_goal']}/{report['summary']['total_goal_images']} "
              f"({report['summary']['success_rate_goal']:.1f}%)")
        print(f"📊 チェックイン画像: {report['summary']['successfully_migrated_checkin']}/{report['summary']['total_checkin_images']} "
              f"({report['summary']['success_rate_checkin']:.1f}%)")
        print(f"❌ 失敗数: {report['summary']['total_failed']}")
        print(f"📄 詳細レポート: {report_file}")
        print(f"💾 バックアップファイル: {self.backup_file}")
        
        if report['summary']['total_failed'] > 0:
            print("\n⚠️  失敗した移行:")
            for failure in self.migration_stats['failed_migrations'][:5]:  # 最初の5件のみ表示
                print(f"   - {failure['type']} ID={failure.get('id', 'N/A')}: {failure['reason']}")
            if len(self.migration_stats['failed_migrations']) > 5:
                print(f"   ... 他 {len(self.migration_stats['failed_migrations']) - 5} 件")
        
        return report
    
    def run_migration(self):
        """メイン移行処理"""
        logger.info("🚀 画像S3移行開始")
        print("🚀 画像S3移行を開始します...")
        
        try:
            # 1. バックアップ
            self.backup_database_state()
            
            # 2. ゴール画像移行
            self.migrate_goal_images()
            
            # 3. チェックイン画像移行
            self.migrate_checkin_images()
            
            # 4. 規定画像移行
            self.migrate_standard_images()
            
            # 5. レポート生成
            report = self.generate_migration_report()
            
            logger.info("✅ 移行完了")
            print("\n✅ 移行が完了しました！")
            
            return report
            
        except Exception as e:
            logger.error(f"💥 移行中に重大なエラーが発生: {str(e)}")
            logger.error(traceback.format_exc())
            print(f"\n💥 移行エラー: {str(e)}")
            print(f"バックアップファイル: {self.backup_file}")
            raise

def main():
    """メイン関数"""
    print("="*60)
    print("🔄 ローカル画像S3移行ツール")
    print("="*60)
    print("このツールは以下を実行します:")
    print("1. データベースの現在の状態をバックアップ")
    print("2. GoalImages のローカル画像をS3に移行")
    print("3. CheckinImages のローカル画像をS3に移行")
    print("4. 標準画像（存在する場合）をS3に移行")
    print("5. 移行レポートの生成")
    print()
    
    # 確認プロンプト
    confirm = input("移行を開始しますか？ [y/N]: ").strip().lower()
    if confirm not in ['y', 'yes']:
        print("移行をキャンセルしました。")
        return
    
    # 移行実行
    migration_service = ImageMigrationService()
    migration_service.run_migration()

if __name__ == "__main__":
    main()
