import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/common_models/data_points.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';

/// A customizable line graph widget using `fl_chart`.
///
/// Displays a line chart with configurable axis labels, colors, and data points.
/// Useful for visualizing time series or other sequential data.
///
/// **Parameters:**
/// - [dataPoints]: List of `DataPoint` objects to plot.
/// - [secondaryDataPoints]: Optional list for a second line (e.g., diastolic in blood pressure).
/// - [leftLabelFormatter]: Function to build left axis labels.
/// - [bottomLabelFormatter]: Function to build bottom axis labels.
/// - [width]: Optional width of the chart.
/// - [height]: Required height of the chart.
/// - [maxY], [maxX], [minX]: Axis bounds.
/// - [spotColor]: Color of the touched spot marker.
/// - [graphColor]: Color of the line.
/// - [secondaryGraphColor]: Color of the secondary line.
/// - [graphShadowColor]: Color of the area below the line.
/// - [graphGridColor]: Color of the grid lines.
/// - [bottomLabelColor]: Color of bottom axis labels.
/// - [bottomLabelSize]: Font size for bottom axis labels.
/// - [bottomLabelFontWeight]: Font weight for bottom axis labels.
/// - [leftLabelInterval]: Interval between left axis labels.
/// - [leftLabelReservedSize]: Reserved space for left axis labels.
/// - [scrollDirection]: Axis direction for scrolling.
/// - [showBottomTitleDates]: Whether to show bottom axis labels.
/// - [isFullScreeGraph]: Whether the graph is fullscreen.
/// - [makeGraphBasedOnActualValue]: Use `actualValue` for plotting.
/// - [isRTL]: Whether the graph should render in right-to-left direction.
///
/// Example usage:
/// ```dart
/// CustomGraph(
///   dataPoints: sampleData,
///   leftLabelFormatter: (value) => ...,
///   bottomLabelFormatter: (value, dataPoints) => ...,
///   height: 200,
///   scrollDirection: Axis.horizontal,
///   maxY: 100,
///   maxX: 2.5,
/// )
class CustomGraph extends StatelessWidget {
  final List<DataPoint> dataPoints;
  final List<DataPoint>? secondaryDataPoints; // For dual-line graphs (e.g., blood pressure)
  final double? width;
  final double height;
  final double? maxY;
  final double? maxX;
  final double? minX;
  final Color? spotColor;
  final Color? graphColor;
  final Color? secondaryGraphColor; // Color for secondary line
  final Color? graphShadowColor;
  final Color? graphGridColor;
  final Color? bottomLabelColor;
  final double? bottomLabelSize;
  final FontWeight? bottomLabelFontWeight;
  final double? leftLabelInterval;
  final double? leftLabelReservedSize;
  final double? bottomLabelReservedSize;
  final bool? showGridLines;
  final GetDrawingGridLine? getDrawingHorizontalLine;
  final double? horizontalInterval;
  final double? minY;
  final bool showShadow;
  final bool showLinePoints;
  final double? cutOffY;
  final RangeAnnotations? rangeAnnotations;
  final bool isRTL;

  ///creates the left label and provide it to the chart as it will be used  by other part of the application so the label will be different for every chart
  final Widget Function(double) leftLabelFormatter;
  final Widget Function(double, List<DataPoint>) bottomLabelFormatter;

  final Axis scrollDirection;
  final bool showBottomTitleDates;
  final bool isFullScreeGraph;
  final bool makeGraphBasedOnActualValue;

  const CustomGraph(
      {super.key,
      required this.dataPoints,
      this.secondaryDataPoints,
      required this.leftLabelFormatter,
      this.width,
      required this.scrollDirection,
      required this.height,
      this.maxY,
      this.maxX,
      this.showBottomTitleDates = true,
      this.isFullScreeGraph = false,
      this.spotColor,
      this.graphColor,
      this.secondaryGraphColor,
      this.graphShadowColor,
      this.graphGridColor,
      this.bottomLabelColor,
      this.bottomLabelFontWeight = FontWeight.w600,
      this.bottomLabelSize,
      this.leftLabelInterval,
      this.leftLabelReservedSize,
      this.bottomLabelReservedSize,
      this.makeGraphBasedOnActualValue = false,
      required this.bottomLabelFormatter,
      this.minX,
      this.showGridLines = false,
      this.getDrawingHorizontalLine,
      this.horizontalInterval,
      this.minY,
      this.showShadow = false,
      this.showLinePoints = false,
      this.cutOffY = 0,
      this.rangeAnnotations,
      this.isRTL = false});

