import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class CustomCupertinoDateTimePicker extends StatefulWidget {
  const CustomCupertinoDateTimePicker({
    Key? key,
    required this.itemExtent,
    required this.onSelectedItemChanged,
    this.minDate,
    this.maxDate,
    this.bahasa = 'id',
    this.selectedDate,
    this.selectedStyle,
    this.unselectedStyle,
    this.disabledStyle,
    this.backgroundColor,
    this.squeeze = 1.45,
    this.diameterRatio = 1.1,
    this.magnification = 1.0,
    this.offAxisFraction = 0.0,
    this.useMaginifier = false,
    this.selectionOverlay = const CupertinoPickerDefaultSelectionOverlay(),
  }) : super(key: key);

  final Color? backgroundColor;
  final String? bahasa;
  final double diameterRatio;
  // Text style of disabled item
  final TextStyle? disabledStyle;

  final double itemExtent;
  final double magnification;
  // Maximum selectable date
  final DateTime? maxDate;

  // Minimum selectable date
  final DateTime? minDate;

  final double offAxisFraction;
  final void Function(DateTime) onSelectedItemChanged;
  // Initially selected date
  final DateTime? selectedDate;

  // Text style of selected item
  final TextStyle? selectedStyle;

  final Widget selectionOverlay;
  final double squeeze;
  // Text style of unselected item
  final TextStyle? unselectedStyle;

  final bool useMaginifier;

  @override
  State<CustomCupertinoDateTimePicker> createState() =>
      _CustomCupertinoDateTimePickerState();
}

enum _SelectorType { day, month, year, hour, minute }

