import 'dart:convert';
import 'dart:io';

import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:spotiflac_android/app.dart';
import 'package:spotiflac_android/models/settings.dart';
import 'package:spotiflac_android/providers/settings_provider.dart';
import 'package:spotiflac_android/services/platform_bridge.dart';

void main() {
  TestWidgetsFlutterBinding.ensureInitialized();
  const backendChannel = MethodChannel('com.zarz.spotiflac/backend');

  tearDown(() {
    TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
        .setMockMethodCallHandler(backendChannel, null);
  });

  group('restored installation detection', () {
    test('fresh package plus persisted settings requires a reset', () {
      const installState = InstallationState(
        markerExisted: false,
        markerCreated: true,
        freshPackageInstall: true,
      );

      expect(
        shouldResetRestoredInstallation(
          hasPersistedSettings: true,
          installState: installState,
        ),
        isTrue,
      );
      expect(
        shouldResetRestoredInstallation(
          hasPersistedSettings: false,
          installState: installState,
        ),
        isFalse,
      );
    });

    test('normal upgrade without the new marker preserves settings', () {
      const installState = InstallationState(
        markerExisted: false,
        markerCreated: true,
        freshPackageInstall: false,
      );

      expect(
        shouldResetRestoredInstallation(
          hasPersistedSettings: true,
          installState: installState,
        ),
        isFalse,
      );
    });

    test('marker creation failure never triggers a repeated reset', () {
      const installState = InstallationState(
        markerExisted: false,
        markerCreated: false,
        freshPackageInstall: true,
      );

      expect(
        shouldResetRestoredInstallation(
          hasPersistedSettings: true,
          installState: installState,
        ),
        isFalse,
      );
    });

    test('subsequent launch with a marker preserves settings', () {
      const installState = InstallationState(
        markerExisted: true,
        markerCreated: true,
        freshPackageInstall: false,
      );

      expect(
        shouldResetRestoredInstallation(
          hasPersistedSettings: true,
          installState: installState,
        ),
        isFalse,
      );
    });
  });

  group('restored settings sanitization', () {
    const restored = AppSettings(
      defaultService: 'extension.lossless',
      audioQuality: 'HI_RES_LOSSLESS',
      locale: 'id',
      downloadDirectory: '/storage/emulated/0/Music/Old',
      downloadDirectoryBookmark: 'old-bookmark',
      storageMode: 'saf',
      downloadTreeUri: 'content://tree/old',
      isFirstLaunch: false,
      useAllFilesAccess: true,
      localLibraryEnabled: true,
      localLibraryPath: 'content://tree/library',
      localLibraryBookmark: 'library-bookmark',
      localLibraryAutoScan: 'daily',
      hasCompletedTutorial: true,
    );

    test('resets installation-bound fields and keeps portable preferences', () {
      final sanitized = resetInstallationBoundSettings(restored);

      expect(sanitized.isFirstLaunch, isTrue);
      expect(sanitized.hasCompletedTutorial, isFalse);
      expect(sanitized.storageMode, 'app');
      expect(sanitized.downloadTreeUri, isEmpty);
      expect(sanitized.downloadDirectory, isEmpty);
      expect(sanitized.downloadDirectoryBookmark, isEmpty);
      expect(sanitized.useAllFilesAccess, isFalse);
      expect(sanitized.localLibraryEnabled, isFalse);
      expect(sanitized.localLibraryPath, isEmpty);
      expect(sanitized.localLibraryBookmark, isEmpty);
      expect(sanitized.localLibraryAutoScan, 'off');
      expect(sanitized.defaultService, restored.defaultService);
      expect(sanitized.audioQuality, restored.audioQuality);
      expect(sanitized.locale, restored.locale);
    });

    test('persists the sanitized state before bootstrap routing', () async {
      SharedPreferences.setMockInitialValues({
        'app_settings': jsonEncode(restored.toJson()),
      });
      final prefs = await SharedPreferences.getInstance();

      expect(hasPersistedAppSettings(prefs), isTrue);
      await resetRestoredInstallationSettings(prefs);
      final bootstrap = loadBootstrapSettings(prefs);

      expect(bootstrap.isFirstLaunch, isTrue);
      expect(bootstrap.storageMode, 'app');
      expect(bootstrap.downloadTreeUri, isEmpty);
      expect(bootstrap.defaultService, restored.defaultService);
    });
  });

  group('bootstrap routing', () {
    test('restored-install reset routes through setup and tutorial', () {
      final sanitized = resetInstallationBoundSettings(
        const AppSettings(
          isFirstLaunch: false,
          hasCompletedTutorial: true,
          storageMode: 'saf',
          downloadTreeUri: 'content://tree/old',
        ),
      );

      expect(
        initialLocationForAppState(
          isFirstLaunch: sanitized.isFirstLaunch,
          hasCompletedTutorial: sanitized.hasCompletedTutorial,
        ),
        '/setup',
      );
      expect(
        initialLocationForAppState(
          isFirstLaunch: false,
          hasCompletedTutorial: false,
        ),
        '/tutorial',
      );
      expect(
        initialLocationForAppState(
          isFirstLaunch: false,
          hasCompletedTutorial: true,
        ),
        '/',
      );
    });
  });

  group('strict SAF validation', () {
    test('returns the native persisted-grant result', () async {
      TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
          .setMockMethodCallHandler(backendChannel, (call) async {
            expect(call.method, 'isSafTreeAccessible');
            expect(
              (call.arguments as Map)['tree_uri'],
              'content://tree/missing',
            );
            return false;
          });

      expect(
        await PlatformBridge.validateSafTreeAccess('content://tree/missing'),
        isFalse,
      );
    });

    test('surfaces bridge errors instead of allowing an unverified write', () {
      TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
          .setMockMethodCallHandler(backendChannel, (call) async {
            throw PlatformException(code: 'bridge_failure');
          });

      expect(
        PlatformBridge.validateSafTreeAccess('content://tree/missing'),
        throwsA(isA<PlatformException>()),
      );
    });
  });

  test('Android backup policy excludes every app-data domain', () {
    final manifest = File(
      'android/app/src/main/AndroidManifest.xml',
    ).readAsStringSync();
    final currentRules = File(
      'android/app/src/main/res/xml/backup_rules.xml',
    ).readAsStringSync();
    final legacyRules = File(
      'android/app/src/main/res/xml/backup_rules_legacy.xml',
    ).readAsStringSync();

    expect(manifest, contains('android:allowBackup="false"'));
    expect(
      manifest,
      contains('android:dataExtractionRules="@xml/backup_rules"'),
    );
    expect(
      manifest,
      contains('android:fullBackupContent="@xml/backup_rules_legacy"'),
    );

    for (final domain in [
      'root',
      'file',
      'database',
      'sharedpref',
      'external',
      'device_root',
      'device_file',
      'device_database',
      'device_sharedpref',
    ]) {
      expect(currentRules, contains('domain="$domain" path="."'));
      expect(legacyRules, contains('domain="$domain" path="."'));
    }
    expect(currentRules, contains('<cloud-backup>'));
    expect(currentRules, contains('<device-transfer>'));
  });
}
