import 'dart:io';
import 'dart:convert';
import 'dart:async'; // TimeoutException
import 'package:archive/archive_io.dart';
import 'package:flutter/foundation.dart'; // debugPrint
import 'package:googleapis/drive/v3.dart' as drive;
import 'package:google_sign_in/google_sign_in.dart';
import 'package:extension_google_sign_in_as_googleapis_auth/extension_google_sign_in_as_googleapis_auth.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path;
import '../models/sake_item.dart';

/// Google Driveへのバックアップ・復元を管理するサービス
///
/// 【主な機能】
/// 1. Googleアカウント認証
/// 2. Hiveデータ + 画像をZIPファイルにまとめる
/// 3. Google Driveへアップロード
/// 4. Google Driveからダウンロード
/// 5. ZIPファイルを展開してデータ復元
class BackupService {
  /// Google Sign Inインスタンス
  /// スコープ: drive.file (アプリが作成したファイルのみアクセス可能)
  final GoogleSignIn _googleSignIn = GoogleSignIn(
    scopes: [drive.DriveApi.driveFileScope],
  );

  /// バックアップファイル名
  static const String backupFileName = 'ponshu_backup.zip';

  /// 現在のGoogleアカウント情報を取得
  GoogleSignInAccount? get currentUser => _googleSignIn.currentUser;

  /// 初期化処理（サイレントサインイン試行）
  Future<void> init() async {
    try {
      await _googleSignIn.signInSilently();
    } catch (e) {
      debugPrint('⚠️ サイレントサインインエラー: $e');
    }
  }

  /// Googleアカウントでサインイン
  ///
  /// 【処理フロー】
  /// 1. Googleアカウント選択画面を表示
  /// 2. ユーザーが許可するとアカウント情報を取得
  /// 3. Google Drive APIへのアクセス権を取得
  ///
  /// 【戻り値】
  /// - 成功: GoogleSignInAccount（アカウント情報）
  /// - 失敗: null（キャンセルまたはエラー）
  Future<GoogleSignInAccount?> signIn() async {
    try {
      // 既にサインイン済みの場合は現在のアカウントを返す
      if (_googleSignIn.currentUser != null) {
        return _googleSignIn.currentUser;
      }

      // サインイン画面を表示
      final account = await _googleSignIn.signIn();
      return account;
    } catch (error) {
      debugPrint('❌ Google Sign In エラー: $error');
      return null;
    }
  }

  /// サインアウト
  Future<void> signOut() async {
    await _googleSignIn.signOut();
  }

  /// バックアップを作成してGoogle Driveにアップロード
  ///
  /// 【処理フロー】
  /// 1. ローカルのHiveデータをJSONに変換
  /// 2. 画像ファイルを収集
  /// 3. ZIPファイルに圧縮
  /// 4. Google Driveにアップロード
  /// 5. 一時ファイルを削除
  ///
  /// 【戻り値】
  /// - 成功: true
  /// - 失敗: false
  Future<bool> createBackup() async {
    try {
      // 1. サインイン確認
      final account = _googleSignIn.currentUser;
      if (account == null) {
        debugPrint('❌ サインインが必要です');
        return false;
      }

      // 2. Drive APIクライアントを作成
      final authClient = await _googleSignIn.authenticatedClient();
      if (authClient == null) {
        debugPrint('❌ 認証クライアントの取得に失敗しました');
        return false;
      }

      final driveApi = drive.DriveApi(authClient);

      // 3. バックアップZIPファイルを作成
      final zipFile = await _createBackupZip();
      if (zipFile == null) {
        debugPrint('❌ バックアップファイルの作成に失敗しました');
        return false;
      }

      // 4. Google Driveにアップロード
      final success = await _uploadToDrive(driveApi, zipFile);

      // 5. 一時ファイルを削除
      await zipFile.delete();

      return success;
    } catch (error) {
      debugPrint('❌ バックアップ作成エラー: $error');
      return false;
    }
  }

