import 'dart:async';
import 'dart:io';

import 'package:clarity_flutter/clarity_flutter.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get_it/get_it.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart';
import 'package:provider/provider.dart';
import 'package:sms_otp_auto_verify/sms_otp_auto_verify.dart';

import 'dart:ui' as ui;

typedef OnDone = void Function(String text);

class ProvidedPinBoxTextAnimation {
  static AnimatedSwitcherTransitionBuilder scalingTransition = (child, animation) {
    return ScaleTransition(
      scale: animation,
      child: child,
    );
  };

  static AnimatedSwitcherTransitionBuilder defaultNoTransition = (Widget child, Animation<double> animation) {
    return child;
  };
}

class OTPWidget extends StatefulWidget {
  final int maxLength;
  final TextEditingController? controller;

  final Color defaultBorderColor;
  final Color pinBoxColor;
  final double pinBoxBorderWidth;
  final double pinBoxRadius;
  final bool hideDefaultKeyboard;

  final TextStyle? pinTextStyle;
  final double pinBoxHeight;
  final double pinBoxWidth;
  final OnDone? onDone;
  final bool hasError;
  final Color errorBorderColor;
  final Color textBorderColor;
  final Function(String)? onTextChanged;
  final bool autoFocus;
  final FocusNode? focusNode;
  final AnimatedSwitcherTransitionBuilder? pinTextAnimatedSwitcherTransition;
  final Duration pinTextAnimatedSwitcherDuration;

  // final TextDirection textDirection;
  final TextInputType keyboardType;
  final EdgeInsets pinBoxOuterPadding;

  const OTPWidget({
    super.key,
    this.maxLength = 4,
    this.controller,
    this.pinBoxWidth = 70.0,
    this.pinBoxHeight = 100.0,
    this.pinTextStyle,
    this.onDone,
    this.defaultBorderColor = Colors.black,
    this.textBorderColor = Colors.black,
    this.pinTextAnimatedSwitcherTransition,
    this.pinTextAnimatedSwitcherDuration = const Duration(),
    this.hasError = false,
    this.errorBorderColor = Colors.red,
    this.onTextChanged,
    this.autoFocus = false,
    this.focusNode,
    this.keyboardType = TextInputType.number,
    this.pinBoxOuterPadding = const EdgeInsets.symmetric(horizontal: 4.0),
    this.pinBoxColor = Colors.white,
    this.pinBoxBorderWidth = 2.0,
    this.pinBoxRadius = 0,
    this.hideDefaultKeyboard = false,
  });

  @override
  State<StatefulWidget> createState() {
    return OTPWidgetState();
  }
}

class OTPWidgetState extends State<OTPWidget> with SingleTickerProviderStateMixin {
  late AnimationController _highlightAnimationController;
  late FocusNode focusNode;
  String text = "";
  int currentIndex = 0;
  List<String> strList = [];
  bool hasFocus = false;
  AuthenticationViewModel? authVm;

  final CacheService cacheService = GetIt.instance<CacheService>();

  @override
  void didUpdateWidget(OTPWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    focusNode = widget.focusNode ?? focusNode;

    if (oldWidget.maxLength < widget.maxLength) {
      setState(() {
        currentIndex = text.length;
      });
      widget.controller?.text = text;
      widget.controller?.selection = TextSelection.collapsed(offset: text.length);
    } else if (oldWidget.maxLength > widget.maxLength && widget.maxLength > 0 && text.isNotEmpty && text.length > widget.maxLength) {
      setState(() {
        text = text.substring(0, widget.maxLength);
        currentIndex = text.length;
      });
      widget.controller?.text = text;
      widget.controller?.selection = TextSelection.collapsed(offset: text.length);
    }
  }

  _calculateStrList() {
    if (strList.length > widget.maxLength) {
      strList.length = widget.maxLength;
    }
    while (strList.length < widget.maxLength) {
      strList.add("");
    }
  }

  @override
  void initState() {
    Clarity.setCurrentScreenName('OTP Verification Screen');
    super.initState();
    authVm = context.read<AuthenticationViewModel>();
    focusNode = widget.focusNode ?? FocusNode();
    _highlightAnimationController = AnimationController(vsync: this);
    _initTextController();
    _calculateStrList();
    if (widget.controller != null) {
      widget.controller!.addListener(_controllerListener);
    }
    focusNode.addListener(_focusListener);
    authVm?.otpScreenNotifier.addListener(_onOtpScreenNotifierChanged);
    cacheService.remove(key: CacheConst.quickLoginEnabled);
  }

  void _controllerListener() {
    if (mounted == true) {
      setState(() {
        _initTextController();
        text = widget.controller?.text ?? "";
        currentIndex = text.length;
      });
      var onTextChanged = widget.onTextChanged;
      if (onTextChanged != null) {
        onTextChanged(widget.controller?.text ?? "");
      }
    }
  }

