import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';

/// Shows a dialog for transaction success or failure. [onDone] is called when
/// the user taps Done (after the dialog is closed); typically used to go to home.
void showTransactionStatusDialog(
  BuildContext context, {
  required bool success,
  required VoidCallback onDone,
}) {
  showDialog(
    context: context,
    barrierDismissible: false,
    builder: (ctx) => _TransactionStatusDialog(
      success: success,
      onDone: () {
        Navigator.of(ctx).pop();
        onDone();
      },
    ),
  );
}

class _TransactionStatusDialog extends StatelessWidget {
  const _TransactionStatusDialog({
    required this.success,
    required this.onDone,
  });

  final bool success;
  final VoidCallback onDone;

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      backgroundColor: const Color(0xFF21252D),
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
      title: Row(
        children: [
          Icon(
            success ? Icons.check_circle_rounded : Icons.error_rounded,
            color: success ? const Color(0xFF34C759) : const Color(0xFFFF4545),
            size: 28,
          ),
          const SizedBox(width: 12),
          Text(
            success ? 'Transaction successful' : 'Transaction failed',
            style: const TextStyle(
              color: Color(0xFFF2F2F2),
              fontSize: 18,
              fontWeight: FontWeight.w700,
            ),
          ),
        ],
      ),
      content: Text(
        success
            ? 'Your order has been placed. You can track it from the Orders screen.'
            : 'Something went wrong. Please try again or contact support.',
        style: const TextStyle(
          color: Color(0xFF8A8F99),
          fontSize: 14,
          height: 1.4,
        ),
      ),
      actions: [
        TextButton(
          onPressed: onDone,
          child: const Text('DONE', style: TextStyle(fontWeight: FontWeight.w700, letterSpacing: 1.5)),
        ),
      ],
    );
  }
}

void showPaymentBottomSheet(
  BuildContext context, {
  required double amount,
  required Dio dio,
  required String callbackBaseUrl,
  required VoidCallback onSuccess,
}) {
  showModalBottomSheet(
    context: context,
    isScrollControlled: true,
    builder: (ctx) => Padding(
      padding: EdgeInsets.only(bottom: MediaQuery.of(ctx).viewInsets.bottom),
      child: SafeArea(
        child: SingleChildScrollView(
          child: _PaymentSheetContent(
            amount: amount,
            dio: dio,
            callbackBaseUrl: callbackBaseUrl,
            onSuccess: onSuccess,
          ),
        ),
      ),
    ),
  );
}

class _PaymentSheetContent extends StatefulWidget {
  const _PaymentSheetContent({
    required this.amount,
    required this.dio,
    required this.callbackBaseUrl,
    required this.onSuccess,
  });

  final double amount;
  final Dio dio;
  final String callbackBaseUrl;
  final VoidCallback onSuccess;

  @override
  State<_PaymentSheetContent> createState() => _PaymentSheetContentState();
}

class _PaymentSheetContentState extends State<_PaymentSheetContent> {
  bool _showMobileMoneyForm = false;
  bool _showMobileMoneyWaiting = false;
  bool _showFinFloWaiting = false;
  final _phoneController = TextEditingController();
  final _phoneFocus = FocusNode();
  bool _loading = false;
  String? _error;

  @override
  void dispose() {
    _phoneController.dispose();
    _phoneFocus.dispose();
    super.dispose();
  }

  void _closeAndSuccess() {
    Navigator.pop(context);
    widget.onSuccess();
  }

  static bool _isValidUgandaPhone(String digits) {
    return digits.length == 9 && digits.startsWith('7');
  }

