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

import 'package:flutter/material.dart';
import 'package:mobile/models/cart_item.dart';
import 'package:mobile/models/product.dart';
import 'package:mobile/results.dart';
import 'package:path_provider/path_provider.dart';

class ProductIdException implements Exception {}

abstract class CartController extends ChangeNotifier {
  List<CartItem> allCartItems();
  CartItem? withProductId(int productId);
  void incrementAmount(int productId);
  void decrementAmount(int productId);
  bool willRemoveOnNextDecrement(int productId);
  void removeCartItem(int productId);
  void addToCart(Product product);
  int totalItemsInCart();
  int totalPrice();
  void clearCart();
}

class CartControllerMemory extends CartController {
  final List<CartItem> cart = [];

  CartControllerMemory();

  @override
  List<CartItem> allCartItems() {
    return cart;
  }

  @override
  CartItem? withProductId(int productId) {
    for (var i = 0; i < cart.length; i++) {
      if (cart[i].product.id == productId) {
        return cart[i];
      }
    }
    return null;
  }

  @override
  void incrementAmount(int productId) {
    final cartItem = withProductId(productId);
    if (cartItem == null) {
      throw ProductIdException();
    }
    cartItem.amount++;
    notifyListeners();
  }

  @override
  void decrementAmount(int productId) {
    final cartItem = withProductId(productId);
    if (cartItem == null) {
      throw ProductIdException();
    }
    cartItem.amount -= 1;
    if (cartItem.amount <= 0) {
      cart.remove(cartItem);
    }
    notifyListeners();
  }

  @override
  bool willRemoveOnNextDecrement(int productId) {
    final cartItem = withProductId(productId);
    if (cartItem == null) {
      throw ProductIdException();
    }
    return cartItem.amount <= 1;
  }

  @override
  void removeCartItem(int productId) {
    final cartItem = withProductId(productId);
    if (cartItem == null) {
      throw ProductIdException();
    }
    cart.remove(cartItem);
    notifyListeners();
  }

  @override
  void addToCart(Product product) {
    final cartItem = withProductId(product.id);
    if (cartItem == null) {
      cart.add(CartItem(product: product, amount: 1));
    } else {
      cartItem.amount++;
    }
    notifyListeners();
  }

  @override
  int totalItemsInCart() {
    return cart.fold<int>(0, (prev, cartItem) => prev + cartItem.amount);
  }

  @override
  int totalPrice() {
    return cart.fold<int>(
        0,
        (prev, cartItem) =>
            prev + cartItem.amount * cartItem.product.priceDkkCent);
  }

  @override
  void clearCart() {
    cart.clear();
    notifyListeners();
  }

  Result<Null, String> pay() {
    return const Err("Not implemented");
  }
}

class CartControllerCache extends CartControllerMemory {
  static Future<File> get _cacheFile async {
    final directory = await getApplicationCacheDirectory();
    return File("${directory.path}/cart.json").create();
  }

  CartControllerCache() {
    load();
  }

  void save() async {
    final json =
        jsonEncode(cart.map((cartItem) => CartItem.toJson(cartItem)).toList());
    (await _cacheFile).writeAsString(json);
  }

  void load() async {
    final json = await (await _cacheFile).readAsString();
    print("Loading cache: $json");
    if (json.isEmpty) {
      return;
    }
    final res = jsonDecode(json);
    final cartItems = (res as List<dynamic>)
        .map(((cartItems) => CartItem.fromJson(cartItems)))
        .toList();
    cart.insertAll(0, cartItems);
    notifyListeners();
  }

  @override
  void incrementAmount(int productId) {
    super.incrementAmount(productId);
    save();
  }

  @override
  void decrementAmount(int productId) {
    super.decrementAmount(productId);
    save();
  }

  @override
  void removeCartItem(int productId) {
    super.removeCartItem(productId);
    save();
  }

  @override
  void addToCart(Product product) {
    super.addToCart(product);
    save();
  }

  @override
  void clearCart() {
    super.clearCart();
    save();
  }
}
