import 'dart:convert';
import 'dart:io';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/models/accuracy.dart';
import 'package:diameter/models/basal.dart';
import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/models/bolus.dart';
import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/models/glucose_target.dart';
import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_event.dart';
import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/models/log_meal.dart';
import 'package:diameter/models/meal.dart';
import 'package:diameter/models/meal_category.dart';
import 'package:diameter/models/meal_portion_type.dart';
import 'package:diameter/models/meal_source.dart';
import 'package:diameter/models/settings.dart';
import 'package:flutter_translate/flutter_translate.dart';
// import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:intl/intl.dart';
import 'package:path_provider/path_provider.dart';
import 'package:http/http.dart' as http;
import 'package:googleapis/drive/v3.dart' as drive;

class GoogleAuthClient extends http.BaseClient {
  final Map<String, String> _headers;

  final http.Client _client = http.Client();

  GoogleAuthClient(this._headers);

  @override
  Future<http.StreamedResponse> send(http.BaseRequest request) {
    return _client.send(request..headers.addAll(_headers));
  }
}

class DataExport {
  static Map<String, dynamic> appDataToJson(DateTime timestamp) {
    final Map<String, dynamic> data = <String, dynamic>{};

    data['exportDate'] = timestamp.toString();
    data['accuracies'] =
        Accuracy.box.getAll().map((accuracy) => accuracy.toJson()).toList();
    data['basalProfiles'] = BasalProfile.box
        .getAll()
        .map((basalProfile) => basalProfile.toJson())
        .toList();
    data['basalRates'] =
        Basal.box.getAll().map((basal) => basal.toJson()).toList();
    data['bolusProfiles'] = BolusProfile.box
        .getAll()
        .map((bolusProfile) => bolusProfile.toJson())
        .toList();
    data['bolusRates'] =
        Bolus.box.getAll().map((bolusRates) => bolusRates.toJson()).toList();
    data['glucoseTargets'] = GlucoseTarget.box
        .getAll()
        .map((glucoseTarget) => glucoseTarget.toJson())
        .toList();
    data['logBoli'] =
        LogBolus.box.getAll().map((logBolus) => logBolus.toJson()).toList();
    data['logEntries'] =
        LogEntry.box.getAll().map((logEntry) => logEntry.toJson()).toList();
    data['logEventTypes'] = LogEventType.box
        .getAll()
        .map((logEventType) => logEventType.toJson())
        .toList();
    data['logEvents'] =
        LogEvent.box.getAll().map((logEvent) => logEvent.toJson()).toList();
    data['logMeals'] =
        LogMeal.box.getAll().map((logMeal) => logMeal.toJson()).toList();
    data['mealCategories'] = MealCategory.box
        .getAll()
        .map((mealCategory) => mealCategory.toJson())
        .toList();
    data['mealPortionTypes'] = MealPortionType.box
        .getAll()
        .map((mealPortionType) => mealPortionType.toJson())
        .toList();
    data['mealSources'] = MealSource.box
        .getAll()
        .map((mealSource) => mealSource.toJson())
        .toList();
    data['meals'] = Meal.box.getAll().map((meal) => meal.toJson()).toList();
    data['settings'] = Settings.toJson();

    return data;
  }

  List<String> appDataFromJson(File file,
      {String source = '', bool overrideExisting = true, bool skipDeleted = false}) {
    final Map<String, dynamic> data = json.decode(file.openRead().toString());
    final List<String> errors = [];

    final exportDate = DateTime.tryParse(data['exportDate']);
    if (exportDate != null && Settings.lastExportTimeStamp != null && exportDate.isAfter(Settings.lastExportTimeStamp!)) {
      if (data.keys.contains('accuracies')) {
        for (var entry in data['accuracies']) {
          if (!skipDeleted || entry['deleted'] == false) {
            final error = Accuracy.putFromJson(entry, overrideExisting, source);
            if (error != null) {
              errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
            }
          }
        }
      }

      if (data.keys.contains('basalProfiles')) {
        for (var entry in data['basalProfiles']) {
          if (!skipDeleted || entry['deleted'] == false) {
            final error = BasalProfile.putFromJson(entry, overrideExisting, source);
            if (error != null) {
              errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
            }
          }
        }
      }

      if (data.keys.contains('basalRates')) {
        for (var entry in data['basalRates']) {
          if (!skipDeleted || entry['deleted'] == false) {
            final error = Basal.putFromJson(entry, overrideExisting, source);
            if (error != null) {
              errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
            }
          }
        }
      }
      
      if (data.keys.contains('bolusRates')) {
        for (var entry in data['bolusRates']) {
          if (!skipDeleted || entry['deleted'] == false) {
            final error = Bolus.putFromJson(entry, overrideExisting, source);
            if (error != null) {
              errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
            }
          }
        }
      }

      if (data.keys.contains('glucoseTargets')) {
        for (var entry in data['glucoseTargets']) {
          if (!skipDeleted || entry['deleted'] == false) {
            final error = GlucoseTarget.putFromJson(entry, overrideExisting, source);
            if (error != null) {
              errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
            }
          }
        }
      }
      
      if (data.keys.contains('logBoli')) {
        for (var entry in data['logBoli']) {
          if (!skipDeleted || entry['deleted'] == false) {
            final error = LogBolus.putFromJson(entry, overrideExisting, source);
            if (error != null) {
              errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
            }
          }
        }
      }
      
      if (data.keys.contains('logEntries')) {
        for (var entry in data['logEntries']) {
          if (!skipDeleted || entry['deleted'] == false) {
            final error = LogEntry.putFromJson(entry, overrideExisting, source);
            if (error != null) {
              errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
            }
          }
        }
      }
      
      if (data.keys.contains('logEvents')) {
        for (var entry in data['logEvents']) {
          if (!skipDeleted || entry['deleted'] == false) {
            final error = LogEvent.putFromJson(entry, overrideExisting, source);
            if (error != null) {
              errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
            }
          }
        }
      }
      
      if (data.keys.contains('logMeals')) {
        for (var entry in data['logMeals']) {
          if (!skipDeleted || entry['deleted'] == false) {
            final error = LogMeal.putFromJson(entry, overrideExisting, source);
            if (error != null) {
              errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
            }
          }
        }
      }
      
      if (data.keys.contains('mealCategories')) {
        for (var entry in data['mealCategories']) {
          if (!skipDeleted || entry['deleted'] == false) {
            final error = MealCategory.putFromJson(entry, overrideExisting, source);
            if (error != null) {
              errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
            }
          }
        }
      }
      
      if (data.keys.contains('mealPortionTypes')) {
        for (var entry in data['mealPortionTypes']) {
          if (!skipDeleted || entry['deleted'] == false) {
            final error = MealPortionType.putFromJson(entry, overrideExisting, source);
            if (error != null) {
              errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
            }
          }
        }
      }
      
      if (data.keys.contains('mealSources')) {
        for (var entry in data['mealSources']) {
          if (!skipDeleted || entry['deleted'] == false) {
            final error = MealSource.putFromJson(entry, overrideExisting, source);
            if (error != null) {
              errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
            }
          }
        }
      }
      
      if (data.keys.contains('meals')) {
        for (var entry in data['meals']) {
          if (!skipDeleted || entry['deleted'] == false) {
            final error = Meal.putFromJson(entry, overrideExisting, source);
            if (error != null) {
              errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
            }
          }
        }
      }
    } else {
      // show confirmation dialog because export is newer than last saved timestamp
    }

    return errors;
  }

