import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:provider/provider.dart';
import 'package:qpay/models/api_response.dart';
import 'package:qpay/screens/transaction_controller.dart';
import 'package:qpay/widgets/app_snack_bar.dart';

import '../../http/http.dart';

part 'confirm_controller.freezed.dart';
part 'confirm_controller.g.dart';

class ConfirmModel {
  bool isLoading = false;
  String status = '';
  String? errorMessage;
  bool isCancelled = false;
}

@freezed
abstract class ConfirmData with _$ConfirmData {
  const factory ConfirmData({
    required String status,
    required String errorMessage,
  }) = _ConfirmData;

  factory ConfirmData.fromJson(Map<String, dynamic> json) =>
      _$ConfirmDataFromJson(json);
}

class ConfirmController extends ChangeNotifier {
  final ConfirmModel model = ConfirmModel();
  late TransactionController transactionController;
  final Http http = Http();
  BuildContext context;

  ConfirmController(this.context) {
    transactionController = Provider.of<TransactionController>(
      context,
      listen: false,
    );
  }

  void _showErrorSnackBar(String? message) {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      AppSnackBar.showError(context, message.toString());
    });
  }

  Future<ApiResponse<Map<String, dynamic>>> doTransaction() async {
    try {
      model.isLoading = true;
      notifyListeners();

      // todo: find out if we still need to update these fields
      transactionController.model.formData = transactionController
          .model
          .formData
          .copyWith(
            type: "REQUEST",
            trace: transactionController.model.confirmationData['trace'],
            id: transactionController.model.confirmationData['id'],
            authType:
                transactionController.model.selectedPaymentProcessor.authType,
          );

      dynamic workflowResponse = await http.get(
        '/public/transaction/request/'
        '${transactionController.model.confirmationData['id']}/'
        '${transactionController.model.selectedPaymentProcessor.authType}',
      );
      logger.i(workflowResponse.toString());

      dynamic response = workflowResponse['body'];

      model.isLoading = false;
      notifyListeners();

      if (response['status'] != 'FAILED') {
        model.status = response['status'];
        transactionController.updateReceiptData(response);
        return ApiResponse.success(Map<String, dynamic>.from(response));
      } else {
        model.status = response['status'];
        model.errorMessage = response['errorMessage'];
        return ApiResponse.failure(
          response['errorMessage'] ?? "Transaction failed",
        );
      }
    } catch (e) {
      logger.e(e);
      model.errorMessage = e.toString();
      model.isLoading = false;
      notifyListeners();
      return ApiResponse.failure(
        "Problem completing the transaction, please try again.",
      );
    }
  }

  Future<ApiResponse<Map<String, dynamic>>> pollTransaction(String uid) async {
    while (!model.isCancelled) {
      // Check again after delay in case it was cancelled during the delay
      if (model.isCancelled) break;

      try {
        dynamic response = await http.get('/public/transaction/poll/$uid');
        logger.i(response.toString());

        if (response['status'] == 'SUCCESS') {
          model.status = response['status'];
          transactionController.updateReceiptData(response);
          model.isCancelled = true;
          notifyListeners();
          return ApiResponse.success(Map<String, dynamic>.from(response));
        }
      } catch (e) {
        logger.e(e);
        _showErrorSnackBar(
          "Network error. Please try again or contact support",
        );
      }

      // Check cancellation flag instead of true
      // Wait 5 seconds before the next poll
      await Future.delayed(const Duration(seconds: 5));
    }
    return ApiResponse.failure("Polling cancelled");
  }

  Future<ApiResponse<Map<String, dynamic>>> updateVelocityTransactionOtp(
    String id,
    Map<String, dynamic> transaction,
  ) async {
    try {
      model.isLoading = true;
      notifyListeners();

      dynamic response = await http.put(
        '/public/transaction/$id/velocity-otp',
        transaction,
      );

      model.isLoading = false;
      notifyListeners();

      if (response['status'] == 'SUCCESS') {
        return ApiResponse.success(Map<String, dynamic>.from(response));
      } else {
        model.errorMessage =
            response['errorMessage'] ??
            "Problem updating transaction. Please try again or contact support";
        return ApiResponse.failure(model.errorMessage!);
      }
    } on DioException catch (e, s) {
      if (kDebugMode) {
        logger.e(e);
        logger.e(s);
      }
      model.isLoading = false;
      notifyListeners();
      return ApiResponse.failure(
        "Network error. Please try again or contact support",
      );
    } catch (e, s) {
      if (kDebugMode) {
        logger.e(e);
        logger.e(s);
      }
      model.isLoading = false;
      notifyListeners();
      return ApiResponse.failure(
        "Problem updating that transaction. Please try again or contact support",
      );
    }
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
  }
}
