import 'dart:convert' show jsonDecode;

import 'package:http/http.dart' as http;
import 'package:http/http.dart' show Response;

import 'package:fotodocumentation/main.dart' show logger;
import 'package:fotodocumentation/utils/di_container.dart';
import 'package:fotodocumentation/utils/http_client_utils.dart';
import 'package:fotodocumentation/utils/jwt_token_storage.dart';
import 'package:fotodocumentation/utils/url_utils.dart';

abstract class BaseController {
  UrlUtils get uriUtils => DiContainer.get();
  JwtTokenStorage get _jwtTokenStorage => DiContainer.get();
  HttpClientUtils get httpClientUtils => DiContainer.get();

  Future<Header> getAuthHeader() async {
    final accessToken = await _jwtTokenStorage.getAccessToken();

    if (accessToken != null && accessToken.isNotEmpty) {
      // Use JWT Bearer token
      return Header('Authorization', 'Bearer $accessToken');
    } else {
      return const Header("Accept-Language", "en-US");
    }
  }

  Exception getServerError(Response response) {
    return Exception("Error receiving data from server");
  }

  Future<List<T>> runGetListWithAuth<T>(String uriStr, List<T> Function(dynamic) convert) async {
    http.Client client = httpClientUtils.client;
    try {
      Header cred = await getAuthHeader();
      Uri uri = Uri.parse(uriStr);
      var response = await client.get(uri, headers: {cred.name: cred.value});
      if (response.statusCode == 200) {
        String text = response.body;
        var jsonArray = jsonDecode(text);
        return convert(jsonArray);
      } else {
        throw ServerError(response.statusCode); // Exception("Failed to get server data ${response.statusCode}");
      }
    } catch (e) {
      logger.e("exception $e");
      rethrow;
    }
  }

  Future<T?> runGetWithAuth<T>(String uriStr, T Function(dynamic) convert) async {
    http.Client client = httpClientUtils.client;
    try {
      Header cred = await getAuthHeader();
      Uri uri = Uri.parse(uriStr);
      var response = await client.get(uri, headers: {cred.name: cred.value});
      if (response.statusCode == 200) {
        String text = response.body;
        var jsonArray = jsonDecode(text);
        return convert(jsonArray);
      } else {
        throw ServerError(response.statusCode); // Exception("Failed to get server data ${response.statusCode}");
      }
    } catch (e) {
      logger.e("exception $e");
      rethrow;
    }
  }

  Future<bool> runDeleteWithAuth(String uriStr) async {
    http.Client client = httpClientUtils.client;
    Header cred = await getAuthHeader();
    Uri uri = Uri.parse(uriStr);
    var response = await client.delete(uri, headers: {cred.name: cred.value});
    return response.statusCode == 200;
  }
}

class Header {
  final String name;
  final String value;

  const Header(this.name, this.value);

  @override
  String toString() => '$runtimeType: $name, $value';
}

class ServerError {
  int statusCode;

  ServerError(this.statusCode);
}