  Future<void> _requestMobileMoney() async {
    final digits = _phoneController.text.replaceAll(RegExp(r'\D'), '');
    if (!_isValidUgandaPhone(digits)) {
      setState(() {
        _error = 'Enter a valid phone (9 digits starting with 7)';
      });
      return;
    }
    final msisdn = '+256$digits';

    setState(() {
      _error = null;
      _loading = true;
    });

    try {
      final response = await widget.dio.post(
        '/api/payments/mobile-money/request',
        data: {
          'msisdn': msisdn,
          'amount': widget.amount,
        },
      );

      final data = response.data;
      if (data is! Map) {
        setState(() {
          _loading = false;
          _error = 'Invalid response';
        });
        return;
      }

      final success = data['success'] == true;
      final reference = data['reference']?.toString();

      if (!success || reference == null || reference.isEmpty) {
        setState(() {
          _loading = false;
          _error = data['message']?.toString() ?? 'Payment request failed';
        });
        return;
      }

      if (!mounted) return;
      setState(() {
        _loading = false;
        _showMobileMoneyWaiting = true;
      });
      await _pollStatus(reference);
    } on DioException catch (e) {
      if (!mounted) return;
      setState(() {
        _loading = false;
        _error = e.response?.data is Map
            ? (e.response?.data as Map)['message']?.toString()
            : null;
        _error ??= 'Payment request failed';
      });
    } catch (e) {
      if (!mounted) return;
      setState(() {
        _loading = false;
        _error = 'Payment request failed';
      });
    }
  }

  static const _pollInterval = Duration(seconds: 2);
  static const _maxPollTries = 30;