  @override
  Widget build(BuildContext context) {
    final resolvedSpotColor = spotColor ?? AppColors.bgGreenColor;
    final resolvedGraphColor = graphColor ?? AppColors.bgGreenColor;
    final resolvedGraphShadowColor = graphShadowColor ?? AppColors.graphGridColor;
    final resolvedGraphGridColor = graphGridColor ?? AppColors.graphGridColor;

    // For RTL, reverse the data points so the graph reads right-to-left
    final displayDataPoints = isRTL ? dataPoints.reversed.toList() : dataPoints;
    final displaySecondaryDataPoints = (isRTL && secondaryDataPoints != null)
        ? secondaryDataPoints!.reversed.toList()
        : secondaryDataPoints;

    return Material(
      color: AppColors.whiteColor,
      child: SizedBox(
        width: width,
        height: height,
        child: LineChart(
          LineChartData(
              minY: minY ?? 0,
              maxY: maxY,
              maxX: maxX,
              minX: minX,
              lineTouchData: LineTouchData(
                getTouchLineEnd: (_, __) => 0,
                handleBuiltInTouches: true,
                touchCallback: (FlTouchEvent event, LineTouchResponse? touchResponse) {
                  // Let fl_chart handle the touch
                },
                getTouchedSpotIndicator: (barData, indicators) {
                  // Show custom marker for touched spot with correct color per line
                  return indicators.map((int index) {
                    // Determine which line is being touched based on barData
                    Color dotColor = resolvedSpotColor;
                    if (secondaryDataPoints != null && barData.spots.length > 0) {
                      // Check if this is the secondary line by comparing the first spot's color
                      final gradient = barData.gradient;
                      if (gradient != null && gradient.colors.isNotEmpty) {
                        dotColor = gradient.colors.first;
                      }
                    }

                    return TouchedSpotIndicatorData(
                      FlLine(color: Colors.transparent),
                      FlDotData(
                        show: true,
                        getDotPainter: (spot, percent, barData, idx) {
                          return FlDotCirclePainter(
                            radius: 8,
                            color: dotColor,
                            strokeWidth: 2,
                            strokeColor: Colors.white,
                          );
                        },
                      ),
                    );
                  }).toList();
                },
                enabled: true,
                touchTooltipData: LineTouchTooltipData(
                  getTooltipColor: (_) => Colors.white,
                  getTooltipItems: (touchedSpots) {
                    if (touchedSpots.isEmpty) return [];
                    // Show tooltip for each touched line
                    return touchedSpots.map((spot) {
                      // Determine which dataset this spot belongs to
                      final isSecondary = displaySecondaryDataPoints != null && spot.barIndex == 1;
                      final dataPoint = isSecondary
                          ? displaySecondaryDataPoints[spot.x.toInt()]
                          : displayDataPoints[spot.x.toInt()];

                      return LineTooltipItem(
                        '${dataPoint.actualValue} ${dataPoint.unitOfMeasurement ?? ""} - ${dataPoint.displayTime}',
                        TextStyle(color: Colors.black, fontSize: 12.f, fontWeight: FontWeight.w600, fontFamily: "Poppins"),
                      );
                    }).toList();
                  },
                ),
              ),
              titlesData: FlTitlesData(
                leftTitles: AxisTitles(
                  sideTitles: SideTitles(
                    showTitles: !isRTL,
                    reservedSize: leftLabelReservedSize ?? 80,
                    interval: leftLabelInterval ?? .1,
                    getTitlesWidget: (value, _) {
                      return leftLabelFormatter(value);
                    },
                  ),
                ),
                rightTitles: AxisTitles(
                  sideTitles: SideTitles(
                    showTitles: isRTL,
                    reservedSize: leftLabelReservedSize ?? 80,
                    interval: leftLabelInterval ?? .1,
                    getTitlesWidget: (value, _) {
                      return leftLabelFormatter(value);
                    },
                  ),
                ),
                bottomTitles: AxisTitles(
                  axisNameSize: 20,
                  sideTitles: SideTitles(
                    showTitles: showBottomTitleDates,
                    reservedSize: bottomLabelReservedSize ?? 20,
                    getTitlesWidget: (value, _) {
                      return bottomLabelFormatter(value, displayDataPoints);
                    },
                    interval: 1, // ensures 1:1 mapping with spots
                  ),
                ),
                topTitles: AxisTitles(),
              ),
              borderData: FlBorderData(
                show: true,
                border: Border(
                  bottom: BorderSide.none,
                  left: isRTL ? BorderSide.none : const BorderSide(color: Colors.grey, width: .5),
                  right: isRTL ? const BorderSide(color: Colors.grey, width: .5) : BorderSide.none,
                  top: BorderSide.none,
                ),
              ),
              lineBarsData: _buildColoredLineSegments(displayDataPoints, showLinePoints, resolvedGraphColor, resolvedGraphShadowColor,
                  secondaryPoints: displaySecondaryDataPoints),
              gridData: FlGridData(
                show: showGridLines ?? true,
                drawVerticalLine: false,
                horizontalInterval: horizontalInterval,
                getDrawingHorizontalLine: getDrawingHorizontalLine ??
                    (value) {
                      return FlLine(
                        color: resolvedGraphGridColor,
                        strokeWidth: 1,
                        dashArray: [5, 5],
                      );
                    },
              ),
              rangeAnnotations: rangeAnnotations),
        ),
      ),
    );
  }

