import 'package:dio/dio.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:frontend/providers/auth.dart';
import 'package:logging/logging.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:shared_models/jwt.dart';

part 'dio.g.dart';

final logger = Logger('Dio');

@riverpod
Dio dio(Ref ref) {
  final dio = Dio(BaseOptions(
    baseUrl: 'http://localhost:8080',
    connectTimeout: const Duration(seconds: 5),
    receiveTimeout: const Duration(seconds: 3),
    validateStatus: (status) => true,
  ));

  final jwt = ref.watch(jwtNotifierProvider).valueOrNull;
  final jwtBody = ref.read(jwtBodyProvider);

  dio.interceptors.addAll([
    JwtInterceptor(
        jwt: jwt, jwtBody: jwtBody, invalidateJwtCallback: () => ref.read(jwtNotifierProvider.notifier).eraseJwt()),
    CustomLogInterceptor()
  ]);

  logger.fine('Created new Dio object');

  return dio;
}

// Adds the jwt to
class JwtInterceptor extends Interceptor {
  final String? jwt;
  final JWTBody? jwtBody;
  final Function invalidateJwtCallback;

  JwtInterceptor({required this.jwt, required this.jwtBody, required this.invalidateJwtCallback});

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    if (jwt == null || jwtBody == null) {
      handler.next(options);
      return;
    }
    if (jwtBody != null && jwtBody!.exp < DateTime.now().millisecondsSinceEpoch ~/ 1000) {
      invalidateJwtCallback();
      handler.next(options);
      return;
    }
    if (jwt != null) {
      options.headers['Authorization'] = 'Bearer $jwt';
      handler.next(options);
    }
  }

  // on unauthorized request, remove jwt
  @override
  // ignore: strict_raw_type
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    if (response.statusCode == 401) {
      invalidateJwtCallback();
    }
    handler.next(response);
  }
}

// Create a custom LogInterceptor using the logger object
class CustomLogInterceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    logger.info('REQUEST[${options.method}] => PATH: ${options.path}');
    return super.onRequest(options, handler);
  }

  @override
  // ignore: strict_raw_type
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    logger.info(
      'RESPONSE[${response.statusCode}] => PATH: ${response.requestOptions.path}',
    );
    return super.onResponse(response, handler);
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) {
    logger.severe(
      'ERROR[${err.response?.statusCode}] => PATH: ${err.requestOptions.path}',
    );
    return super.onError(err, handler);
  }
}