import 'dart:convert';
import 'dart:developer';

import 'package:dio/dio.dart';

abstract class DataLoader<TResult> {
  final Dio dio;
  DataLoader(this.dio) {
    dio.options.connectTimeout = const Duration(seconds: 10).inMilliseconds;
    dio.options.sendTimeout = const Duration(seconds: 30).inMilliseconds;
    dio.options.receiveTimeout = const Duration(seconds: 30).inMilliseconds;
  }

  Future<TResult> run() async {
    var fetcher = fetch();
    await Future.wait([
      fetcher,
      Future.delayed(const Duration(milliseconds: 500)) // TODO tune or remove
    ]);

    var response = await fetcher;
    try {
      return assemble(DataLoaderResult(
        json: jsonDecode(response.data!),
        headers: response.headers.map.map((key, value) => MapEntry(key, value.join(';'))),
      ));
    } catch(trace, e) {
      log(trace.toString());
      throw(e);
    }
  }

  Future<Response<String>> fetch();
  TResult assemble(DataLoaderResult data);
}

class DataLoaderResult {
  final dynamic json;
  final Map<String, String> headers;

  Map<String, dynamic> asMap() => json as Map<String, dynamic>;
  List<dynamic> asList() => json as List<dynamic>;
  List<Map<String, dynamic>> asListOfMaps() => asList().map((e) => e as Map<String, dynamic>).toList();

  DataLoaderResult({required this.json, required this.headers});
}