import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:lightmeter/data/models/camera_feature.dart';
import 'package:lightmeter/data/models/ev_source_type.dart';
import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
import 'package:lightmeter/data/models/supported_locale.dart';
import 'package:lightmeter/data/models/theme_type.dart';
import 'package:lightmeter/data/models/volume_action.dart';
import 'package:lightmeter/data/shared_prefs_service.dart';
import 'package:lightmeter/res/theme.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import 'package:mocktail/mocktail.dart';
import 'package:shared_preferences/shared_preferences.dart';

class _MockSharedPreferences extends Mock implements SharedPreferences {}

void main() {
  late _MockSharedPreferences sharedPreferences;
  late UserPreferencesService service;

  setUpAll(() {
    sharedPreferences = _MockSharedPreferences();
    service = UserPreferencesService(sharedPreferences);
  });

  tearDown(() {
    reset(sharedPreferences);
  });

  group('migrateOldKeys()', () {
    test('no legacy keys', () async {
      when(() => sharedPreferences.getInt("curIsoIndex")).thenReturn(null);
      when(() => sharedPreferences.getInt("curndIndex")).thenReturn(null);
      when(() => sharedPreferences.getDouble("cameraCalibr")).thenReturn(null);
      when(() => sharedPreferences.getDouble("sensorCalibr")).thenReturn(null);
      when(() => sharedPreferences.getBool("vibrate")).thenReturn(null);

      when(() => sharedPreferences.remove("curIsoIndex")).thenAnswer((_) async => true);
      when(() => sharedPreferences.remove("curndIndex")).thenAnswer((_) async => true);
      when(() => sharedPreferences.remove("cameraCalibr")).thenAnswer((_) async => true);
      when(() => sharedPreferences.remove("sensorCalibr")).thenAnswer((_) async => true);
      when(() => sharedPreferences.remove("vibrate")).thenAnswer((_) async => true);

      await service.migrateOldKeys();

      verifyNever(() => sharedPreferences.remove("curIsoIndex"));
      verifyNever(() => sharedPreferences.remove("curndIndex"));
      verifyNever(() => sharedPreferences.remove("cameraCalibr"));
      verifyNever(() => sharedPreferences.remove("sensorCalibr"));
      verifyNever(() => sharedPreferences.remove("vibrate"));
    });

    test('migrate all keys', () async {
      when(() => sharedPreferences.getInt("curIsoIndex")).thenReturn(1);
      when(() => sharedPreferences.getInt("curndIndex")).thenReturn(0);
      when(() => sharedPreferences.getDouble("cameraCalibr")).thenReturn(1.0);
      when(() => sharedPreferences.getDouble("sensorCalibr")).thenReturn(-1.0);
      when(() => sharedPreferences.getBool("vibrate")).thenReturn(false);

      when(
        () => sharedPreferences.setInt(UserPreferencesService.isoKey, IsoValue.values[1].value),
      ).thenAnswer((_) => Future.value(true));
      when(
        () => sharedPreferences.setInt(UserPreferencesService.ndFilterKey, NdValue.values[0].value),
      ).thenAnswer((_) => Future.value(true));
      when(
        () => sharedPreferences.setDouble(UserPreferencesService.cameraEvCalibrationKey, 1.0),
      ).thenAnswer((_) => Future.value(true));
      when(
        () => sharedPreferences.setDouble(UserPreferencesService.lightSensorEvCalibrationKey, -1.0),
      ).thenAnswer((_) => Future.value(true));
      when(
        () => sharedPreferences.setBool(UserPreferencesService.hapticsKey, false),
      ).thenAnswer((_) => Future.value(true));

      when(() => sharedPreferences.remove("curIsoIndex")).thenAnswer((_) async => true);
      when(() => sharedPreferences.remove("curndIndex")).thenAnswer((_) async => true);
      when(() => sharedPreferences.remove("cameraCalibr")).thenAnswer((_) async => true);
      when(() => sharedPreferences.remove("sensorCalibr")).thenAnswer((_) async => true);
      when(() => sharedPreferences.remove("vibrate")).thenAnswer((_) async => true);

      await service.migrateOldKeys();

      verify(() => sharedPreferences.remove("curIsoIndex")).called(1);
      verify(() => sharedPreferences.remove("curndIndex")).called(1);
      verify(() => sharedPreferences.remove("cameraCalibr")).called(1);
      verify(() => sharedPreferences.remove("sensorCalibr")).called(1);
      verify(() => sharedPreferences.remove("vibrate")).called(1);
    });
  });

  group('iso', () {
    test('get default', () {
      when(() => sharedPreferences.getInt(UserPreferencesService.isoKey)).thenReturn(null);
      expect(service.iso, const IsoValue(100, StopType.full));
    });

    test('get', () {
      when(() => sharedPreferences.getInt(UserPreferencesService.isoKey)).thenReturn(100);
      expect(service.iso, const IsoValue(100, StopType.full));
    });

    test('set', () {
      when(() => sharedPreferences.setInt(UserPreferencesService.isoKey, 200)).thenAnswer((_) => Future.value(true));
      service.iso = const IsoValue(200, StopType.full);
      verify(() => sharedPreferences.setInt(UserPreferencesService.isoKey, 200)).called(1);
    });
  });

  group('ndFilter', () {
    test('get default', () {
      when(() => sharedPreferences.getInt(UserPreferencesService.ndFilterKey)).thenReturn(null);
      expect(service.ndFilter, const NdValue(0));
    });

    test('get', () {
      when(() => sharedPreferences.getInt(UserPreferencesService.ndFilterKey)).thenReturn(4);
      expect(service.ndFilter, const NdValue(4));
    });

    test('set', () {
      when(() => sharedPreferences.setInt(UserPreferencesService.ndFilterKey, 0)).thenAnswer((_) => Future.value(true));
      service.ndFilter = const NdValue(0);
      verify(() => sharedPreferences.setInt(UserPreferencesService.ndFilterKey, 0)).called(1);
    });
  });

  group('evSourceType', () {
    test('get default', () {
      when(() => sharedPreferences.getInt(UserPreferencesService.evSourceTypeKey)).thenReturn(null);
      expect(service.evSourceType, EvSourceType.camera);
    });

    test('get', () {
      when(() => sharedPreferences.getInt(UserPreferencesService.evSourceTypeKey)).thenReturn(1);
      expect(service.evSourceType, EvSourceType.sensor);
    });

    test('set', () {
      when(() => sharedPreferences.setInt(UserPreferencesService.evSourceTypeKey, 1))
          .thenAnswer((_) => Future.value(true));
      service.evSourceType = EvSourceType.sensor;
      verify(() => sharedPreferences.setInt(UserPreferencesService.evSourceTypeKey, 1)).called(1);
    });
  });

  group('caffeine', () {
    test('get default', () {
      when(() => sharedPreferences.getBool(UserPreferencesService.caffeineKey)).thenReturn(null);
      expect(service.caffeine, false);
    });

    test('get', () {
      when(() => sharedPreferences.getBool(UserPreferencesService.caffeineKey)).thenReturn(true);
      expect(service.caffeine, true);
    });

    test('set', () {
      when(() => sharedPreferences.setBool(UserPreferencesService.caffeineKey, false))
          .thenAnswer((_) => Future.value(true));
      service.caffeine = false;
      verify(() => sharedPreferences.setBool(UserPreferencesService.caffeineKey, false)).called(1);
    });
  });

  group('stopType', () {
    test('get default', () {
      when(() => sharedPreferences.getInt(UserPreferencesService.stopTypeKey)).thenReturn(null);
      expect(service.stopType, StopType.third);
    });

    test('get', () {
      when(() => sharedPreferences.getInt(UserPreferencesService.stopTypeKey)).thenReturn(1);
      expect(service.stopType, StopType.half);
    });

    test('set', () {
      when(() => sharedPreferences.setInt(UserPreferencesService.stopTypeKey, 0)).thenAnswer((_) => Future.value(true));
      service.stopType = StopType.full;
      verify(() => sharedPreferences.setInt(UserPreferencesService.stopTypeKey, 0)).called(1);
    });
  });

  group('showEv100', () {
    test('get default', () {
      when(() => sharedPreferences.getBool(UserPreferencesService.showEv100Key)).thenReturn(null);
      expect(service.showEv100, false);
    });

    test('get', () {
      when(() => sharedPreferences.getBool(UserPreferencesService.showEv100Key)).thenReturn(true);
      expect(service.showEv100, true);
    });

    test('set', () {
      when(() => sharedPreferences.setBool(UserPreferencesService.showEv100Key, false))
          .thenAnswer((_) => Future.value(true));
      service.showEv100 = false;
      verify(() => sharedPreferences.setBool(UserPreferencesService.showEv100Key, false)).called(1);
    });
  });

  group('meteringScreenLayout', () {
    test('get default', () {
      when(
        () => sharedPreferences.getString(UserPreferencesService.meteringScreenLayoutKey),
      ).thenReturn(null);
      expect(
        service.meteringScreenLayout,
        {
          MeteringScreenLayoutFeature.extremeExposurePairs: true,
          MeteringScreenLayoutFeature.filmPicker: true,
          MeteringScreenLayoutFeature.equipmentProfiles: true,
        },
      );
    });

    test('get (legacy)', () {
      when(
        () => sharedPreferences.getString(UserPreferencesService.meteringScreenLayoutKey),
      ).thenReturn("""{"0":false,"1":true}""");
      expect(
        service.meteringScreenLayout,
        {
          MeteringScreenLayoutFeature.extremeExposurePairs: false,
          MeteringScreenLayoutFeature.filmPicker: true,
          MeteringScreenLayoutFeature.equipmentProfiles: true,
        },
      );
    });

    test('get', () {
      when(
        () => sharedPreferences.getString(UserPreferencesService.meteringScreenLayoutKey),
      ).thenReturn("""{"extremeExposurePairs":false,"filmPicker":true}""");
      expect(
        service.meteringScreenLayout,
        {
          MeteringScreenLayoutFeature.extremeExposurePairs: false,
          MeteringScreenLayoutFeature.filmPicker: true,
          MeteringScreenLayoutFeature.equipmentProfiles: true,
        },
      );
    });

    test('set', () {
      when(
        () => sharedPreferences.setString(
          UserPreferencesService.meteringScreenLayoutKey,
          """{"extremeExposurePairs":false,"filmPicker":true,"equipmentProfiles":true}""",
        ),
      ).thenAnswer((_) => Future.value(true));
      service.meteringScreenLayout = {
        MeteringScreenLayoutFeature.extremeExposurePairs: false,
        MeteringScreenLayoutFeature.filmPicker: true,
        MeteringScreenLayoutFeature.equipmentProfiles: true,
      };
      verify(
        () => sharedPreferences.setString(
          UserPreferencesService.meteringScreenLayoutKey,
          """{"extremeExposurePairs":false,"filmPicker":true,"equipmentProfiles":true}""",
        ),
      ).called(1);
    });
  });

  group('cameraFeatures', () {
    test('get default', () {
      when(() => sharedPreferences.getString(UserPreferencesService.cameraFeaturesKey)).thenReturn(null);
      expect(
        service.cameraFeatures,
        {
          CameraFeature.spotMetering: false,
          CameraFeature.histogram: false,
        },
      );
    });

    test('get', () {
      when(() => sharedPreferences.getString(UserPreferencesService.cameraFeaturesKey))
          .thenReturn("""{"spotMetering":false,"histogram":true}""");
      expect(
        service.cameraFeatures,
        {
          CameraFeature.spotMetering: false,
          CameraFeature.histogram: true,
        },
      );
    });

    test('set', () {
      when(
        () => sharedPreferences.setString(
          UserPreferencesService.cameraFeaturesKey,
          """{"spotMetering":false,"histogram":true}""",
        ),
      ).thenAnswer((_) => Future.value(true));
      service.cameraFeatures = {
        CameraFeature.spotMetering: false,
        CameraFeature.histogram: true,
      };
      verify(
        () => sharedPreferences.setString(
          UserPreferencesService.cameraFeaturesKey,
          """{"spotMetering":false,"histogram":true}""",
        ),
      ).called(1);
    });
  });

  group('haptics', () {
    test('get default', () {
      when(() => sharedPreferences.getBool(UserPreferencesService.hapticsKey)).thenReturn(null);
      expect(service.haptics, true);
    });

    test('get', () {
      when(() => sharedPreferences.getBool(UserPreferencesService.hapticsKey)).thenReturn(true);
      expect(service.haptics, true);
    });

    test('set', () {
      when(() => sharedPreferences.setBool(UserPreferencesService.hapticsKey, false))
          .thenAnswer((_) => Future.value(true));
      service.haptics = false;
      verify(() => sharedPreferences.setBool(UserPreferencesService.hapticsKey, false)).called(1);
    });
  });

  group('autostartTimer', () {
    test('get default', () {
      when(() => sharedPreferences.getBool(UserPreferencesService.autostartTimerKey)).thenReturn(null);
      expect(service.autostartTimer, true);
    });

    test('get', () {
      when(() => sharedPreferences.getBool(UserPreferencesService.autostartTimerKey)).thenReturn(true);
      expect(service.autostartTimer, true);
    });

    test('set', () {
      when(() => sharedPreferences.setBool(UserPreferencesService.autostartTimerKey, false))
          .thenAnswer((_) => Future.value(true));
      service.autostartTimer = false;
      verify(() => sharedPreferences.setBool(UserPreferencesService.autostartTimerKey, false)).called(1);
    });
  });

  group('volumeAction', () {
    test('get default', () {
      when(() => sharedPreferences.getBool(UserPreferencesService.volumeActionKey)).thenReturn(null);
      expect(service.volumeAction, VolumeAction.shutter);
    });

    test('get', () {
      when(() => sharedPreferences.getString(UserPreferencesService.volumeActionKey))
          .thenReturn(VolumeAction.shutter.toString());
      expect(service.volumeAction, VolumeAction.shutter);
    });

    test('set', () {
      when(() => sharedPreferences.setString(UserPreferencesService.volumeActionKey, VolumeAction.shutter.toString()))
          .thenAnswer((_) => Future.value(true));
      service.volumeAction = VolumeAction.shutter;
      verify(() => sharedPreferences.setString(UserPreferencesService.volumeActionKey, VolumeAction.shutter.toString()))
          .called(1);
    });
  });

  group('locale', () {
    test('get default', () {
      when(() => sharedPreferences.getString(UserPreferencesService.localeKey)).thenReturn(null);
      expect(service.locale, SupportedLocale.en);
    });

    test('get', () {
      when(() => sharedPreferences.getString(UserPreferencesService.localeKey)).thenReturn('SupportedLocale.ru');
      expect(service.locale, SupportedLocale.ru);
    });

    test('set', () {
      when(
        () => sharedPreferences.setString(UserPreferencesService.localeKey, 'SupportedLocale.en'),
      ).thenAnswer((_) => Future.value(true));
      service.locale = SupportedLocale.en;
      verify(
        () => sharedPreferences.setString(UserPreferencesService.localeKey, 'SupportedLocale.en'),
      ).called(1);
    });
  });

  group('cameraEvCalibration', () {
    test('get default', () {
      when(() => sharedPreferences.getDouble(UserPreferencesService.cameraEvCalibrationKey)).thenReturn(null);
      expect(service.cameraEvCalibration, 0.0);
    });

    test('get', () {
      when(() => sharedPreferences.getDouble(UserPreferencesService.cameraEvCalibrationKey)).thenReturn(2.0);
      expect(service.cameraEvCalibration, 2.0);
    });

    test('set', () {
      when(
        () => sharedPreferences.setDouble(UserPreferencesService.cameraEvCalibrationKey, 1.0),
      ).thenAnswer((_) => Future.value(true));
      service.cameraEvCalibration = 1.0;
      verify(
        () => sharedPreferences.setDouble(UserPreferencesService.cameraEvCalibrationKey, 1.0),
      ).called(1);
    });
  });

  group('lightSensorEvCalibration', () {
    test('get default', () {
      when(() => sharedPreferences.getDouble(UserPreferencesService.lightSensorEvCalibrationKey)).thenReturn(null);
      expect(service.lightSensorEvCalibration, 0.0);
    });

    test('get', () {
      when(() => sharedPreferences.getDouble(UserPreferencesService.lightSensorEvCalibrationKey)).thenReturn(2.0);
      expect(service.lightSensorEvCalibration, 2.0);
    });

    test('set', () {
      when(
        () => sharedPreferences.setDouble(UserPreferencesService.lightSensorEvCalibrationKey, 1.0),
      ).thenAnswer((_) => Future.value(true));
      service.lightSensorEvCalibration = 1.0;
      verify(
        () => sharedPreferences.setDouble(UserPreferencesService.lightSensorEvCalibrationKey, 1.0),
      ).called(1);
    });
  });

  group('themeType', () {
    test('get default', () {
      when(() => sharedPreferences.getInt(UserPreferencesService.themeTypeKey)).thenReturn(null);
      expect(service.themeType, ThemeType.light);
    });

    test('get', () {
      when(() => sharedPreferences.getInt(UserPreferencesService.themeTypeKey)).thenReturn(1);
      expect(service.themeType, ThemeType.dark);
    });

    test('set', () {
      when(
        () => sharedPreferences.setInt(UserPreferencesService.themeTypeKey, 1),
      ).thenAnswer((_) => Future.value(true));
      service.themeType = ThemeType.dark;
      verify(
        () => sharedPreferences.setInt(UserPreferencesService.themeTypeKey, 1),
      ).called(1);
    });
  });

  group('primaryColor', () {
    test('get default', () {
      when(() => sharedPreferences.getInt(UserPreferencesService.primaryColorKey)).thenReturn(null);
      expect(service.primaryColor, primaryColorsList[5]);
    });

    test('get', () {
      when(() => sharedPreferences.getInt(UserPreferencesService.primaryColorKey)).thenReturn(0xff9c27b0);
      expect(service.primaryColor, primaryColorsList[2]);
    });

    test('set', () {
      when(
        () => sharedPreferences.setInt(UserPreferencesService.primaryColorKey, 0xff000000),
      ).thenAnswer((_) => Future.value(true));
      service.primaryColor = Colors.black;
      verify(
        () => sharedPreferences.setInt(UserPreferencesService.primaryColorKey, 0xff000000),
      ).called(1);
    });
  });

  group('dynamicColor', () {
    test('get default', () {
      when(() => sharedPreferences.getBool(UserPreferencesService.dynamicColorKey)).thenReturn(null);
      expect(service.dynamicColor, false);
    });

    test('get', () {
      when(() => sharedPreferences.getBool(UserPreferencesService.dynamicColorKey)).thenReturn(true);
      expect(service.dynamicColor, true);
    });

    test('set', () {
      when(() => sharedPreferences.setBool(UserPreferencesService.dynamicColorKey, false))
          .thenAnswer((_) => Future.value(true));
      service.dynamicColor = false;
      verify(() => sharedPreferences.setBool(UserPreferencesService.dynamicColorKey, false)).called(1);
    });
  });
}