  List<LineChartBarData> _buildColoredLineSegments(List<DataPoint> dataPoints, bool showLinePoints, Color resolvedGraphColor, Color resolvedGraphShadowColor,
      {List<DataPoint>? secondaryPoints}) {
    final List<FlSpot> allSpots = dataPoints.asMap().entries.map((entry) {
      double value = (makeGraphBasedOnActualValue) ? double.tryParse(entry.value.actualValue) ?? 0.0 : entry.value.value;
      return FlSpot(entry.key.toDouble(), value);
    }).toList();

    var data = <LineChartBarData>[
      LineChartBarData(
        spots: allSpots,
        isCurved: true,
        isStrokeCapRound: true,
        isStrokeJoinRound: true,
        barWidth: 3,
        gradient: LinearGradient(
          colors: [resolvedGraphColor, resolvedGraphColor],
          begin: Alignment.centerLeft,
          end: Alignment.centerRight,
        ),
        dotData: FlDotData(
          show: true,
          getDotPainter: (spot, percent, barData, idx) {
            return FlDotCirclePainter(
              radius: 6,
              color: resolvedGraphColor,
              strokeWidth: 2,
              strokeColor: Colors.white,
            );
          },
        ),
        belowBarData: BarAreaData(
          show: showShadow,
          applyCutOffY: cutOffY != null,
          cutOffY: cutOffY ?? 0,
          gradient: LinearGradient(
            colors: [
              resolvedGraphShadowColor,
              Colors.white,
            ],
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
          ),
        ),
      )
    ];

    // Add secondary line if provided (for dual-line graphs like blood pressure)
    if (secondaryPoints != null && secondaryPoints.isNotEmpty) {
      final List<FlSpot> secondarySpots = secondaryPoints.asMap().entries.map((entry) {
        double value = (makeGraphBasedOnActualValue) ? double.tryParse(entry.value.actualValue) ?? 0.0 : entry.value.value;
        return FlSpot(entry.key.toDouble(), value);
      }).toList();

      data.add(
        LineChartBarData(
          spots: secondarySpots,
          isCurved: true,
          isStrokeCapRound: true,
          isStrokeJoinRound: true,
          barWidth: 2,
          gradient: LinearGradient(
            colors: [secondaryGraphColor ?? AppColors.blueColor, secondaryGraphColor ?? AppColors.blueColor],
            begin: Alignment.centerLeft,
            end: Alignment.centerRight,
          ),
          dotData: FlDotData(
            show: true,
            getDotPainter: (spot, percent, barData, idx) {
              return FlDotCirclePainter(
                radius: 6,
                color: secondaryGraphColor ?? AppColors.blueColor,
                strokeWidth: 2,
                strokeColor: Colors.white,
              );
            },
          ),
          belowBarData: BarAreaData(show: false),
        ),
      );
    }

    return data;
  }
}