  void _focusListener() {
    if (mounted == true) {
      setState(() {
        hasFocus = focusNode.hasFocus;
      });
    }
  }

  void onWrongOtpClear() {
    if (mounted) {
      setState(() {
        text = "";
        currentIndex = 0;
        strList.clear();
        _calculateStrList();
      });

      // Clear the controller if it exists
      if (widget.controller != null) {
        widget.controller!.clear();
      }

      if (focusNode.hasFocus) {
        focusNode.unfocus();
      }
    }
  }

  void _onOtpScreenNotifierChanged() {
    if (authVm?.otpScreenNotifier.value == true) {
      onWrongOtpClear();
      authVm?.otpScreenNotifier.value = false;
    }
  }

  void _initTextController() {
    if (widget.controller == null) {
      return;
    }
    strList.clear();
    var text = widget.controller?.text ?? "";
    if (text.isNotEmpty) {
      if (text.length > widget.maxLength) {
        throw Exception("TextEditingController length exceeded maxLength!");
      }
    }
    for (var i = 0; i < text.length; i++) {
      strList.add(text[i]);
    }
  }

  double get _width {
    var width = 0.0;
    for (var i = 0; i < widget.maxLength; i++) {
      width += widget.pinBoxWidth;
      if (i == 0) {
        width += widget.pinBoxOuterPadding.left;
      } else if (i + 1 == widget.maxLength) {
        width += widget.pinBoxOuterPadding.right;
      } else {
        width += widget.pinBoxOuterPadding.left;
      }
    }
    return width;
  }

  @override
  void dispose() {
    if (widget.focusNode == null) {
      focusNode.dispose();
    } else {
      focusNode.removeListener(_focusListener);
    }
    _highlightAnimationController.dispose();
    if (widget.controller != null) {
      widget.controller!.removeListener(_controllerListener);
    }

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        _otpTextInput(),
        _touchPinBoxRow(),
      ],
    );
  }

  Widget _touchPinBoxRow() {
    return widget.hideDefaultKeyboard
        ? _pinBoxRow(context)
        : GestureDetector(
            behavior: HitTestBehavior.opaque,
            onTap: () {
              if (hasFocus) {
                FocusScope.of(context).requestFocus(FocusNode());
                Future.delayed(Duration(milliseconds: 100), () {
                  FocusScope.of(context).requestFocus(focusNode);
                });
              } else {
                FocusScope.of(context).requestFocus(focusNode);
              }
            },
            child: _pinBoxRow(context),
          );
  }

  Widget _otpTextInput() {
    var transparentBorder = OutlineInputBorder(
      borderSide: BorderSide(
        color: Colors.transparent,
        width: 0.0,
      ),
    );
    return SizedBox(
      width: _width,
      height: widget.pinBoxHeight,
      child: TextField(
        autofocus: !kIsWeb ? widget.autoFocus : false,
        enableInteractiveSelection: false,
        focusNode: focusNode,
        controller: widget.controller,
        keyboardType: widget.keyboardType,
        inputFormatters: widget.keyboardType == TextInputType.number ? <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly] : null,
        style: TextStyle(height: 0.1, color: Colors.transparent),
        decoration: InputDecoration(
          contentPadding: EdgeInsets.all(0),
          focusedErrorBorder: transparentBorder,
          errorBorder: transparentBorder,
          disabledBorder: transparentBorder,
          enabledBorder: transparentBorder,
          focusedBorder: transparentBorder,
          counterText: null,
          counterStyle: null,
          helperStyle: TextStyle(height: 0.0, color: Colors.transparent),
          labelStyle: TextStyle(height: 0.1),
          fillColor: Colors.transparent,
          border: InputBorder.none,
        ),
        cursorColor: Colors.transparent,
        showCursor: false,
        maxLength: widget.maxLength,
        onChanged: _onTextChanged,
      ),
    );
  }

  void _onTextChanged(text) {
    var onTextChanged = widget.onTextChanged;
    if (onTextChanged != null) {
      onTextChanged(text);
    }
    setState(() {
      this.text = text;
      if (text.length >= currentIndex) {
        for (int i = currentIndex; i < text.length; i++) {
          strList[i] = text[i];
        }
      }
      currentIndex = text.length;
    });
    if (text.length == widget.maxLength) {
      FocusScope.of(context).requestFocus(FocusNode());
      var onDone = widget.onDone;
      if (onDone != null) {
        onDone(text);
      }
    }
  }

  Widget _pinBoxRow(BuildContext context) {
    _calculateStrList();
    List<Widget> pinCodes = List.generate(widget.maxLength, (int i) {
      return _buildPinCode(i, context);
    });
    return Row(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: pinCodes,
    );
  }

  Widget _buildPinCode(int i, BuildContext context) {
    Color pinBoxColor;

    if (widget.hasError) {
      pinBoxColor = widget.errorBorderColor;
    } else if (text.length == widget.maxLength) {
      pinBoxColor = AppColors.successColor;
    } else if (i < text.length) {
      pinBoxColor = AppColors.blackBgColor;
    } else {
      pinBoxColor = widget.pinBoxColor;
    }

    EdgeInsets insets;
    if (i == 0) {
      insets = EdgeInsets.only(
        left: 0,
        top: widget.pinBoxOuterPadding.top,
        right: widget.pinBoxOuterPadding.right,
        bottom: widget.pinBoxOuterPadding.bottom,
      );
    } else if (i == strList.length - 1) {
      insets = EdgeInsets.only(
        left: widget.pinBoxOuterPadding.left,
        top: widget.pinBoxOuterPadding.top,
        right: 0,
        bottom: widget.pinBoxOuterPadding.bottom,
      );
    } else {
      insets = widget.pinBoxOuterPadding;
    }

    return AnimatedContainer(
      duration: const Duration(milliseconds: 200),
      curve: Curves.easeInOut,
      key: ValueKey<String>("container$i"),
      alignment: Alignment.center,
      padding: EdgeInsets.symmetric(vertical: 4.0, horizontal: 1.0),
      margin: insets,
      decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
        color: pinBoxColor,
        borderRadius: widget.pinBoxRadius,
      ),
      width: widget.pinBoxWidth,
      height: widget.pinBoxHeight,
      child: _animatedTextBox(strList[i], i),
    );
  }

  Widget _animatedTextBox(String text, int i) {
    if (widget.pinTextAnimatedSwitcherTransition != null) {
      return AnimatedSwitcher(
        duration: widget.pinTextAnimatedSwitcherDuration,
        transitionBuilder: widget.pinTextAnimatedSwitcherTransition ??
            (Widget child, Animation<double> animation) {
              return child;
            },
        child: Text(
          text,
          key: ValueKey<String>("$text$i"),
          style: widget.pinTextStyle,
        ),
      );
    } else {
      return Text(
        text,
        key: ValueKey<String>("${strList[i]}$i"),
        style: widget.pinTextStyle,
      );
    }
  }
}

