import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:qpay/http/http.dart';
import 'package:qpay/models/api_response.dart';
import 'package:qpay/models/pageable_model.dart';

part 'home_controller.freezed.dart';
part 'home_controller.g.dart';

class HomeScreenModel {
  List<Map<String, dynamic>> transactions = [];
  List<Category> categories = [];
  List<BillProvider> providers = [];
  List<BillProvider> filterableProviders = [];
  List<Account> accounts = [];
  BillProvider? selectedProvider;
  dynamic selectedCategory;
  dynamic account = {};
  dynamic profile = {};
  bool isLoading = false;
  bool isLoggedIn = false;
  String? errorMessage;
}

class Account {
  late String name;
  late String description;
  late String image;
  late String currency;
  late String balance;
  late String availableBalance;
  late String accountNumber;
  late String accountType;
  late String accountHolderName;

  Account({
    required this.name,
    required this.description,
    required this.image,
    required this.currency,
    required this.balance,
  });
}

@freezed
abstract class Category with _$Category {
  const factory Category({
    required String name,
    required String description,
    required String? image,
    required String label,
  }) = _Category;

  factory Category.fromJson(Map<String, dynamic> json) =>
      _$CategoryFromJson(json);
}

@freezed
abstract class BillProvider with _$BillProvider {
  const factory BillProvider({
    required String id,
    required String clientId,
    required String name,
    required String description,
    required bool requiresAccount,
    required bool requiresAmount,
    required bool? requiresAmountFromMerchant,
    required bool requiresPhone,
    required bool? requiresReversal,
    required String? additionalDataString,
    required String? processorType,
    required String uid,
    required String image,
    required String label,
    required String category,
    required String? accountFieldName,
  }) = _BillProvider;

  factory BillProvider.fromJson(Map<String, dynamic> json) =>
      _$BillProviderFromJson(json);
}

class HomeController extends ChangeNotifier {
  final HomeScreenModel model = HomeScreenModel();
  final Http http = Http();
  BuildContext context;

  bool _disposed = false;

  HomeController(this.context);

  @override
  void dispose() {
    _disposed = true;
    super.dispose();
  }

  Future<ApiResponse<List<Account>>> getAccounts() async {
    model.accounts = getFakeAccounts();
    if (!_disposed) notifyListeners();
    return ApiResponse.success(model.accounts);
  }

  Future<ApiResponse<List<BillProvider>>> getProviders(String currency) async {
    try {
      model.filterableProviders = getFakeProviders();
      model.transactions = getFakeTransactions();

      model.isLoading = true;
      if (!_disposed) notifyListeners();

      final response = await http.get(
        '/public/providers?currency=$currency&sort=priority,asc',
      );
      List<BillProvider> providers = PageableModel.fromJson(response).content
          .map((e) => BillProvider.fromJson(e as Map<String, dynamic>))
          .toList();
      model.providers = providers;
      model.filterableProviders = model.providers;

      if (model.providers.isNotEmpty) {
        updateSelectedProvider(model.providers[0]);
      }

      model.isLoading = false;
      if (!_disposed) notifyListeners();
      return ApiResponse.success(model.providers);
    } catch (e, s) {
      logger.e(e);
      logger.e(s);
      model.errorMessage = e.toString();
      model.providers = [];
      model.filterableProviders = [];
      model.isLoading = false;
      if (!_disposed) notifyListeners();
      return ApiResponse.failure(
        "Problem fetching providers, are you connected to the internet?",
      );
    }
  }

  void updateSelectedProvider(BillProvider provider) {
    model.selectedProvider = provider;

    // getProducts(provider.uid);

    if (!_disposed) notifyListeners();
  }

  Future<ApiResponse<List<Map<String, dynamic>>>> getTransactions(
    String workspaceId,
  ) async {
    model.isLoading = true;
    if (!_disposed) notifyListeners();

    try {
      Map<String, dynamic> response = await http.get(
        '/public/transaction?sort=createdAt,desc&size=3&page=0&partyType=INDIVIDUAL,GROUP&workspaceId=$workspaceId',
      );

      PageableModel pageableModel = PageableModel.fromJson(response);

      model.transactions = pageableModel.content
          .map((e) => e as Map<String, dynamic>)
          .toList();

      model.isLoading = false;
      if (!_disposed) notifyListeners();
      return ApiResponse.success(model.transactions);
    } catch (e) {
      logger.e(e);
      model.errorMessage = e.toString();
      model.transactions = [];
      model.isLoading = false;
      if (!_disposed) notifyListeners();
      return ApiResponse.failure(
        "Problem fetching transactions, are you connected to the internet?",
      );
    }
  }