  Future<void> _pollStatus(String reference) async {
    int tries = 0;
    while (tries < _maxPollTries && mounted) {
      await Future.delayed(_pollInterval);
      if (!mounted) return;
      tries++;

      try {
        final response = await widget.dio.get(
          '/api/payments/mobile-money/status/${Uri.encodeComponent(reference)}',
        );
        final data = response.data;
        if (data is! Map) continue;

        final success = data['success'] == true;
        final transaction = data['transaction'];
        final status = transaction is Map ? transaction['status']?.toString() : null;

        if (!success || status == null) continue;

        switch (status.toUpperCase()) {
          case 'SUCCESS':
            if (!mounted) return;
            setState(() => _showMobileMoneyWaiting = false);
            _closeAndSuccess();
            return;
          case 'FAILED':
            if (!mounted) return;
            setState(() {
              _showMobileMoneyWaiting = false;
              _error = 'Transaction failed';
            });
            if (mounted) {
              ScaffoldMessenger.of(context).showSnackBar(
                const SnackBar(content: Text('Transaction failed')),
              );
            }
            return;
          case 'PENDING':
            continue;
          default:
            continue;
        }
      } catch (_) {
        // Retry on network error
      }
    }

    if (!mounted) return;
    setState(() {
      _showMobileMoneyWaiting = false;
      _error = 'Transaction timed out. Please try again.';
    });
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Transaction failed')),
    );
  }

  Future<void> _startFinFlo() async {
    debugPrint('[FinFlo] User tapped FinFlo payment. amount=${widget.amount} callbackBaseUrl=${widget.callbackBaseUrl}');
    setState(() {
      _error = null;
      _loading = true;
    });

    try {
      final payload = {
        'amount': widget.amount,
        'callback_base': widget.callbackBaseUrl,
      };
      debugPrint('[FinFlo] POST create-session payload: $payload');
      final response = await widget.dio.post(
        '/api/payments/finflo/create-session',
        data: payload,
      );

      final data = response.data;
      debugPrint('[FinFlo] create-session response status=${response.statusCode} data=$data');
      if (data is! Map) {
        debugPrint('[FinFlo] Invalid response: not a Map');
        setState(() {
          _loading = false;
          _error = 'Invalid response';
        });
        return;
      }

      final responseData = data['data'];
      final payUrl = responseData is Map ? responseData['pay_url']?.toString() : null;
      final pendingId = responseData is Map ? responseData['pending_id']?.toString() : null;

      if (payUrl == null || payUrl.isEmpty || pendingId == null || pendingId.isEmpty) {
        debugPrint('[FinFlo] Missing pay_url or pending_id in response');
        setState(() {
          _loading = false;
          _error = 'Could not start payment';
        });
        return;
      }

      debugPrint('[FinFlo] Got pay_url (length=${payUrl.length}) pending_id=$pendingId. Opening browser...');
      final uri = Uri.parse(payUrl);
      try {
        final launched = await launchUrl(
          uri,
          mode: LaunchMode.externalApplication,
        );
        debugPrint('[FinFlo] launchUrl(externalApplication) returned $launched');
        if (!launched) {
          final inApp = await launchUrl(uri, mode: LaunchMode.inAppWebView);
          debugPrint('[FinFlo] launchUrl(inAppWebView) returned $inApp');
          if (!inApp && mounted) {
            setState(() {
              _loading = false;
              _error = 'Cannot open payment page. Try again later.';
            });
            return;
          }
        }
      } catch (e, st) {
        debugPrint('[FinFlo] launchUrl failed: $e\n$st');
        if (!mounted) return;
        try {
          final inApp = await launchUrl(uri, mode: LaunchMode.inAppWebView);
          debugPrint('[FinFlo] Fallback inAppWebView returned $inApp');
          if (!inApp && mounted) {
            setState(() {
              _loading = false;
              _error = 'Cannot open payment page. Try again later.';
            });
            return;
          }
        } catch (_) {
          if (!mounted) return;
          setState(() {
            _loading = false;
            _error = 'Cannot open payment page. Try again later.';
          });
          return;
        }
      }

      if (!mounted) return;
      debugPrint('[FinFlo] Browser opened. Starting status poll for pending_id=$pendingId');
      setState(() {
        _loading = false;
        _showFinFloWaiting = true;
      });

      _pollFinFloStatus(pendingId);
    } on DioException catch (e) {
      debugPrint('[FinFlo] DioException: ${e.type} ${e.response?.statusCode} ${e.response?.data} ${e.message}');
      if (!mounted) return;
      String? msg;
      if (e.response?.data is Map) {
        final d = e.response!.data as Map;
        msg = d['message']?.toString();
        if (msg == null && d['error'] is Map) {
          msg = (d['error'] as Map)['message']?.toString();
        }
      }
      setState(() {
        _loading = false;
        _error = msg ?? 'Could not start FinFlo payment. Check your connection.';
      });
    } catch (e, st) {
      debugPrint('[FinFlo] Unexpected error: $e\n$st');
      if (!mounted) return;
      setState(() {
        _loading = false;
        _error = 'Could not start FinFlo payment. Check your connection.';
      });
    }
  }

  static const _finfloPollInterval = Duration(seconds: 2);
  static const _finfloMaxPollTries = 120; // ~4 min

  Future<void> _pollFinFloStatus(String pendingId) async {
    int tries = 0;
    debugPrint('[FinFlo] Poll started for pending_id=$pendingId');
    while (tries < _finfloMaxPollTries && mounted) {
      await Future.delayed(_finfloPollInterval);
      if (!mounted) return;
      tries++;

      try {
        final response = await widget.dio.get(
          '/api/payments/finflo/status/${Uri.encodeComponent(pendingId)}',
        );
        final data = response.data;
        if (data is! Map) continue;

        final responseData = data['data'];
        final status = responseData is Map ? responseData['status']?.toString() : null;
        if (tries == 1 || (tries % 10 == 0) || status == 'completed' || status == 'expired') {
          debugPrint('[FinFlo] Poll #$tries status=$status');
        }

        if (status == 'completed') {
          debugPrint('[FinFlo] Payment completed. Closing and calling onSuccess.');
          if (!mounted) return;
          setState(() => _showFinFloWaiting = false);
          _closeAndSuccess();
          return;
        }
        if (status == 'expired') {
          debugPrint('[FinFlo] Session expired.');
          if (!mounted) return;
          setState(() {
            _showFinFloWaiting = false;
            _error = 'Payment session expired';
          });
          return;
        }
      } catch (e) {
        if (tries <= 3) debugPrint('[FinFlo] Poll #$tries error: $e');
      }
    }

    debugPrint('[FinFlo] Poll timed out after $tries tries');
    if (!mounted) return;
    setState(() {
      _showFinFloWaiting = false;
      _error = 'Payment timed out';
    });
  }

  @override
  Widget build(BuildContext context) {
    if (_showFinFloWaiting) {
      return Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Row(
              children: [
                IconButton(
                  icon: const Icon(Icons.arrow_back),
                  onPressed: () => setState(() => _showFinFloWaiting = false),
                ),
                Text('FinFlo', style: Theme.of(context).textTheme.titleMedium),
              ],
            ),
            const SizedBox(height: 16),
            const Text(
              'Complete payment in the browser, then return to the app. '
              'Payment will be confirmed automatically.',
              textAlign: TextAlign.center,
            ),
            const SizedBox(height: 16),
            const Center(child: CircularProgressIndicator()),
          ],
        ),
      );
    }

    if (_showMobileMoneyWaiting) {
      return Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Row(
              children: [
                IconButton(
                  icon: const Icon(Icons.arrow_back),
                  onPressed: () => setState(() => _showMobileMoneyWaiting = false),
                ),
                Text('Mobile Money', style: Theme.of(context).textTheme.titleMedium),
              ],
            ),
            const SizedBox(height: 24),
            const Icon(Icons.phone_android, size: 48, color: Colors.grey),
            const SizedBox(height: 16),
            const Text(
              'Please check your phone and enter your Mobile Money PIN to complete payment.',
              textAlign: TextAlign.center,
              style: TextStyle(fontSize: 16),
            ),
            const SizedBox(height: 24),
            const Center(child: CircularProgressIndicator()),
          ],
        ),
      );
    }

    if (_showMobileMoneyForm) {
      return Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Row(
              children: [
                IconButton(
                  icon: const Icon(Icons.arrow_back),
                  onPressed: _loading ? null : () => setState(() => _showMobileMoneyForm = false),
                ),
                Text('Mobile Money', style: Theme.of(context).textTheme.titleMedium),
              ],
            ),
            const SizedBox(height: 8),
            if (_error != null)
              Padding(
                padding: const EdgeInsets.only(bottom: 12),
                child: Text(
                  _error!,
                  style: TextStyle(color: Theme.of(context).colorScheme.error),
                ),
              ),
            TextField(
              controller: _phoneController,
              focusNode: _phoneFocus,
              keyboardType: TextInputType.phone,
              enabled: !_loading,
              decoration: const InputDecoration(
                labelText: 'Phone number',
                hintText: '7XXXXXXX',
                border: OutlineInputBorder(),
              ),
              onChanged: (_) => setState(() => _error = null),
            ),
            const SizedBox(height: 12),
            Text(
              'Amount: ${widget.amount.toStringAsFixed(2)} UGX',
              style: Theme.of(context).textTheme.titleSmall,
            ),
            const SizedBox(height: 16),
            FilledButton(
              onPressed: _loading ? null : _requestMobileMoney,
              child: _loading
                  ? const SizedBox(
                      height: 24,
                      width: 24,
                      child: CircularProgressIndicator(strokeWidth: 2),
                    )
                  : const Text('Pay'),
            ),
          ],
        ),
      );
    }

    return Padding(
      padding: const EdgeInsets.all(24),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Text('Choose payment', style: Theme.of(context).textTheme.titleMedium),
          const SizedBox(height: 16),
          ListTile(
            leading: const Icon(Icons.account_balance),
            title: const Text('FinFlo'),
            onTap: _loading ? null : _startFinFlo,
          ),
          ListTile(
            leading: const Icon(Icons.phone_android),
            title: const Text('Mobile Money'),
            onTap: () => setState(() => _showMobileMoneyForm = true),
          ),
        ],
      ),
    );
  }
}
