import 'dart:async';
import 'dart:io';

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:matrix/matrix.dart' as mx;
import 'package:pyramid/core/fcm_push_service.dart';
import 'package:pyramid/core/matrix_client.dart';
import 'package:shared_preferences/shared_preferences.dart';

const _prefHomeserver = 'homeserver_url';

final homeserverProvider = StateProvider<String?>((ref) => null);

Future<void> initHomeserver(WidgetRef ref) async {
  final prefs = await SharedPreferences.getInstance();
  final saved = prefs.getString(_prefHomeserver);
  ref.read(homeserverProvider.notifier).state = saved;
}

Future<void> saveHomeserver(WidgetRef ref, String url) async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setString(_prefHomeserver, url);
  ref.read(homeserverProvider.notifier).state = url;
}

enum LoginStatus { idle, loading, error }

class LoginFormState {
  final LoginStatus status;
  final String? errorMessage;

  const LoginFormState({this.status = LoginStatus.idle, this.errorMessage});

  LoginFormState copyWith({LoginStatus? status, String? errorMessage}) =>
      LoginFormState(
        status: status ?? this.status,
        errorMessage: errorMessage,
      );
}

class LoginNotifier extends StateNotifier<LoginFormState> {
  final Ref _ref;
  LoginNotifier(this._ref) : super(const LoginFormState());

  Future<void> login({
    required String username,
    required String password,
    required String homeserver,
    String? deviceName,
  }) async {
    state = state.copyWith(status: LoginStatus.loading, errorMessage: null);

    final client = _ref.read(matrixClientProvider).valueOrNull;
    if (client == null) {
      state = state.copyWith(
        status: LoginStatus.error,
        errorMessage: 'Client nicht initialisiert',
      );
      return;
    }

    if (client.isLogged()) {
      state = state.copyWith(status: LoginStatus.idle);
      return;
    }

    try {
      final serverUri = Uri.https(
        homeserver.replaceAll(RegExp(r'^https?://'), ''),
        '',
      );
      await client.checkHomeserver(serverUri);

      await client.login(
        mx.LoginType.mLoginPassword,
        identifier: mx.AuthenticationUserIdentifier(user: username),
        password: password,
        initialDeviceDisplayName: deviceName ?? 'Pyramid',
        // Ohne Refresh-Token eskaliert das SDK jeden Soft-Logout (z. B. nach
        // Ablauf des Access-Tokens) sofort zu einem echten, destruktiven
        // Logout, weil `refreshAccessToken()` sonst mangels Refresh-Token
        // fehlschlägt (siehe matrix-Paket Client._handleSoftLogout). Das ist
        // der wahrscheinlichste Auslöser des Uta-Random-Logout-Bugs.
        refreshToken: true,
      );

      if (Platform.isAndroid) unawaited(initFcm(client));

      state = state.copyWith(status: LoginStatus.idle);
    } on mx.MatrixException catch (e) {
      state = state.copyWith(
        status: LoginStatus.error,
        errorMessage: e.errorMessage,
      );
    } catch (e) {
      state = state.copyWith(
        status: LoginStatus.error,
        errorMessage: e.toString(),
      );
    }
  }
}

final loginNotifierProvider =
    StateNotifierProvider<LoginNotifier, LoginFormState>((ref) {
  return LoginNotifier(ref);
});

final isLoggedInProvider = StreamProvider<bool>((ref) async* {
  final client = await ref.watch(matrixClientProvider.future);
  yield client.isLogged();
  await for (final loginState in client.onLoginStateChanged.stream) {
    yield loginState == mx.LoginState.loggedIn;
  }
});
