import 'package:em2rp/models/event_model.dart';
import 'package:em2rp/models/event_statistics_models.dart';
import 'package:flutter/material.dart';

class EventStatisticsService {
  const EventStatisticsService();

  static const double _taxRatio = 1.2;

  EventStatisticsSummary buildSummary({
    required List<EventModel> events,
    required EventStatisticsFilter filter,
    required Map<String, String> eventTypeNames,
  }) {
    final filteredEvents =
        events.where((event) => _matchesFilter(event, filter)).toList();

    if (filteredEvents.isEmpty) {
      return EventStatisticsSummary.empty;
    }

    var validatedEvents = 0;
    var pendingEvents = 0;
    var canceledEvents = 0;

    var validatedAmount = 0.0;
    var pendingAmount = 0.0;
    var canceledAmount = 0.0;

    var baseAmount = 0.0;
    var optionsAmount = 0.0;

    final eventAmounts = <double>[];
    final byType = <String, _EventTypeAccumulator>{};
    final optionStats = <String, _OptionAccumulator>{};

    for (final event in filteredEvents) {
      final base = _toHtAmount(event.basePrice);
      final optionTotal = _computeOptionsTotal(event);
      final amount = base + optionTotal;
      final isValidated = event.status == EventStatus.confirmed;

      eventAmounts.add(amount);
      baseAmount += base;
      optionsAmount += optionTotal;

      switch (event.status) {
        case EventStatus.confirmed:
          validatedEvents += 1;
          validatedAmount += amount;
          break;
        case EventStatus.waitingForApproval:
          pendingEvents += 1;
          pendingAmount += amount;
          break;
        case EventStatus.canceled:
          canceledEvents += 1;
          canceledAmount += amount;
          break;
      }

      final eventTypeId = event.eventTypeId;
      final eventTypeName = eventTypeNames[eventTypeId] ?? 'Type inconnu';
      final typeAccumulator = byType.putIfAbsent(
        eventTypeId,
        () => _EventTypeAccumulator(
            eventTypeId: eventTypeId, eventTypeName: eventTypeName),
      );
      typeAccumulator.totalEvents += 1;
      typeAccumulator.totalAmount += amount;
      switch (event.status) {
        case EventStatus.confirmed:
          typeAccumulator.validatedAmount += amount;
          break;
        case EventStatus.waitingForApproval:
          typeAccumulator.pendingAmount += amount;
          break;
        case EventStatus.canceled:
          typeAccumulator.canceledAmount += amount;
          break;
      }

      for (final rawOption in event.options) {
        final optionPrice = _toHtAmount(_toDouble(rawOption['price']));
        final optionQuantity = _toInt(rawOption['quantity'], fallback: 1);
        if (optionPrice == 0) {
          continue;
        }

        final optionId = (rawOption['id'] ??
                rawOption['code'] ??
                rawOption['name'] ??
                'option')
            .toString();
        final optionLabel = _buildOptionLabel(rawOption, optionId);
        final optionAmount = optionPrice * optionQuantity;

        final optionAccumulator = optionStats.putIfAbsent(
          optionId,
          () =>
              _OptionAccumulator(optionKey: optionId, optionLabel: optionLabel),
        );
        optionAccumulator.usageCount += 1;
        if (isValidated) {
          optionAccumulator.validatedUsageCount += 1;
        }
        optionAccumulator.quantity += optionQuantity;
        optionAccumulator.totalAmount += optionAmount;
      }
    }

    final byEventType = byType.values
        .map((accumulator) => EventTypeStatistics(
              eventTypeId: accumulator.eventTypeId,
              eventTypeName: accumulator.eventTypeName,
              totalEvents: accumulator.totalEvents,
              totalAmount: accumulator.totalAmount,
              validatedAmount: accumulator.validatedAmount,
              pendingAmount: accumulator.pendingAmount,
              canceledAmount: accumulator.canceledAmount,
            ))
        .toList()
      ..sort((a, b) => b.totalAmount.compareTo(a.totalAmount));

    final topOptions = optionStats.values
        .map((accumulator) => OptionStatistics(
              optionKey: accumulator.optionKey,
              optionLabel: accumulator.optionLabel,
              usageCount: accumulator.usageCount,
              validatedUsageCount: accumulator.validatedUsageCount,
              quantity: accumulator.quantity,
              totalAmount: accumulator.totalAmount,
            ))
        .toList()
      ..sort((a, b) {
        final validatedComparison =
            b.validatedUsageCount.compareTo(a.validatedUsageCount);
        if (validatedComparison != 0) {
          return validatedComparison;
        }
        return b.totalAmount.compareTo(a.totalAmount);
      });

    return EventStatisticsSummary(
      totalEvents: filteredEvents.length,
      validatedEvents: validatedEvents,
      pendingEvents: pendingEvents,
      canceledEvents: canceledEvents,
      totalAmount: validatedAmount + pendingAmount + canceledAmount,
      validatedAmount: validatedAmount,
      pendingAmount: pendingAmount,
      canceledAmount: canceledAmount,
      baseAmount: baseAmount,
      optionsAmount: optionsAmount,
      medianAmount: _computeMedian(eventAmounts),
      byEventType: byEventType,
      topOptions: topOptions.take(8).toList(),
    );
  }

