import 'dart:math' as math;

import '../models/klt_models.dart';

class LambdaEngine {
  static const double eps = 1e-12;

  static LambdaResult lambdaTruth(Reper4 reper) {
    return lambdaTruthValues(reper.u, reper.i, reper.r, reper.d);
  }

  static LambdaResult lambdaTruthValues(double u, double i, double r, double d) {
    final numerator = (u - r) * (i - d);
    final denominator = (u - d) * (i - r);
    if (denominator.abs() < eps) {
      return const LambdaResult(
        status: 'singular',
        message: 'Projective denominator is zero or too close to zero.',
      );
    }
    final value = numerator / denominator;
    final delta = (value + 1.0).abs();
    final auth = 1.0 / (1.0 + math.max(0.0, delta));
    return LambdaResult(lambda: value, delta: delta, auth: auth, status: 'ok');
  }

  static double? dStar(double u, double i, double r) {
    final denominator = u + i - 2.0 * r;
    if (denominator.abs() < eps) return null;
    return (2.0 * u * i - u * r - i * r) / denominator;
  }

  static double? magnitudeShiftIndex(double costA, double costB) {
    if (costA <= 0 || costB <= 0) return null;
    return (math.log(costB / costA)).abs();
  }

  static double? deficitIndex(double cMin, double cOffer) {
    if (cMin <= 0) return null;
    return math.max(0.0, (cMin - cOffer) / cMin);
  }

  static double collapseRiskIndex({
    Iterable<double> severities = const [],
    Iterable<double> deltas = const [],
    Iterable<double> msiValues = const [],
    Iterable<double> diValues = const [],
  }) {
    final values = <double>[];
    values.addAll(severities.map((s) => s.clamp(0.0, 1.0).toDouble()));
    values.addAll(deltas.map((d) => (d.clamp(0.0, 5.0) / 5.0).toDouble()));
    values.addAll(msiValues.map((m) => (m.clamp(0.0, 2.0) / 2.0).toDouble()));
    values.addAll(diValues.map((di) => di.clamp(0.0, 1.0).toDouble()));
    if (values.isEmpty) return 0.0;
    return (values.reduce((a, b) => a + b) / values.length).clamp(0.0, 1.0).toDouble();
  }

  static double scalarFromText(String text) {
    final trimmed = text.trim();
    if (trimmed.isEmpty) return 0.0;
    final codes = trimmed.runes.map((r) => r.toDouble()).toList();
    return codes.reduce((a, b) => a + b) / codes.length;
  }
}