  /// ローカルデータをZIPファイルにまとめる
  ///
  /// 【ファイル構造】
  /// ponshu_backup.zip
  /// ├── sake_items.json (Hiveのデータ)
  /// ├── settings.json (アプリ設定)
  /// └── images/
  ///     ├── uuid1.jpg
  ///     ├── uuid2.jpg
  ///     └── ...
  Future<File?> _createBackupZip() async {
    try {
      final tempDir = await getTemporaryDirectory();
      final backupDir = Directory(path.join(tempDir.path, 'backup_temp'));

      // バックアップ用の一時ディレクトリを作成
      if (await backupDir.exists()) {
        await backupDir.delete(recursive: true);
      }
      await backupDir.create(recursive: true);

      // 1. Hiveデータ取得
      final sakeBox = Hive.box<SakeItem>('sake_items');
      final settingsBox = Hive.box('settings');

      // データの整合性を保つためにフラッシュ
      await sakeBox.flush();
      await settingsBox.flush();

      // 2. sake_items.jsonを作成
      final sakeItems = sakeBox.values.map((item) => {
        'id': item.id,
        'displayData': {
          'name': item.displayData.displayName,
          'brewery': item.displayData.displayBrewery,
          'prefecture': item.displayData.displayPrefecture,
          'catchCopy': item.displayData.catchCopy,
          'imagePaths': item.displayData.imagePaths,
          'rating': item.displayData.rating,
        },
        'hiddenSpecs': {
          'description': item.hiddenSpecs.description,
          'tasteStats': item.hiddenSpecs.tasteStats,
          'flavorTags': item.hiddenSpecs.flavorTags,
          'sweetnessScore': item.hiddenSpecs.sweetnessScore,
          'bodyScore': item.hiddenSpecs.bodyScore,
        },
        'userData': {
          'isFavorite': item.userData.isFavorite,
          'isUserEdited': item.userData.isUserEdited,
          'price': item.userData.price,
          'costPrice': item.userData.costPrice,
          'markup': item.userData.markup,
          'priceVariants': item.userData.priceVariants,
        },
        'gamification': {
          'ponPoints': item.gamification.ponPoints,
        },
        'metadata': {
          'createdAt': item.metadata.createdAt.toIso8601String(),
          'aiConfidence': item.metadata.aiConfidence,
        },
        'itemType': item.itemType.toString().split('.').last,
      }).toList();
      final sakeItemsFile = File(path.join(backupDir.path, 'sake_items.json'));
      await sakeItemsFile.writeAsString(json.encode(sakeItems));
      debugPrint('📄 sake_items.json 作成: ${await sakeItemsFile.length()} bytes');
      
      // 3. settings.jsonを作成
      final settings = Map<String, dynamic>.from(settingsBox.toMap());
      final settingsFile = File(path.join(backupDir.path, 'settings.json'));
      await settingsFile.writeAsString(json.encode(settings));

      // 4. 画像ファイルをコピー
      final imagesDir = Directory(path.join(backupDir.path, 'images'));
      await imagesDir.create();

      final appDir = await getApplicationDocumentsDirectory();
      final imageFiles = appDir.listSync().where((file) =>
        file.path.endsWith('.jpg') ||
        file.path.endsWith('.jpeg') ||
        file.path.endsWith('.png')
      );

      for (var imageFile in imageFiles) {
        final fileName = path.basename(imageFile.path);
        await File(imageFile.path).copy(path.join(imagesDir.path, fileName));
      }

      // 5. ZIPファイルに圧縮
      final encoder = ZipFileEncoder();
      final zipPath = path.join(tempDir.path, backupFileName);
      encoder.create(zipPath);
      encoder.addDirectory(backupDir);
      encoder.close();

      // 6. 一時ディレクトリを削除
      await backupDir.delete(recursive: true);

      debugPrint('✅ バックアップZIPファイル作成完了: $zipPath');
      return File(zipPath);
    } catch (error) {
      debugPrint('❌ ZIP作成エラー: $error');
      return null;
    }
  }

