import 'dart:math' as math;

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

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

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

  static String _classifyInterval(double? delta, double? deltaQuadratic, String quadraticStatus) {
    if (delta == null) return 'linear_singular';
    if (deltaQuadratic == null || quadraticStatus != 'ok') return 'quadratic_singular';
    if (delta <= harmonicTol && deltaQuadratic <= harmonicTol) return 'full_quadratic_harmonic';
    if (delta <= harmonicTol && deltaQuadratic > harmonicTol) return 'quadratic_obstruction';
    if (delta > harmonicTol && deltaQuadratic <= harmonicTol) return 'metric_closed_amplitude_gap';
    if (delta <= 0.25 && deltaQuadratic <= 0.25) return 'near_harmonic_complex_interval';
    return 'open_interval';
  }

  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));

    final numerator2 = (u * u - r * r) * (i * i - d * d);
    final denominator2 = (u * u - d * d) * (i * i - r * r);
    if (denominator2.abs() < eps) {
      return LambdaResult(
        lambda: value,
        delta: delta,
        auth: auth,
        status: 'ok',
        quadraticStatus: 'singular',
        intervalStatus: 'quadratic_singular',
      );
    }
    final value2 = numerator2 / denominator2;
    final delta2 = (value2 + 1.0).abs();
    final auth2 = 1.0 / (1.0 + math.max(0.0, delta2));
    final qDen = (u + d) * (i + r);
    final q = qDen.abs() < eps ? null : ((u + r) * (i + d)) / qDen;
    return LambdaResult(
      lambda: value,
      delta: delta,
      auth: auth,
      status: 'ok',
      lambdaQuadratic: value2,
      deltaQuadratic: delta2,
      authQuadratic: auth2,
      qFactor: q,
      quadraticStatus: 'ok',
      intervalStatus: _classifyInterval(delta, delta2, '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 Map<String, dynamic> dStarQuadratic(double u, double i, double r) {
    final denominator = u * u + i * i - 2.0 * r * r;
    if (denominator.abs() < eps) return {'status': 'singular', 'D2': null, 'branches': <double>[]};
    final d2 = (2.0 * u * u * i * i - r * r * i * i - u * u * r * r) / denominator;
    if (d2 < 0) return {'status': 'complex_branch_required', 'D2': d2, 'branches': <double>[]};
    final root = math.sqrt(d2);
    return {'status': 'ok', 'D2': d2, 'branches': <double>[root, -root]};
  }

  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;
  }
}