class _CustomCupertinoDateTimePickerState
    extends State<CustomCupertinoDateTimePicker> {
  late final FixedExtentScrollController _dayScrollController;
  final _days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  late DateTime _maxDate;
  late DateTime _minDate;
  late final FixedExtentScrollController _monthScrollController;
  final _months = [
    'January',
    'February',
    'March',
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December',
  ];

  final _months_id = [
    'Januari',
    'Februari',
    'Maret',
    'April',
    'Mei',
    'Juni',
    'Juli',
    'Agustus',
    'September',
    'Oktober',
    'November',
    'Desember',
  ];

  late DateTime _selectedDate;
  late int _selectedDayIndex;
  late int _selectedMonthIndex;
  late int _selectedYearIndex;
  late int _selectedHourIndex;
  late int _selectedMinuteIndex;
  late final FixedExtentScrollController _yearScrollController;
  late final FixedExtentScrollController _hourScrollController;
  late final FixedExtentScrollController _minuteScrollController;

  @override
  void dispose() {
    _dayScrollController.dispose();
    _monthScrollController.dispose();
    _yearScrollController.dispose();

    _hourScrollController.dispose();
    _minuteScrollController.dispose();
    super.dispose();
  }

  @override
  void initState() {
    super.initState();
    _validateDates();
    _dayScrollController = FixedExtentScrollController();
    _monthScrollController = FixedExtentScrollController();
    _yearScrollController = FixedExtentScrollController();
    _hourScrollController = FixedExtentScrollController();
    _minuteScrollController = FixedExtentScrollController();
    _initDates();
  }

  void _validateDates() {
    if (widget.minDate != null && widget.maxDate != null) {
      assert(!widget.minDate!.isAfter(widget.maxDate!));
    }
    if (widget.minDate != null && widget.selectedDate != null) {
      assert(!widget.minDate!.isAfter(widget.selectedDate!));
    }
    if (widget.maxDate != null && widget.selectedDate != null) {
      assert(!widget.selectedDate!.isAfter(widget.maxDate!));
    }
  }

  void _initDates() {
    final currentDate = DateTime.now();
    _minDate = widget.minDate ?? DateTime(currentDate.year - 100);
    _maxDate = widget.maxDate ?? DateTime(currentDate.year + 100);
    // if (widget.selectedDate != null) {
    //   _selectedDate = widget.selectedDate!;
    // } else if (!currentDate.isBefore(_minDate) &&
    //     !currentDate.isAfter(_maxDate)) {
    //   _selectedDate = currentDate;
    // } else {
    //   _selectedDate = _minDate;
    // }
    _selectedDate = widget.selectedDate ?? _minDate;
    if (_selectedDate.isBefore(_minDate)) {
      _selectedDate = _minDate;
    }
    if (_selectedDate.isAfter(_maxDate)) {
      _selectedDate = _maxDate;
    }

    _selectedDayIndex = _selectedDate.day - 1;
    _selectedMonthIndex = _selectedDate.month - 1;
    _selectedYearIndex = _selectedDate.year - _minDate.year;
    _selectedHourIndex = _selectedDate.hour;
    _selectedMinuteIndex = _selectedDate.minute;
    WidgetsBinding.instance.addPostFrameCallback((_) => {
          _scrollList(_dayScrollController, _selectedDayIndex),
          _scrollList(_monthScrollController, _selectedMonthIndex),
          _scrollList(_yearScrollController, _selectedYearIndex),
          _scrollList(_hourScrollController, _selectedHourIndex),
          _scrollList(_minuteScrollController, _selectedMinuteIndex),
        });
  }

  void _scrollList(FixedExtentScrollController controller, int index) {
    controller.animateToItem(
      index,
      curve: Curves.easeIn,
      duration: const Duration(milliseconds: 300),
    );
  }

  bool _isLeapYear() {
    final year = _minDate.year + _selectedYearIndex;
    return year % 4 == 0 &&
        (year % 100 != 0 || (year % 100 == 0 && year % 400 == 0));
  }

  int _numberOfDays() {
    if (_selectedMonthIndex == 1) {
      _days[1] = _isLeapYear() ? 29 : 28;
    }
    return _days[_selectedMonthIndex];
  }

void _onSelectedItemChanged(int index, _SelectorType type) {
  DateTime temp;
  switch (type) {
    case _SelectorType.day:
      temp = DateTime(
        _minDate.year + _selectedYearIndex,
        _selectedMonthIndex + 1,
        index + 1,
        _selectedHourIndex,
        _selectedMinuteIndex
      );
      break;
    case _SelectorType.month:
      temp = DateTime(
        _minDate.year + _selectedYearIndex,
        index + 1,
        _selectedDayIndex + 1,
        _selectedHourIndex,
        _selectedMinuteIndex
      );
      break;
    case _SelectorType.year:
      temp = DateTime(
        _minDate.year + index,
        _selectedMonthIndex + 1,
        _selectedDayIndex + 1,
        _selectedHourIndex,
        _selectedMinuteIndex
      );
      break;
    case _SelectorType.hour:
      temp = DateTime(
        _minDate.year + _selectedYearIndex,
        _selectedMonthIndex + 1,
        _selectedDayIndex + 1,
        index,
        _selectedMinuteIndex,
      );
      break;
    case _SelectorType.minute:
      temp = DateTime(
        _minDate.year + _selectedYearIndex,
        _selectedMonthIndex + 1,
        _selectedDayIndex + 1,
        _selectedHourIndex,
        index,
      );
      break;
  }
  
  if (temp.isBefore(_minDate) || temp.isAfter(_maxDate)) {
    switch (type) {
      case _SelectorType.day:
        _dayScrollController.jumpToItem(_selectedDayIndex);
        break;
      case _SelectorType.month:
        _monthScrollController.jumpToItem(_selectedMonthIndex);
        break;
      case _SelectorType.year:
        _yearScrollController.jumpToItem(_selectedYearIndex);
        break;
      case _SelectorType.hour:
        _hourScrollController.jumpToItem(_selectedHourIndex);
        break;
      case _SelectorType.minute:
        _minuteScrollController.jumpToItem(_selectedMinuteIndex);
        break;
    }
    return;
  }
  
  // Update selected date
  _selectedDate = temp;
  switch (type) {
    case _SelectorType.day:
      _selectedDayIndex = index;
      break;
    case _SelectorType.month:
      _selectedMonthIndex = index;
      if (_selectedMonthIndex == 1 && _selectedDayIndex > 27) {
        _selectedDayIndex = _isLeapYear() ? 28 : 27;
      }
      if (_selectedDayIndex == 30 && _days[_selectedMonthIndex] == 30) {
        _selectedDayIndex = 29;
      }
      break;
    case _SelectorType.year:
      _selectedYearIndex = index;
      if (!_isLeapYear() && _selectedMonthIndex == 1 && _selectedDayIndex == 28) {
        _selectedDayIndex = 27;
      }
      break;
    case _SelectorType.hour:
      _selectedHourIndex = index;
      break;
    case _SelectorType.minute:
      _selectedMinuteIndex = index;
      break;
  }
  
  // print(temp);
  // print(_selectedDate);
  setState(() {});
  widget.onSelectedItemChanged(_selectedDate);
}

  bool _isDisabled(int index, _SelectorType type) {
    DateTime temp;
    switch (type) {
      case _SelectorType.day:
        temp = DateTime(
          _minDate.year + _selectedYearIndex,
          _selectedMonthIndex + 1,
          index + 1,
          _selectedHourIndex,
          _selectedMinuteIndex,
        );
        break;
      case _SelectorType.month:
        temp = DateTime(
          _minDate.year + _selectedYearIndex,
          index + 1,
          _selectedDayIndex + 1,
          _selectedHourIndex,
          _selectedMinuteIndex,
        );
        break;
      case _SelectorType.year:
        temp = DateTime(
          _minDate.year + index,
          _selectedMonthIndex + 1,
          _selectedDayIndex + 1,
          _selectedHourIndex,
          _selectedMinuteIndex,
        );
        break;
      case _SelectorType.hour:
        temp = DateTime(
          _minDate.year + _selectedYearIndex,
          _selectedMonthIndex + 1,
          _selectedDayIndex + 1,
          index,
          _selectedMinuteIndex,
        );
        break;
      case _SelectorType.minute:
        temp = DateTime(
          _minDate.year + _selectedYearIndex,
          _selectedMonthIndex + 1,
          _selectedDayIndex + 1,
          _selectedHourIndex,
          index,
        );
    }
    return temp.isAfter(_maxDate) || temp.isBefore(_minDate);
  }

  Widget _selector({
    required List<dynamic> values,
    required int selectedValueIndex,
    required bool Function(int) isDisabled,
    required void Function(int) onSelectedItemChanged,
    required FixedExtentScrollController scrollController,
  }) {
    return CupertinoPicker.builder(
      childCount: values.length,
      squeeze: widget.squeeze,
      itemExtent: widget.itemExtent,
      scrollController: scrollController,
      useMagnifier: widget.useMaginifier,
      diameterRatio: widget.diameterRatio,
      magnification: widget.magnification,
      backgroundColor: widget.backgroundColor,
      offAxisFraction: widget.offAxisFraction,
      selectionOverlay: widget.selectionOverlay,
      onSelectedItemChanged: onSelectedItemChanged,
      itemBuilder: (context, index) => Container(
        height: widget.itemExtent,
        alignment: Alignment.center,
        child: Text(
          '${values[index]}',
          style: index == selectedValueIndex
              ? widget.selectedStyle
              : isDisabled(index)
                  ? widget.disabledStyle
                  : widget.unselectedStyle,
        ),
      ),
    );
  }

  Widget _daySelector() {
    return _selector(
      values: List.generate(_numberOfDays(), (index) => index + 1),
      selectedValueIndex: _selectedDayIndex,
      scrollController: _dayScrollController,
      isDisabled: (index) => _isDisabled(index, _SelectorType.day),
      onSelectedItemChanged: (v) => _onSelectedItemChanged(
        v,
        _SelectorType.day,
      ),
    );
  }

  Widget _monthSelector() {
    return _selector(
      values: widget.bahasa == 'id' ? _months_id : _months,
      selectedValueIndex: _selectedMonthIndex,
      scrollController: _monthScrollController,
      isDisabled: (index) => _isDisabled(index, _SelectorType.month),
      onSelectedItemChanged: (v) => _onSelectedItemChanged(
        v,
        _SelectorType.month,
      ),
    );
  }

  Widget _yearSelector() {
    return _selector(
      values: List.generate(
        _maxDate.year - _minDate.year + 1,
        (index) => _minDate.year + index,
      ),
      selectedValueIndex: _selectedYearIndex,
      scrollController: _yearScrollController,
      isDisabled: (index) => _isDisabled(index, _SelectorType.year),
      onSelectedItemChanged: (v) => _onSelectedItemChanged(
        v,
        _SelectorType.year,
      ),
    );
  }

  Widget _hourSelector() {
    return _selector(
      values: List.generate(24, (index) => index),
      selectedValueIndex: _selectedHourIndex,
      scrollController: _hourScrollController,
      isDisabled: (index) => _isDisabled(index, _SelectorType.hour),
      onSelectedItemChanged: (v) => _onSelectedItemChanged(
        v,
        _SelectorType.hour,
      ),
    );
  }

  Widget _minuteSelector() {
    return _selector(
      values: List.generate(60, (index) => index),
      selectedValueIndex: _selectedMinuteIndex,
      scrollController: _minuteScrollController,
      isDisabled: (index) => _isDisabled(index, _SelectorType.minute),
      onSelectedItemChanged: (v) => _onSelectedItemChanged(
        v,
        _SelectorType.minute,
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Expanded(flex: 2, child: _daySelector()),
        Expanded(flex: 4, child: _monthSelector()),
        Expanded(flex: 2, child: _yearSelector()),
        Expanded(flex: 2, child: _hourSelector()),
        Expanded(
            flex: 1,
            child: Center(
                child: Text(
              ':',
              style: TextStyle(
                  fontWeight: FontWeight.bold,
                  fontSize: 30,
                  color: Colors.blueAccent),
            ))),
        Expanded(flex: 2, child: _minuteSelector()),
      ],
    );
  }
}