  static Future<File> exportDataToFile(DateTime timestamp) async {
    final appDocDir = await getApplicationDocumentsDirectory();
    final appDocPath = appDocDir.path;

    final DateFormat formatter = DateFormat(DateFormat.YEAR_MONTH_DAY);
    final date = DateTime.now();
    final file = File('$appDocPath/diameter_${formatter.format(date)}.json');

    file.writeAsStringSync(json.encode(appDataToJson(timestamp)));
    return file;
  }

  static Future<void> exportToGoogleDrive() async {
    final login = GoogleSignIn.standard(scopes: [drive.DriveApi.driveScope]);
    final GoogleSignInAccount? account = await login.signIn();

    if (account != null) {
      final authenticateClient = GoogleAuthClient(await account.authHeaders);
      final driveApi = drive.DriveApi(authenticateClient);

      final timestamp = DateTime.now();
      var localFile = await DataExport.exportDataToFile(timestamp);
      var media = drive.Media(localFile.openRead(), localFile.lengthSync());

      final settings = Settings.get();
      settings.lastExportTimestamp = timestamp;
      Settings.put(settings);

      drive.File driveFile = drive.File()..name = 'diameter.json';
      await driveApi.files.create(driveFile, uploadMedia: media);
    }
  }

  static Future<void> importFromGoogleDrive() async {
    final login = GoogleSignIn.standard(scopes: [drive.DriveApi.driveScope]);
    final GoogleSignInAccount? account = await login.signIn();

    if (account != null) {
      final authenticateClient = GoogleAuthClient(await account.authHeaders);
      final driveApi = drive.DriveApi(authenticateClient);

      final timestamp = DateTime.now();
      var localFile = await DataExport.exportDataToFile(timestamp);
      var media = drive.Media(localFile.openRead(), localFile.lengthSync());

      final settings = Settings.get();
      settings.lastExportTimestamp = timestamp;
      Settings.put(settings);

      drive.File driveFile = drive.File()..name = 'diameter.json';
      await driveApi.files.create(driveFile, uploadMedia: media);
    }
  }
}

// class DataExportDialog extends StatefulWidget {
//   static const String routeName = '/data-export';
//   const DataExportDialog({Key? key}) : super(key: key);

//   @override
//   _DataExportDialogState createState() => _DataExportDialogState();
// }

// class _DataExportDialogState extends State<DataExportDialog> {
//   final ScrollController _scrollController = ScrollController();

//   bool _isSaving = false;

//   @override
//   void dispose() {
//     _scrollController.dispose();
//     super.dispose();
//   }

//   @override
//   void initState() {
//     super.initState();
//   }

//   void onExport(BuildContext context) async {
//     setState(() {
//       _isSaving = true;
//     });

//     Navigator.pop(context);
//     setState(() {
//       _isSaving = false;
//     });
//   }

//   @override
//   Widget build(BuildContext context) {
//     return AlertDialog(
//         content: Column(
//           mainAxisSize: MainAxisSize.min,
//           crossAxisAlignment: CrossAxisAlignment.center,
//           children: const [
//             // CheckboxListTile(
//             //   value: showChart,
//             //   onChanged: (_) => setState(() => showChart = !showChart),
//             //   title: const Text('show Chart'),
//             //   controlAffinity: ListTileControlAffinity.leading,
//             // ),
//           ],
//         ),
//         actions: <Widget>[
//           TextButton(
//             onPressed: () => Navigator.pop(context),
//             child: const Text('CANCEL'),
//           ),
//           ElevatedButton(
//             onPressed: !_isSaving ? () => onExport(context) : null,
//             child: const Text('EXPORT'),
//           ),
//         ]);
//   }
// }