  bool _matchesFilter(EventModel event, EventStatisticsFilter filter) {
    if (!_overlapsRange(event, filter.period)) {
      return false;
    }

    if (!filter.selectedStatuses.contains(event.status)) {
      return false;
    }

    if (filter.eventTypeIds.isNotEmpty &&
        !filter.eventTypeIds.contains(event.eventTypeId)) {
      return false;
    }

    return true;
  }

  bool _overlapsRange(EventModel event, DateTimeRange range) {
    return !event.endDateTime.isBefore(range.start) &&
        !event.startDateTime.isAfter(range.end);
  }

  double _computeOptionsTotal(EventModel event) {
    return event.options.fold<double>(0.0, (sum, option) {
      final optionPrice = _toHtAmount(_toDouble(option['price']));
      final optionQuantity = _toInt(option['quantity'], fallback: 1);
      return sum + (optionPrice * optionQuantity);
    });
  }

  double _toHtAmount(double storedAmount) {
    return storedAmount / _taxRatio;
  }

  double _toDouble(dynamic value) {
    if (value == null) {
      return 0.0;
    }
    if (value is num) {
      return value.toDouble();
    }
    return double.tryParse(value.toString()) ?? 0.0;
  }

  int _toInt(dynamic value, {int fallback = 0}) {
    if (value == null) {
      return fallback;
    }
    if (value is int) {
      return value;
    }
    if (value is num) {
      return value.toInt();
    }
    return int.tryParse(value.toString()) ?? fallback;
  }

  String _buildOptionLabel(Map<String, dynamic> option, String fallback) {
    final code = (option['code'] ?? '').toString().trim();
    final name = (option['name'] ?? '').toString().trim();

    if (code.isNotEmpty && name.isNotEmpty) {
      return '$code - $name';
    }

    if (name.isNotEmpty) {
      return name;
    }

    if (code.isNotEmpty) {
      return code;
    }

    return fallback;
  }

  double _computeMedian(List<double> values) {
    if (values.isEmpty) {
      return 0.0;
    }

    final sorted = [...values]..sort();
    final middleIndex = sorted.length ~/ 2;

    if (sorted.length.isOdd) {
      return sorted[middleIndex];
    }

    return (sorted[middleIndex - 1] + sorted[middleIndex]) / 2;
  }
}

class _EventTypeAccumulator {
  final String eventTypeId;
  final String eventTypeName;
  int totalEvents = 0;
  double totalAmount = 0.0;
  double validatedAmount = 0.0;
  double pendingAmount = 0.0;
  double canceledAmount = 0.0;

  _EventTypeAccumulator({
    required this.eventTypeId,
    required this.eventTypeName,
  });
}

class _OptionAccumulator {
  final String optionKey;
  final String optionLabel;
  int usageCount = 0;
  int validatedUsageCount = 0;
  int quantity = 0;
  double totalAmount = 0.0;

  _OptionAccumulator({
    required this.optionKey,
    required this.optionLabel,
  });
}