class OTPVerificationScreen extends StatefulWidget {
  final String phoneNumber;
  final String zipCode;
  final Function(int code) checkActivationCode;
  final Function(String phoneNumber, String zipCode) onResendOTPPressed;
  final bool isFormFamilyFile;

  const OTPVerificationScreen({super.key, required this.phoneNumber, required this.zipCode, required this.checkActivationCode, required this.onResendOTPPressed, required this.isFormFamilyFile});

  @override
  State<OTPVerificationScreen> createState() => _OTPVerificationScreenState();
}

class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
  final int _otpLength = 4;
  late final TextEditingController _otpController;

  Timer? _resendTimer;
  int _resendTime = 120;
  bool _isOtpComplete = false;
  bool _isVerifying = false;

  @override
  void initState() {
    Clarity.setCurrentScreenName('OTP Verification Screen');
    super.initState();
    _otpController = TextEditingController();
    _startResendTimer();
    if (Platform.isAndroid) {
      checkSignature();
    }
  }

  @override
  void dispose() {
    _otpController.dispose();
    _resendTimer?.cancel();
    super.dispose();
  }

  void _startResendTimer() {
    _resendTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
      if (_resendTime > 0) {
        setState(() => _resendTime--);
      } else {
        timer.cancel();
      }
    });
  }

  void _onOtpChanged(String value) {
    setState(() {
      _isOtpComplete = value.length == _otpLength;
    });

    if (_isOtpComplete && !_isVerifying) {
      _isVerifying = true;
      _verifyOtp(value);
    } else if (!_isOtpComplete) {
      // Reset the flag when OTP is incomplete (user is editing)
      _isVerifying = false;
    }
  }

  void checkSignature() async {
    SmsVerification.startListeningSms().then((message) {
      final intRegex = RegExp(r'\d+', multiLine: true);
      var otp = SmsVerification.getCode(message, intRegex);
      if (otp != null && otp.length == _otpLength) {
        autoFillOtp(otp); // Use autoFillOtp to update controller and UI
      }
      SmsVerification.stopListening();
    });
  }

  void _onAutoOtpChanged(String value) {
    setState(() {
      _isOtpComplete = value.length == _otpLength;
    });

    if (_isOtpComplete && !_isVerifying) {
      _isVerifying = true;
      _verifyOtp(value);
    } else if (!_isOtpComplete) {
      _isVerifying = false;
    }
  }

  void _resendOtp() {
    if (_resendTime == 0) {
      setState(() {
        _resendTime = 120;
        _isVerifying = false;
        _isOtpComplete = false;
      });
      _otpController.clear();
      _startResendTimer();
      widget.onResendOTPPressed(widget.phoneNumber, widget.zipCode);
    }
  }

  String _getMaskedPhoneNumber() {
    final phone = widget.phoneNumber;
    return phone.length > 4 ? '05xxxxxx${phone.substring(phone.length - 2)}' : phone;
  }

  @override
  Widget build(BuildContext context) {
    AuthenticationViewModel authVM = context.read<AuthenticationViewModel>();
    AppState appState = getIt<AppState>();
    return Scaffold(
      backgroundColor: AppColors.scaffoldBgColor,
      appBar: CustomAppBar(
        hideLogoAndLang: true,
        onBackPressed: () {
          Navigator.of(context).pop();
        },
        onLanguageChanged: (lang) {},
      ),
      body: SingleChildScrollView(
        child: Padding(
          padding: EdgeInsets.symmetric(horizontal: 24.h),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              SizedBox(height: 10.h),
              LocaleKeys.otpVerification.tr(context: context).toText24(isBold: true),
              SizedBox(height: 20.h),
              Wrap(
                spacing: 2.h,
                runSpacing: 2.h,
                children: [
                  LocaleKeys.weHaveSendOTP.tr(context: context).toText15(color: AppColors.inputLabelTextColor, letterSpacing: -0.4),
                  _getMaskedPhoneNumber().toText15(color: AppColors.inputLabelTextColor, isBold: true),
                  // LocaleKeys.via.tr(context: context).toText15(color: AppColors.inputLabelTextColor, letterSpacing: -0.4),
                  // (widget.isFormFamilyFile ? LoginTypeEnum.sms.displayName : authVM.loginTypeEnum.displayName)
                  //     .toText15(color: AppColors.inputLabelTextColor, isBold: true, letterSpacing: -0.4),
                  // appState.getUserRegistrationPayload.isRegister != null && appState.getUserRegistrationPayload.isRegister == true
                  //     ? LocaleKeys.forRegistrationVerification.tr(context: context).toText15(color: AppColors.inputLabelTextColor, letterSpacing: -0.4)
                  //     : LocaleKeys.forLoginVerification.tr(context: context).toText15(color: AppColors.inputLabelTextColor, letterSpacing: -0.4),
                ],
              ),

              SizedBox(height: 16.h),
              Center(
                child: Directionality(
                  textDirection: ui.TextDirection.ltr,
                  child: OTPWidget(
                    maxLength: _otpLength,
                    controller: _otpController,
                    pinBoxWidth: 70.h,
                    pinBoxHeight: 100.h,
                    pinBoxRadius: 16,
                    pinBoxBorderWidth: 0,
                    pinBoxOuterPadding: EdgeInsets.symmetric(horizontal: 4.h),
                    defaultBorderColor: Colors.transparent,
                    textBorderColor: Colors.transparent,
                    pinBoxColor: AppColors.whiteColor,
                    autoFocus: true,
                    onTextChanged: _onOtpChanged,
                    pinTextStyle: TextStyle(fontSize: 40.f, fontWeight: FontWeight.bold, color: AppColors.whiteColor, fontFamily: "Poppins"),
                  ),
                ),
              ),
              SizedBox(height: 32.h),

              // Resend OTP
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  LocaleKeys.didntReceiveIt.tr(context: context).toText16(color: AppColors.inputLabelTextColor),
                  SizedBox(width: 5.h),
                  if (_resendTime > 0)
                    Builder(
                      builder: (context) {
                        final minutes = (_resendTime ~/ 60).toString().padLeft(2, '0');
                        final seconds = (_resendTime % 60).toString().padLeft(2, '0');
                        return Row(
                          children: [
                            LocaleKeys.resendIn.tr(context: context).toText16(color: AppColors.inputLabelTextColor),
                            SizedBox(width: 2.h),
                            ' ($minutes:$seconds). '.toText16(color: AppColors.inputLabelTextColor, isEnglishOnly: true)
                          ],
                        );
                      },
                    )
                  else
                    GestureDetector(
                      onTap: _resendOtp,
                      child: LocaleKeys.resendOTP.tr(context: context).toText16(color: AppColors.primaryRedColor),
                    ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }

  void _verifyOtp(String otp) {
    widget.checkActivationCode(int.parse(otp));
  }

  /// Auto fill OTP into text fields
  void autoFillOtp(String otp) {
    if (!mounted) return;
    if (otp.length != _otpLength) return;
    _isVerifying = false;
    _otpController.text = otp;
    setState(() {});
    _onOtpChanged(otp);
  }
}
