import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:helpers/helpers/print.dart';
import 'package:rluv/global/api.dart';
import 'package:rluv/models/budget.dart';
import 'package:rluv/models/budget_category_model.dart';
import 'package:shared_preferences/shared_preferences.dart';

import '../models/family_model.dart';
import '../models/shared_note.dart';
import '../models/transaction_model.dart';
import '../models/user.dart';

StateProvider<SharedPreferences?> prefsProvider =
    StateProvider<SharedPreferences?>((ref) => null);

// final StateProvider<Token?> tokenProvider = StateProvider<Token?>((ref) {
//   // final tokenStr = prefs.getString('jwt');
//   // if (tokenStr != null) {
//   // return Token.fromJson(jsonDecode(tokenStr));
//   // }
//   return null;
// });

final Provider<User?> userProvider = Provider<User?>(
  (ref) {
    final dash = ref.watch(dashboardProvider);
    if (dash == null) return null;
    final userData = dash['user'] as Map<String, dynamic>;
    return User.fromJson(userData);
  },
);

final Provider<FamilyModel?> familyProvider = Provider<FamilyModel?>(
  (ref) {
    final dash = ref.watch(dashboardProvider);
    if (dash == null) return null;
    final familyData = dash['family'] as Map<String, dynamic>;
    return FamilyModel.fromJson(familyData);
  },
);

final Provider<List<BudgetCategory>> budgetCategoriesProvider =
    Provider<List<BudgetCategory>>((ref) {
  final dash = ref.watch(dashboardProvider);
  if (dash == null) return [];
  final categoriesData = dash['budget_categories'] as List<dynamic>;
  final categories = categoriesData
      .map(
        (e) => BudgetCategory.fromJson(e as Map<String, dynamic>),
      )
      .toList();

  final prefs = ref.read(prefsProvider);
  final budgetJson = jsonEncode({'budget_categories': categoriesData});
  printBlue('updated prefs stored categories');
  prefs?.setString('budget_categories', budgetJson);

  return categories;
});

final Provider<Budget?> budgetProvider = Provider<Budget?>(
  (ref) {
    final dash = ref.watch(dashboardProvider);
    if (dash == null) return null;
    final budgetData = dash['budget'] as Map<String, dynamic>?;
    if (budgetData == null) return null;
    return Budget.fromJson(budgetData);
  },
);

final Provider<List<Transaction>> transactionsProvider =
    Provider<List<Transaction>>((ref) {
  final dash = ref.watch(dashboardProvider);
  if (dash == null) return [];
  final transactions = dash['transactions'] as List<dynamic>;
  return transactions
      .map(
        (e) => Transaction.fromJson(e as Map<String, dynamic>),
      )
      .toList();
});

final Provider<List<SharedNote>> sharedNotesProvider =
    Provider<List<SharedNote>>(
  (ref) {
    final dash = ref.watch(dashboardProvider);
    if (dash == null) return [];
    final sharedNotes = dash['shared_notes'] as List<dynamic>;
    return sharedNotes
        .map(
          (e) => SharedNote.fromJson(e as Map<String, dynamic>),
        )
        .toList();
  },
);

final dashboardProvider =
    StateNotifierProvider<DashBoardStateNotifier, Map<String, dynamic>?>(
  (ref) {
    return DashBoardStateNotifier(ref);
  },
);

final loadingStateProvider = StateProvider<bool>(
  (ref) => false,
);

class DashBoardStateNotifier extends StateNotifier<Map<String, dynamic>?> {
  DashBoardStateNotifier(this.ref) : super(null);

  StateNotifierProviderRef ref;

  Future<void> fetchDashboard() async {
    WidgetsBinding.instance.addPostFrameCallback(
      (_) => ref.read(loadingStateProvider.notifier).state = true,
    );
    final token = ref.read(tokenProvider);
    if (token?.familyId == null) {
      printPink('No token, cannot fetch dashboard');
      return;
    }
    printAmber('Fetching dashboard');
    state = await ref.read(apiProvider.notifier).get("dashboard");
    WidgetsBinding.instance.addPostFrameCallback(
      (_) => ref.read(loadingStateProvider.notifier).state = false,
    );
  }

  void update(Map<String, dynamic> data) {
    if (state == null) {
      printPink('Cant update data, state is null');
      return;
    }
    if (data.keys.length != 1 || data.values.length != 1) {
      throw Exception('Only one key/val used in update');
    }
    final key = data.keys.first;
    switch (key) {
      case 'transactions':
      case 'budget_categories':
      case 'shared_notes':
        final subStateList = state![key] as List<dynamic>;
        final subStateListObj = subStateList
            .map(
              (e) => e as Map<String, dynamic>,
            )
            .toList();
        subStateListObj.removeWhere(
          (element) =>
              element['id'] ==
              (data.values.first as Map<String, dynamic>)['id'],
        );
        subStateListObj.add(data.values.first);

        final newState = state;
        newState![key] = subStateListObj;
        state = {...newState};
        // printBlue(state);
        break;
      default:
        break;
    }
  }

  void add(Map<String, dynamic> data) {
    if (state == null) {
      printPink('Cant add data, state is null');
      return;
    }
    if (data.keys.length != 1 || data.values.length != 1) {
      throw Exception('Only one key/val used in add');
    }
    final key = data.keys.first;
    switch (key) {
      case 'transactions':
      case 'budget_categories':
      case 'shared_noted':
        final subStateList = state![key] as List<dynamic>;
        final newState = state;
        subStateList.add(data.values.first);
        newState![key] = subStateList;
        state = {...newState};
        // printBlue(state);
        break;
      default:
        break;
    }
  }

  void removeWithId(String stateKey, int id) {
    if (state == null) {
      printPink('Cant remove data, state is null');
      return;
    }
    switch (stateKey) {
      case 'transactions':
      case 'budget_categories':
      case 'shared_noted':
        final subStateList = state![stateKey] as List<dynamic>;
        final newState = state;
        subStateList
            .removeWhere((e) => (e as Map<String, dynamic>)['id'] == id);
        newState![stateKey] = subStateList;
        state = {...newState};
        // printBlue(state);
        break;
      default:
        break;
    }
  }
}