  /// Google DriveにZIPファイルをアップロード
  Future<bool> _uploadToDrive(drive.DriveApi driveApi, File zipFile) async {
    try {
      debugPrint('[BACKUP] 📤 アップロード開始: ${zipFile.lengthSync()} bytes');
      // 1. 既存のバックアップファイルを検索
      final fileList = await driveApi.files.list(
        q: "name = '$backupFileName' and trashed = false",
        spaces: 'drive',
      );

      // 2. 既存ファイルがあれば削除（完全上書き戦略）
      if (fileList.files != null && fileList.files!.isNotEmpty) {
        for (var file in fileList.files!) {
          try {
            await driveApi.files.delete(file.id!);
            debugPrint('🗑️ [BACKUP] 既存ファイルを削除: ${file.id}');
          } catch (e) {
            debugPrint('⚠️ [BACKUP] 既存ファイル削除失敗 (無視): $e');
          }
        }
      }

      // 3. 新しいファイルをアップロード
      final driveFile = drive.File();
      driveFile.name = backupFileName;

      final media = drive.Media(zipFile.openRead(), zipFile.lengthSync());

      debugPrint('[BACKUP] 🚀 Driveへ送信中...');
      final uploadedFile = await driveApi.files.create(
        driveFile,
        uploadMedia: media,
      ).timeout(const Duration(minutes: 3), onTimeout: () {
        throw TimeoutException('アップロードがタイムアウトしました (3分)');
      });

      if (uploadedFile.id == null) {
        debugPrint('❌ [BACKUP] ID取得失敗');
        return false;
      }
      
      debugPrint('✅ [BACKUP] アップロード完了 ID: ${uploadedFile.id}');

      // 4. 検証ステップ
      int retryCount = 0;
      bool verified = false;
      
      while (retryCount < 3 && !verified) {
        await Future.delayed(Duration(milliseconds: 1000 * (retryCount + 1)));
        try {
          await driveApi.files.get(uploadedFile.id!);
          verified = true;
           debugPrint('✅ [BACKUP] 検証成功');
        } catch (e) {
           debugPrint('⚠️ [BACKUP] 検証試行 ${retryCount + 1} 失敗: $e');
        }
        retryCount++;
      }

      return verified;
    } catch (error) {
      debugPrint('❌ [BACKUP] アップロードエラー: $error');
      return false;
    }
  }

  /// Google Driveからバックアップを復元
  ///
  /// 【処理フロー】
  /// 1. Google Driveからバックアップファイルをダウンロード
  /// 2. ZIPファイルを展開
  /// 3. 現在のデータを退避（pre_restore_backup.zip）
  /// 4. バックアップデータでHiveを上書き
  /// 5. 画像ファイルを復元
  ///
  /// 【注意】
  /// 現在のデータは完全に上書きされます。
  /// 事前に確認ダイアログを表示することを推奨します。
  Future<bool> restoreBackup() async {
    try {
      // 1. サインイン確認
      final account = _googleSignIn.currentUser;
      if (account == null) {
        debugPrint('❌ サインインが必要です');
        return false;
      }

      // 2. Drive APIクライアントを作成
      final authClient = await _googleSignIn.authenticatedClient();
      if (authClient == null) {
        debugPrint('❌ 認証クライアントの取得に失敗しました');
        return false;
      }

      final driveApi = drive.DriveApi(authClient);

      // 3. 現在のデータを退避
      await _createPreRestoreBackup();

      // 4. Google Driveからダウンロード
      final zipFile = await _downloadFromDrive(driveApi);
      if (zipFile == null) {
        debugPrint('❌ ダウンロードに失敗しました');
        return false;
      }

      // 5. データを復元
      final success = await _restoreFromZip(zipFile);

      // 6. 一時ファイルを削除
      await zipFile.delete();

      return success;
    } catch (error) {
      debugPrint('❌ 復元エラー: $error');
      return false;
    }
  }

  /// 復元前に現在のデータを退避
  Future<void> _createPreRestoreBackup() async {
    try {
      final tempDir = await getTemporaryDirectory();
      final backupPath = path.join(tempDir.path, 'pre_restore_backup.zip');

      final zipFile = await _createBackupZip();
      if (zipFile != null) {
        await zipFile.copy(backupPath);
        await zipFile.delete();
        debugPrint('✅ 復元前のデータを退避しました: $backupPath');
      }
    } catch (error) {
      debugPrint('⚠️ データ退避エラー: $error');
    }
  }

