import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../../core/auth/auth_notifier.dart';
import '../../../core/theme/halo_tokens.dart';
import '../../../core/theme/widgets/widgets.dart';

/// S3a · Input WhatsApp (mitra).
///
/// Visual contract mirrors `figma-bestie/project/screens/onboarding.jsx::S3Phone`
/// (first half — phone-input view). Differences from the customer S3a:
///   - Greeting heading "Halo Mitra Bestie" (no name-set step pre-OTP).
///   - No "lanjut tanpa verifikasi" footer (mitra has no anonymous path).
///   - Privacy reassurance card is omitted (audience is internal).
class LoginScreen extends ConsumerStatefulWidget {
  const LoginScreen({super.key});

  @override
  ConsumerState<LoginScreen> createState() => _LoginScreenState();
}

class _LoginScreenState extends ConsumerState<LoginScreen> {
  final _phoneController = TextEditingController();
  ProviderSubscription<AsyncValue<MitraAuthData>>? _authSub;

  String? _phoneErrorText;

  // Server-imposed lockout from /otp/request 429s. Drives both the CTA's
  // disabled state and its label so the mitra sees a live countdown.
  int _lockoutSeconds = 0;
  Timer? _lockoutTimer;

  // Set true in _submit() right before requestOtp; cleared after the listener
  // pushes /otp. Without this flag the listener fires on every subsequent
  // auth-state transition (verifyOtp's AsyncLoading / AsyncError preserve the
  // OtpSentData via Riverpod's copyWithPrevious) and stacks duplicate /otp
  // pages on top of itself, because GoRouterState.of(context) returns the
  // LoginScreen's own page state (/login), not the navigator's top route.
  bool _expectOtpPush = false;

  @override
  void initState() {
    super.initState();
    _phoneController.addListener(() {
      // Re-render so the +62 pill border + CTA enabled-state respond to
      // the user typing.
      setState(() {});
    });

    _authSub = ref.listenManual<AsyncValue<MitraAuthData>>(mitraAuthProvider,
        (prev, next) async {
      if (!mounted) return;
      final data = next.valueOrNull;
      if (data is MitraAuthOtpSentData && _expectOtpPush) {
        _expectOtpPush = false;
        context.push('/otp', extra: _e164Phone());
        return;
      }
      if (next is! AsyncError) return;
      // Only handle errors for our own requestOtp call. verifyOtp errors
      // belong to OtpScreen — without this gate LoginScreen's default
      // snackbar would paint on top of OtpScreen's inline error.
      if (!_expectOtpPush) return;
      _expectOtpPush = false;

      final err = next.error;
      if (err is! MitraAuthError) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text(err.toString())),
        );
        return;
      }

