import 'dart:convert';
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:graphql/client.dart';

abstract class BaseRepository {
  final Dio dio;
  final GraphQLClient graphql;
  BaseRepository({required this.dio, required this.graphql});

  // POST PAKE GRAPHQL
  Future<Map<String, dynamic>> postGraphQlMutation(
      String query, Map<String, dynamic> inpVariables) async {
    try {
      final options =
          MutationOptions(document: gql(query), variables: inpVariables);
      final QueryResult result = await graphql.mutate(options);

      if (result.hasException) {
        // throw BaseRepositoryException(message: result.exception.toString());
        if (result.exception != null) {
          if (result.exception!.graphqlErrors.isNotEmpty) {
            String error = "";
            if (result.exception!.graphqlErrors[0].message != "") {
              error += result.exception!.graphqlErrors[0].message;
              throw BaseRepositoryException(message: error);
            }
          }
        }
      }

      // return result.data!;
      return result.data!;
    } catch (e) {
      throw BaseRepositoryException(message: e.toString());
    }
  }

  Future<Map<String, dynamic>> postGraphQlQuery(
      String query, Map<String, dynamic> inpVariables) async {
    try {
      final options =
          QueryOptions(document: gql(query), variables: inpVariables);
      final QueryResult result = await graphql.query(options);

      if (result.hasException) {
        // throw BaseRepositoryException(message: result.exception.toString());
        if (result.exception != null) {
          if (result.exception!.graphqlErrors.isNotEmpty) {
            if (result.exception!.graphqlErrors[0].message != "") {
              throw BaseRepositoryException(
                message: result.exception!.graphqlErrors[0].message.toString(),
              );
            }
          }
        }
      }

      // throw BaseRepositoryException(message: result.exception.toString());

      return result.data!;
    } catch (e) {
      throw BaseRepositoryException(message: e.toString());
    }
  }

  // with handling
  Future<Map<String, dynamic>> postGraphQlQueryX(
      String query, Map<String, dynamic> inpVariables) async {
    try {
      final options =
          QueryOptions(document: gql(query), variables: inpVariables);
      final QueryResult result = await graphql.query(options);

      if (result.hasException) {
        if (result.exception != null) {
          if (result.exception!.graphqlErrors.isNotEmpty
              is BaseRepositoryException) {
            final error = result.exception!.graphqlErrors[0];
            BaseRepositoryException exception =
                result.exception!.linkException as BaseRepositoryException;
            var errorMessage = exception.message;
            print(errorMessage);
            throw BaseRepositoryException(
              message: errorMessage,
            );
          }
        }
      }

      return result.data!;
    } catch (e) {
      throw BaseRepositoryException(message: e.toString());
    }
  }

  // NATIVE POST PAKE DIO
  Future<Map<String, dynamic>> post({
    required Map<String, dynamic> param,
    required String service,
    String? token,
  }) async {
    try {
      final response = await dio.post(
        service,
        data: jsonEncode(param),
        options: Options(
          headers: token != null
              ? {
                  HttpHeaders.contentTypeHeader: "application/json",
                  HttpHeaders.authorizationHeader: "Bearer $token",
                }
              : {
                  HttpHeaders.contentTypeHeader: "application/json",
                },
          contentType: "application/json",
        ),
      );
      if (response.statusCode != 200) {
        throw BaseRepositoryException(
          message: "Invalid Http Response ${response.statusCode}",
        );
      }
      Map<String, dynamic> jsonData = jsonDecode(response.data);
      if (jsonData["status"] != "OK") {
        throw BaseRepositoryException(
          message: jsonData["message"],
        );
      } else {
        return jsonData;
      }
    } on DioError catch (e) {
      throw BaseRepositoryException(message: e.message);
    } on SocketException catch (e) {
      throw BaseRepositoryException(message: e.message);
    } on BaseRepositoryException catch (e) {
      throw BaseRepositoryException(message: e.message);
    }
  }

  Future<Map<String, dynamic>> get({
    required String service,
    String? token,
  }) async {
    try {
      final response = await dio.get(
        service,
        options: Options(
          headers: token != null
              ? {
                  HttpHeaders.contentTypeHeader: "application/json",
                  HttpHeaders.authorizationHeader: "Bearer $token",
                }
              : {
                  HttpHeaders.contentTypeHeader: "application/json",
                },
          contentType: "application/json",
        ),
      );
      if (response.statusCode != 200) {
        throw BaseRepositoryException(
          message: "Invalid Http Response ${response.statusCode}",
        );
      }
      Map<String, dynamic> jsonData = jsonDecode(response.data);
      if (jsonData["status"] != "OK") {
        throw BaseRepositoryException(
          message: jsonData["message"],
        );
      } else {
        return jsonData;
      }
    } on DioError catch (e) {
      throw BaseRepositoryException(message: e.message);
    } on SocketException catch (e) {
      throw BaseRepositoryException(message: e.message);
    } on BaseRepositoryException catch (e) {
      throw BaseRepositoryException(message: e.message);
    }
  }
}

class BaseRepositoryException implements Exception {
  final String? message;
  BaseRepositoryException({
    required this.message,
  });
}