  /// Google DriveからZIPファイルをダウンロード
  Future<File?> _downloadFromDrive(drive.DriveApi driveApi) async {
    try {
      // 1. バックアップファイルを検索
      final fileList = await driveApi.files.list(
        q: "name = '$backupFileName' and trashed = false",
        spaces: 'drive',
      );

      if (fileList.files == null || fileList.files!.isEmpty) {
        debugPrint('❌ バックアップファイルが見つかりません');
        return null;
      }

      final fileId = fileList.files!.first.id!;

      // 2. ファイルをダウンロード
      final media = await driveApi.files.get(
        fileId,
        downloadOptions: drive.DownloadOptions.fullMedia,
      ) as drive.Media;

      final tempDir = await getTemporaryDirectory();
      final downloadPath = path.join(tempDir.path, 'downloaded_backup.zip');
      final downloadFile = File(downloadPath);

      // 3. ストリームをファイルに書き込み
      final sink = downloadFile.openWrite();
      await media.stream.pipe(sink);

      debugPrint('✅ ダウンロード完了: $downloadPath');
      return downloadFile;
    } catch (error) {
      debugPrint('❌ ダウンロードエラー: $error');
      return null;
    }
  }

  /// ZIPファイルからデータを復元
  Future<bool> _restoreFromZip(File zipFile) async {
    try {
      final tempDir = await getTemporaryDirectory();
      final extractDir = Directory(path.join(tempDir.path, 'restore_temp'));

      // 1. ZIP展開
      if (await extractDir.exists()) {
        await extractDir.delete(recursive: true);
      }
      await extractDir.create(recursive: true);

      final bytes = await zipFile.readAsBytes();
      final archive = ZipDecoder().decodeBytes(bytes);

      for (var file in archive) {
        final filename = file.name;
        final data = file.content as List<int>;
        final extractPath = path.join(extractDir.path, filename);
        
        // __MACOSX などの不要なディレクトリはスキップ
        if (filename.startsWith('__MACOSX')) continue;

        if (file.isFile) {
          final outFile = File(extractPath);
          await outFile.create(recursive: true);
          await outFile.writeAsBytes(data);
          debugPrint('📦 展開: $filename (${data.length} bytes)');
        }
      }

      // デバッグ: 展開されたファイル一覧を表示
      debugPrint('📂 展開ディレクトリの中身:');
      extractDir.listSync(recursive: true).forEach((f) => debugPrint('  - ${path.basename(f.path)}'));

      // 2. sake_items.jsonを検索 (ルートまたはサブディレクトリ)
      File? sakeItemsFile;
      final potentialFiles = extractDir.listSync(recursive: true).whereType<File>();
      
      try {
        sakeItemsFile = potentialFiles.firstWhere((f) => path.basename(f.path) == 'sake_items.json');
      } catch (e) {
        // 見つからない場合
        debugPrint('❌ sake_items.json が見つかりません');
      }

      if (sakeItemsFile != null && await sakeItemsFile.exists()) {
        final sakeItemsJson = json.decode(await sakeItemsFile.readAsString()) as List;
        debugPrint('🔍 復元対象データ数: ${sakeItemsJson.length}件');
        final sakeBox = Hive.box<SakeItem>('sake_items');

        await sakeBox.clear();
        for (var itemData in sakeItemsJson) {
          final data = itemData as Map<String, dynamic>;

          // JSONからSakeItemオブジェクトを再構築
          final item = SakeItem(
            id: data['id'] as String,
            displayData: DisplayData(
              name: data['displayData']['name'] as String,
              brewery: data['displayData']['brewery'] as String,
              prefecture: data['displayData']['prefecture'] as String,
              catchCopy: data['displayData']['catchCopy'] as String?,
              imagePaths: List<String>.from(data['displayData']['imagePaths'] as List),
              rating: data['displayData']['rating'] as double?,
            ),
            hiddenSpecs: HiddenSpecs(
              description: data['hiddenSpecs']['description'] as String?,
              tasteStats: Map<String, int>.from(data['hiddenSpecs']['tasteStats'] as Map),
              flavorTags: List<String>.from(data['hiddenSpecs']['flavorTags'] as List),
              sweetnessScore: data['hiddenSpecs']['sweetnessScore'] as double?,
              bodyScore: data['hiddenSpecs']['bodyScore'] as double?,
            ),
            userData: UserData(
              isFavorite: data['userData']['isFavorite'] as bool,
              isUserEdited: data['userData']['isUserEdited'] as bool,
              price: data['userData']['price'] as int?,
              costPrice: data['userData']['costPrice'] as int?,
              markup: data['userData']['markup'] as double,
              priceVariants: data['userData']['priceVariants'] != null
                ? Map<String, int>.from(data['userData']['priceVariants'] as Map)
                : null,
            ),
            gamification: Gamification(
              ponPoints: data['gamification']['ponPoints'] as int,
            ),
            metadata: Metadata(
              createdAt: DateTime.parse(data['metadata']['createdAt'] as String),
              aiConfidence: data['metadata']['aiConfidence'] as int?,
            ),
            itemType: data['itemType'] == 'set' ? ItemType.set : ItemType.sake,
          );

          // IDを保持するためにput()を使用（add()は新しいキーを生成してしまう）
          await sakeBox.put(item.id, item);
        }
        debugPrint('✅ SakeItemsを復元しました（${sakeItemsJson.length}件）');
        // UI更新のためにわずかに待機
        await Future.delayed(const Duration(milliseconds: 500));
      }

      // 3. settings.jsonを検索 (ルートまたはサブディレクトリ)
      File? settingsFile;
      try {
        settingsFile = potentialFiles.firstWhere((f) => path.basename(f.path) == 'settings.json');
      } catch (e) {
         debugPrint('⚠️ settings.json が見つかりません (スキップ)');
      }

      if (settingsFile != null && await settingsFile.exists()) {
        final settingsJson = json.decode(await settingsFile.readAsString()) as Map<String, dynamic>;
        final settingsBox = Hive.box('settings');

        await settingsBox.clear();
        for (var entry in settingsJson.entries) {
          await settingsBox.put(entry.key, entry.value);
        }
        debugPrint('✅ 設定を復元しました');
      }

      // 4. 画像ファイルを復元 (sake_items.jsonと同じ階層のimagesフォルダを探す)
      if (sakeItemsFile != null) {
         final parentDir = sakeItemsFile.parent;
         final imagesDir = Directory(path.join(parentDir.path, 'images'));
         
         if (await imagesDir.exists()) {
            final appDir = await getApplicationDocumentsDirectory();
            final imageFiles = imagesDir.listSync();

            for (var imageFile in imageFiles) {
              if (imageFile is File) {
                final fileName = path.basename(imageFile.path);
                await imageFile.copy(path.join(appDir.path, fileName));
              }
            }
            debugPrint('✅ 画像ファイルを復元しました（${imageFiles.length}ファイル）');
         }
      }

      // 5. 一時ディレクトリを削除
      await extractDir.delete(recursive: true);

      debugPrint('✅ データの復元が完了しました');
      return true;
    } catch (error) {
      debugPrint('❌ 復元処理エラー: $error');
      // スタックトレースも出す
      debugPrint(error.toString()); 
      if (error is Error) {
          debugPrint(error.stackTrace.toString());
      }
      return false;
    }
  }

  /// Google Driveにバックアップファイルが存在するか確認
  Future<bool> hasBackupOnDrive() async {
    try {
      final account = _googleSignIn.currentUser;
      if (account == null) return false;

      final authClient = await _googleSignIn.authenticatedClient();
      if (authClient == null) return false;

      final driveApi = drive.DriveApi(authClient);

      final fileList = await driveApi.files.list(
        q: "name = '$backupFileName' and trashed = false",
        spaces: 'drive',
      );

      return fileList.files != null && fileList.files!.isNotEmpty;
    } catch (error) {
      debugPrint('❌ バックアップ確認エラー: $error');
      return false;
    }
  }
}