      switch (err.code) {
        case 'PHONE_INVALID':
          setState(() => _phoneErrorText = err.message);
          break;
        case 'OTP_COOLDOWN':
          if (err.retryAfterSeconds != null) {
            _startLockout(err.retryAfterSeconds!);
          }
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text(err.message)),
          );
          break;
        case 'OTP_RATE_LIMIT_PHONE':
          if (err.retryAfterSeconds != null) {
            _startLockout(err.retryAfterSeconds!);
          }
          await _showRateLimitDialog(err, isIp: false);
          break;
        case 'OTP_RATE_LIMIT_IP':
          if (err.retryAfterSeconds != null) {
            _startLockout(err.retryAfterSeconds!);
          }
          await _showRateLimitDialog(err, isIp: true);
          break;
        default:
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text(err.message)),
          );
      }
    });
  }

  @override
  void dispose() {
    _authSub?.close();
    _lockoutTimer?.cancel();
    _phoneController.dispose();
    super.dispose();
  }

  void _startLockout(int seconds) {
    _lockoutTimer?.cancel();
    setState(() => _lockoutSeconds = seconds);
    _lockoutTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
      if (!mounted) {
        timer.cancel();
        return;
      }
      setState(() {
        if (_lockoutSeconds > 0) _lockoutSeconds--;
        if (_lockoutSeconds <= 0) timer.cancel();
      });
    });
  }

  /// Subscriber digits with any country-code / leading-zero noise stripped.
  /// Accepts these user-typed formats (all normalize to `8xxxxxxxxx`):
  ///   `8xxxxxxxxx`       — subscriber only
  ///   `08xxxxxxxxx`      — local format with leading 0
  ///   `628xxxxxxxxx`     — country code without +
  ///   `+628xxxxxxxxx`    — full E.164
  ///   `0628xxxxxxxxx`    — typo combo (rare but seen)
  ///
  /// Strategy: strip non-digits, strip leading zeros, strip leading `62`.
  /// Indonesian mobile subscriber numbers always start with `8`, so a
  /// leading `62` after the zero-strip is always the country code.
  String _subscriberDigits() {
    var digits = _phoneController.text.replaceAll(RegExp(r'\D'), '');
    digits = digits.replaceFirst(RegExp(r'^0+'), '');
    if (digits.startsWith('62')) {
      digits = digits.substring(2);
    }
    return digits;
  }

  String _e164Phone() => '+62${_subscriberDigits()}';

  String _formatCountdown(int seconds) {
    if (seconds < 60) return '${seconds}s';
    final mins = seconds ~/ 60;
    final secs = seconds % 60;
    return '${mins}m ${secs.toString().padLeft(2, '0')}s';
  }

  Future<void> _submit() {
    final phone = _e164Phone();
    setState(() => _phoneErrorText = null);
    _expectOtpPush = true;
    return ref.read(mitraAuthProvider.notifier).requestOtp(phone);
  }

  Future<void> _showRateLimitDialog(MitraAuthError err, {required bool isIp}) {
    final retryText = err.retryAfterSeconds != null
        ? '\n\nCoba lagi dalam ${_formatCountdown(err.retryAfterSeconds!)}.'
        : '';
    return showDialog<void>(
      context: context,
      builder: (ctx) => AlertDialog(
        title: Text(isIp
            ? 'Terlalu banyak permintaan dari jaringan ini'
            : 'Terlalu banyak permintaan untuk nomor ini'),
        content: Text('${err.message}$retryText'),
        actions: [
          TextButton(
            onPressed: () => Navigator.of(ctx).pop(),
            child: const Text('Tutup'),
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    final authState = ref.watch(mitraAuthProvider);
    final isLoading = authState is AsyncLoading;
    final hasMinDigits = _subscriberDigits().length >= 9;
    final isLockedOut = _lockoutSeconds > 0;
    final canSubmit = hasMinDigits && !isLoading && !isLockedOut;

    return Scaffold(
      backgroundColor: HaloTokens.bg,
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.fromLTRB(28, 8, 28, 28),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              Expanded(
                child: LayoutBuilder(
                  builder: (context, constraints) => SingleChildScrollView(
                    child: ConstrainedBox(
                      constraints: BoxConstraints(minHeight: constraints.maxHeight),
                      child: IntrinsicHeight(
                        child: Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          crossAxisAlignment: CrossAxisAlignment.stretch,
                          children: [
                            const Text(
                              'Halo Mitra Bestie',
                              style: TextStyle(
                                fontFamily: HaloTokens.fontDisplay,
                                fontSize: 28,
                                fontWeight: FontWeight.w700,
                                color: HaloTokens.brandDark,
                                height: 1.15,
                                letterSpacing: -0.56,
                              ),
                            ),
                            const SizedBox(height: 10),
                            const Text(
                              'masukin nomor wa kamu untuk lanjut',
                              style: TextStyle(
                                fontFamily: HaloTokens.fontBody,
                                fontSize: 14.5,
                                color: HaloTokens.inkSoft,
                                height: 1.5,
                              ),
                            ),
                            const SizedBox(height: 24),
                            _PhoneRow(
                              controller: _phoneController,
                              borderColor: _phoneErrorText != null
                                  ? HaloTokens.danger
                                  : hasMinDigits
                                      ? HaloTokens.brand
                                      : HaloTokens.border,
                            ),
                            if (_phoneErrorText != null) ...[
                              const SizedBox(height: 8),
                              Text(
                                _phoneErrorText!,
                                style: const TextStyle(
                                  fontFamily: HaloTokens.fontBody,
                                  color: HaloTokens.danger,
                                  fontSize: 13,
                                ),
                              ),
                            ],
                          ],
                        ),
                      ),
                    ),
                  ),
                ),
              ),
              HaloButton(
                label: isLoading
                    ? 'memproses...'
                    : isLockedOut
                        ? 'coba lagi dalam ${_formatCountdown(_lockoutSeconds)}'
                        : 'kirim kode',
                fullWidth: true,
                onPressed: canSubmit ? _submit : null,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class _PhoneRow extends StatelessWidget {
  final TextEditingController controller;
  final Color borderColor;
  const _PhoneRow({required this.controller, required this.borderColor});

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 60,
      padding: const EdgeInsets.symmetric(horizontal: 18),
      decoration: BoxDecoration(
        color: HaloTokens.surface,
        borderRadius: HaloRadius.lg,
        border: Border.all(color: borderColor, width: 1.5),
      ),
      child: Row(
        children: [
          const Text(
            '🇮🇩 +62',
            style: TextStyle(
              fontFamily: HaloTokens.fontBody,
              fontSize: 15,
              fontWeight: FontWeight.w600,
              color: HaloTokens.ink,
            ),
          ),
          const SizedBox(width: 12),
          Expanded(
            child: TextField(
              controller: controller,
              keyboardType: TextInputType.phone,
              // Allow + alongside digits so users can paste/type +62...;
              // _subscriberDigits() strips it during normalization.
              inputFormatters: [
                FilteringTextInputFormatter.allow(RegExp(r'[\d+]')),
              ],
              maxLength: 16,
              style: const TextStyle(
                fontFamily: HaloTokens.fontBody,
                fontSize: 16,
                fontWeight: FontWeight.w500,
                color: HaloTokens.ink,
              ),
              decoration: const InputDecoration(
                hintText: '812 3456 7890',
                hintStyle: TextStyle(
                  fontFamily: HaloTokens.fontBody,
                  fontSize: 16,
                  fontWeight: FontWeight.w500,
                  color: HaloTokens.inkMuted,
                ),
                // Override app-wide inputDecorationTheme so the input sits
                // flush inside the outer pill — no fill, no border.
                filled: false,
                border: InputBorder.none,
                enabledBorder: InputBorder.none,
                focusedBorder: InputBorder.none,
                errorBorder: InputBorder.none,
                focusedErrorBorder: InputBorder.none,
                disabledBorder: InputBorder.none,
                isCollapsed: true,
                counterText: '',
                contentPadding: EdgeInsets.symmetric(vertical: 18),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