  Future<ApiResponse<List<Category>>> getCategories() async {
    // activate skeletonizer
    model.categories = getFakeCategoryData();
    model.isLoading = true;
    if (!_disposed) notifyListeners();

    // Simulate fetching categories
    try {
      final response = await http.get('/public/categories');
      model.categories = (response as List<dynamic>)
          .map((e) => Category.fromJson(e as Map<String, dynamic>))
          .toList();

      if (model.categories.isNotEmpty) {
        updateSelectedCategory(model.categories[0]);
      }

      model.isLoading = false;
      if (!_disposed) notifyListeners();
      return ApiResponse.success(model.categories);
    } on Exception catch (e) {
      logger.e(e);
      model.errorMessage = e.toString();
      model.categories = [];
      model.isLoading = false;
      if (!_disposed) notifyListeners();
      return ApiResponse.failure(
        "Problem fetching categories, are you connected to the internet?",
      );
    }
  }

  void updateSelectedCategory(Category? category) {
    if (category == null) {
      model.filterableProviders = model.providers;
      if (!_disposed) notifyListeners();
      return;
    }

    model.selectedCategory = category;

    model.filterableProviders = model.providers
        .where((element) => element.category == category.label)
        .toList();

    if (!_disposed) notifyListeners();
  }

  List<Map<String, dynamic>> getFakeTransactions() {
    return [
      {
        "id": "77b2c479-b803-49fc-b5c3-acbf52cba633",
        "trace": "22a3044b-8f53-4e43-b4f5-c77dcec48d2d",
        "amount": 25.0,
        "charge": 0.0,
        "gatewayCharge": 0.25,
        "tax": 0.5,
        "totalAmount": 25.75,
        "reference": "c838136c-c015-4a0b-9c7c-52f127c8c109",
        "paymentProcessorLabel": "MPGS",
        "paymentProcessorImage": "visa-mastercard.png",
        "debitPhone": "",
        "debitAccount": "",
        "debitCurrency": "USD",
        "debitRef": "Velocity",
        "creditPhone": "",
        "creditAccount": "07088597534",
        "billClientId": "powertel_zesa",
        "billName": "Zesa",
        "type": "REQUEST",
        "authType": "WEB",
        "errorMessage": "",
        "responseCode": "00",
        "status": "SUCCESS",
        "sdkActionId": "0aa0d90d-6132-48a7-a5d8-e796f757c73f",
      },
    ];
  }

  List<BillProvider> getFakeProviders() {
    return [
      BillProvider(
        id: '1',
        clientId: 'econet_airtime',
        name: 'Econet Airtime',
        description: 'Econet Airtime',
        requiresAccount: false,
        requiresAmount: true,
        requiresAmountFromMerchant: false,
        requiresPhone: true,
        requiresReversal: true,
        additionalDataString: '',
        processorType: 'AIRTIME',
        uid: '78f1f497-c9eb-401c-b22c-884756e68e40',
        image: 'econet.png',
        label: 'ECONET',
        category: 'ECONET',
        accountFieldName: 'Phone Number',
      ),
    ];
  }

  // required by skeletonizer
  List<Category> getFakeCategoryData() {
    return [
      Category(
        name: 'Airtime',
        description: 'Pay your electricity bills easily',
        image: 'assets/images/electricity.png',
        label: 'Electricity',
      ),
    ];
  }

  List<Account> getFakeAccounts() {
    return [
      Account(
        name: 'Vusumuzi Khoza',
        description: 'USD Wallet',
        image: 'united-states.png',
        currency: 'USD',
        balance: '47655.02',
      ),
      Account(
        name: 'Vusumuzi Khoza',
        description: 'ZWG Wallet',
        image: 'zimbabwe.png',
        currency: 'ZWL',
        balance: '36.00',
      ),
    ];
  }
}
