import 'package:flutter/material.dart';
import 'dart:async';
import 'package:qpay/http/http.dart';
import 'package:qpay/models/api_response.dart';
import 'package:qpay/screens/transaction_controller.dart';
import 'package:qpay/widgets/app_snack_bar.dart';
import 'package:provider/provider.dart';

class GatewayModel {
  bool isLoading = false;
  String status = '';
  String? errorMessage;
  bool isCancelled = false;
  int count = 0;
}

class GatewayController extends ChangeNotifier {
  final GatewayModel model = GatewayModel();
  final Http http = Http();
  Timer? _pollTimer; // Add timer field
  BuildContext context;

  late TransactionController transactionController;

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

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

  void startLoading() {
    model.isLoading = true;
    model.status = '';
    notifyListeners();
  }

  void finishLoading() {
    model.isLoading = false;
    notifyListeners();
  }

  Future<ApiResponse<Map<String, dynamic>>> poll(String uid) async {
    startLoading();
    try {
      dynamic workflowResponse = await http.get(
        '/public/transaction/poll/$uid',
      );

      logger.i(workflowResponse.toString());

      dynamic response = workflowResponse['body'];

      model.status = response['pollingStatus'];
      transactionController.model.paymentStatus = response['paymentStatus'];

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

  Future<ApiResponse<Map<String, dynamic>>> pollTransaction(String uid) async {
    // poll up to 15 times (~45 seconds at 3-second intervals)
    int maxAttempts = 15;
    int attempt = 0;

    while (!model.isCancelled && attempt < maxAttempts) {
      attempt++;

      ApiResponse<Map<String, dynamic>> result = await poll(uid);

      // Only stop on success or failure
      if (result.isSuccess) {
        String status = model.status;
        // If status is SUCCESS, we're done
        if (status == 'SUCCESS') {
          return result;
        }

        await Future.delayed(const Duration(seconds: 3));
        if (model.isCancelled) break;

        // Otherwise (e.g. PENDING), continue polling
      } else {
        await Future.delayed(const Duration(seconds: 3));
        if (model.isCancelled) break;

        // Network error — show failure UI and stop
        model.isCancelled = true;
        model.status = 'FAILED';
        model.errorMessage = result.error;
        return result;
      }
    }

    // Timed out after max attempts
    model.isCancelled = true;
    model.status = 'FAILED';
    model.errorMessage = 'We failed to check your transaction status';
    finishLoading();
    notifyListeners();
    return ApiResponse.failure("Polling timed out");
  }

  // Alternative method using Timer instead of while loop
  Future<ApiResponse<Map<String, dynamic>>> pollTransactionWithTimer(
    String uid,
  ) async {
    _pollTimer = Timer.periodic(const Duration(seconds: 5), (timer) async {
      if (model.isCancelled) {
        timer.cancel();
        return;
      }

      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);
          notifyListeners();
          timer.cancel(); // Stop polling on success
        }
      } catch (e) {
        logger.e(e);
        _showErrorSnackBar(
          "Network error. Please try again or contact support",
        );
      }
    });
    // This returns immediately; the caller should use the timer callback approach.
    // Consider refactoring this to use poll(String uid) directly for a consistent API.
    return ApiResponse.failure(
      "Polling via timer started; use a different flow for the result",
    );
  }

  @override
  void dispose() {
    model.isCancelled = true;
    _pollTimer?.cancel(); // Cancel timer if it exists
    super.dispose();
  }
}
