import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:jwt_decoder/jwt_decoder.dart';
import 'package:logging/logging.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:shared_models/jwt.dart';
import 'package:shared_preferences/shared_preferences.dart';

part 'auth.g.dart';

final logger = Logger('provider/auth');

@riverpod
class JwtNotifier extends _$JwtNotifier {
  @override
  Future<String?> build() async {
    if (!await SharedPreferencesAsync().containsKey('jwt')) {
      logger.fine('No JWT saved to client');
      return null;
    }
    final jwtString = await SharedPreferencesAsync().getString('jwt');
    if (jwtString == null) {
      logger.fine('Saved JWT came back null, removing key');
      SharedPreferencesAsync().remove('jwt');
      return null;
    }

    return jwtString;
  }

  Future<void> setJwt(String jwt) async {
    final payload = JwtDecoder.tryDecode(jwt);
    if (payload == null) {
      logger.info('Tried to set JWT token that did not decode to payload');
      state = AsyncValue.error('JWT set to invalid token', StackTrace.current);
      return;
    }

    logger.fine('Saving jwt token to shared prefs');
    await SharedPreferencesAsync().setString('jwt', jwt);

    state = AsyncValue.data(jwt);
  }

  Future<void> eraseJwt() async {
    SharedPreferencesAsync().remove('jwt');
    state = AsyncValue.data(null);
  }
}

@riverpod
JWTBody? jwtBody(Ref ref) {
  final jwtString = ref.watch(jwtNotifierProvider).valueOrNull;
  if (jwtString == null) {
    return null;
  }

  final payload = JwtDecoder.tryDecode(jwtString);
  if (payload == null) {
    logger.fine('Failed to decode JWT, removing key.');
    Future.delayed(const Duration(), () => ref.read(jwtNotifierProvider.notifier).eraseJwt());
    return null;
  }
  try {
    final body = JWTBody.fromJson(payload);
    return body;
  } catch (e) {
    logger.shout(
        'Failed to parse JWT payload to JWTBody, something is wrong.\nPayload: $payload', e, StackTrace.current);
    Future.delayed(const Duration(), () => ref.read(jwtNotifierProvider.notifier).eraseJwt());
    return null;
  }
}