import 'package:hooks_riverpod/hooks_riverpod.dart';

import '../../../common/domain/metal_type.dart';
import '../../market/data/mock_market_repository.dart';
import '../data/asset_models.dart';
import '../data/mock_asset_repository.dart';

final assetPortfolioControllerProvider =
    StateNotifierProvider<AssetPortfolioController, PortfolioSummary>((ref) {
      final holdings = ref.watch(mockAssetRepositoryProvider).loadHoldings();
      final market = ref.watch(mockMarketRepositoryProvider);
      return AssetPortfolioController(
        holdings: holdings,
        spotPriceFor: (metal) => market.quoteFor(metal).spotPrice,
        changeFor: (metal) => market.quoteFor(metal).changeAmount,
      );
    });

class AssetPortfolioController extends StateNotifier<PortfolioSummary> {
  AssetPortfolioController({
    required List<GoldAssetHolding> holdings,
    required double Function(MetalType metal) spotPriceFor,
    required double Function(MetalType metal) changeFor,
    this.recycleDiscount = 0.97,
  }) : _holdings = holdings,
       _spotPriceFor = spotPriceFor,
       _changeFor = changeFor,
       super(
         _buildSummary(
           holdings: holdings,
           spotPriceFor: spotPriceFor,
           changeFor: changeFor,
           recycleDiscount: recycleDiscount,
         ),
       );

  final List<GoldAssetHolding> _holdings;
  final double Function(MetalType metal) _spotPriceFor;
  final double Function(MetalType metal) _changeFor;
  final double recycleDiscount;

  void addHolding(GoldAssetHolding holding) {
    _holdings.add(holding);
    state = _buildSummary(
      holdings: _holdings,
      spotPriceFor: _spotPriceFor,
      changeFor: _changeFor,
      recycleDiscount: recycleDiscount,
    );
  }

  static PortfolioSummary _buildSummary({
    required List<GoldAssetHolding> holdings,
    required double Function(MetalType metal) spotPriceFor,
    required double Function(MetalType metal) changeFor,
    required double recycleDiscount,
  }) {
    final active = holdings
        .where((holding) => holding.status == HoldingStatus.active)
        .toList(growable: false);
    final valuations = [
      for (final holding in active)
        HoldingValuation(
          holding: holding,
          materialValue: holding.materialValue(spotPriceFor(holding.metal)),
          todayChange:
              holding.weightGram * holding.purity * changeFor(holding.metal),
        ),
    ];

    final totalValue = valuations.fold<double>(
      0,
      (sum, item) => sum + item.materialValue,
    );
    final todayChange = valuations.fold<double>(
      0,
      (sum, item) => sum + item.todayChange,
    );
    final totalCost = active.fold<double>(
      0,
      (sum, holding) => sum + (holding.costAmount ?? 0),
    );
    final totalWeight = active.fold<double>(
      0,
      (sum, holding) => sum + holding.weightGram,
    );

    return PortfolioSummary(
      totalValue: totalValue,
      todayChangeAmount: todayChange,
      todayChangePercent: totalValue == 0 ? 0 : todayChange / totalValue * 100,
      totalCost: totalCost,
      totalWeightGram: totalWeight,
      recycleReferenceValue: totalValue * recycleDiscount,
      breakdowns: [
        for (final metal in MetalType.values)
          _breakdownFor(metal, active, spotPriceFor),
      ].where((item) => item.count > 0).toList(growable: false),
      holdings: valuations,
    );
  }

  static MetalBreakdown _breakdownFor(
    MetalType metal,
    List<GoldAssetHolding> holdings,
    double Function(MetalType metal) spotPriceFor,
  ) {
    final items = holdings.where((holding) => holding.metal == metal).toList();
    return MetalBreakdown(
      metal: metal,
      value: items.fold<double>(
        0,
        (sum, holding) => sum + holding.materialValue(spotPriceFor(metal)),
      ),
      weightGram: items.fold<double>(
        0,
        (sum, holding) => sum + holding.weightGram,
      ),
      count: items.length,
    );
  }
}
