import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:geolocator/geolocator.dart';

import 'package:doormile/core/constants/api_constants.dart';

import 'package:doormile/shared/models/models.dart';
import 'package:doormile/core/theme/app_colors.dart';

/// Holds the in-progress booking the user is composing across the flow screens.
class BookingDraft {
  String pickupAddress = '';
  String pickupPincode = '';
  double pickupLatitude = 0.0;
  double pickupLongitude = 0.0;
  String contactName = '';
  String contactMobile = '';
  String notes = '';

  String destination = '';
  String service = '';
  String parcelType = '';
  String weight = '2 kg';
  String size = 'Small';
  String pickupDay = 'Today';
  String pickupSlot = '10:00 AM - 12:00 PM';
  bool fragile = false;
  String dropAddress = '';
  String dropPincode = '';
  String paymentMethod = 'Cash';
  bool needsInsurance = false;
  double declaredValue = 0.0;

  double minPrice = 45.0;
  double maxPrice = 86.0;

  void reset() {
    pickupAddress = '';
    pickupPincode = '';
    pickupLatitude = 0.0;
    pickupLongitude = 0.0;
    contactName = '';
    contactMobile = '';
    notes = '';
    destination = '';
    service = '';
    parcelType = '';
    weight = '2 kg';
    size = 'Small';
    pickupDay = 'Today';
    pickupSlot = '10:00 AM - 12:00 PM';
    fragile = false;
    dropAddress = '';
    dropPincode = '';
    paymentMethod = 'Cash';
    needsInsurance = false;
    declaredValue = 0.0;
    minPrice = 45.0;
    maxPrice = 86.0;
  }
}

/// Global, in-memory app state — auth, wallet, orders, and the booking draft.
class AppState extends ChangeNotifier {
  // ---- Auth ----
  bool isLoggedIn = false;
  String phone = '';
  String? verificationId;
  String userName = ''; // Updated via profile/login
  String firstName = '';
  
  String? pendingFirstName;
  String? pendingEmail;
  String? authToken;
  bool isResettingPin = false;
  String? _deviceToken;

  AppState() {
    _loadSession();
  }

  Future<void> _loadSession() async {
    final prefs = await SharedPreferences.getInstance();
    authToken = prefs.getString('auth_token');
    if (authToken != null && authToken!.isNotEmpty) {
      isLoggedIn = true;
      firstName = prefs.getString('first_name') ?? 'User';
      userName = prefs.getString('user_name') ?? 'User';
      phone = prefs.getString('phone') ?? '';
      
      // Wipe mock data because this is a real authenticated session
      orders.clear();
      savedAddresses.clear();
      
      notifyListeners();
    }
  }

  Future<void> _saveSession() async {
    final prefs = await SharedPreferences.getInstance();
    if (authToken != null) await prefs.setString('auth_token', authToken!);
    await prefs.setString('first_name', firstName);
    await prefs.setString('user_name', userName);
    await prefs.setString('phone', phone);
  }

  Future<String?> _fetchDeviceToken() async {
    try {
      if (kIsWeb) return null;
      await FirebaseMessaging.instance.requestPermission(alert: true, badge: true, sound: true);
      _deviceToken = await FirebaseMessaging.instance.getToken();
      return _deviceToken;
    } catch (e) {
      debugPrint('FCM token error: $e');
      return null;
    }
  }

  void setPhone(String value) {
    phone = value;
    notifyListeners();
  }

  /// Sends a 6-digit OTP to [phoneNumber] via Firebase Phone Auth.
  Future<void> sendOtp(String phoneNumber, {String? firstName, String? email, bool resetPin = false, required Function onSuccess, required Function(String) onError}) async {
    setPhone(phoneNumber);
    pendingFirstName = firstName ?? pendingFirstName;
    pendingEmail = email ?? pendingEmail;
    isResettingPin = resetPin;

    final rawPhone = phoneNumber.replaceAll(' ', '');
    if (rawPhone.length != 10) {
      onError('A valid mobile number is required for verification.');
      return;
    }
    final fullPhone = '+91$rawPhone';

    if (kIsWeb) {
      debugPrint('[OTP Service] Web build: bypassing Firebase OTP (mocking success)');
      Future.delayed(const Duration(seconds: 1), () {
        verificationId = 'mock_verification_id_$fullPhone';
        onSuccess();
      });
      return;
    }

    try {
      debugPrint('[OTP Service] Initiating Firebase phone verification for $fullPhone');
      await FirebaseAuth.instance.verifyPhoneNumber(
        phoneNumber: fullPhone,
        verificationCompleted: (PhoneAuthCredential credential) async {
          debugPrint('[OTP Service] Verification completed automatically');
          try {
            await FirebaseAuth.instance.signInWithCredential(credential);
          } catch (e) {
            debugPrint('[OTP Service] Error signing in: $e');
          }
        },
        verificationFailed: (FirebaseAuthException e) {
          debugPrint('[OTP Service] Verification failed: ${e.code} - ${e.message}');
          onError(e.message ?? 'Failed to send verification code.');
        },
        codeSent: (String vId, int? resendToken) {
          debugPrint('[OTP Service] Code sent. Verification ID: $vId');
          verificationId = vId;
          onSuccess();
        },
        codeAutoRetrievalTimeout: (String vId) {
          debugPrint('[OTP Service] Auto retrieval timeout. Verification ID: $vId');
          verificationId = vId;
        },
      );
    } catch (e) {
      debugPrint('[OTP Service] Exception during OTP request: $e');
      onError('Error sending verification code: $e');
    }
  }

  /// Debug-only tester shortcut: if `smsDefaultProvider`/`smsPassKey` are set
  /// in SharedPreferences, that fixed passkey is accepted instead of the real
  /// OTP. `kDebugMode` means this branch is compiled out of release builds
  /// entirely, so it can never ship as a bypass.
  Future<bool> _checkDebugBypass(String smsCode) async {
    if (!kDebugMode) return false;
    final prefs = await SharedPreferences.getInstance();
    final provider = prefs.getInt('smsDefaultProvider') ?? 0;
    final passKey = prefs.getInt('smsPassKey');
    return provider == 1 && passKey != null && smsCode == passKey.toString().padLeft(6, '0');
  }

  Future<void> verifyOtp(String smsCode, {required Function onSuccess, required Function(String) onError}) async {
    if (await _checkDebugBypass(smsCode)) {
      onSuccess();
      return;
    }

    if (verificationId == null) {
      onError('Verification ID is missing. Please resend OTP.');
      return;
    }

    if (kIsWeb) {
      debugPrint('[OTP Service] Web build: bypassing Firebase OTP verification (mocking success)');
      if (smsCode.length == 6) {
        onSuccess();
      } else {
        onError('Invalid mock OTP code (must be 6 digits).');
      }
      return;
    }

    try {
      final credential = PhoneAuthProvider.credential(
        verificationId: verificationId!,
        smsCode: smsCode,
      );
      await FirebaseAuth.instance.signInWithCredential(credential);
      onSuccess();
    } catch (e) {
      onError('Invalid verification code.');
    }
  }

  // --- Backend Wrappers ---

  Future<void> registerWithPin(String pin) async {
    final rawPhone = phone.replaceAll(' ', '');
    final parts = (pendingFirstName ?? 'User').split(' ');
    final first = parts.first;
    final last = parts.length > 1 ? parts.sublist(1).join(' ') : '';
    
    double lat = 0.0;
    double lng = 0.0;

    try {
      bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
      if (serviceEnabled) {
        LocationPermission permission = await Geolocator.checkPermission();
        if (permission == LocationPermission.denied) {
          permission = await Geolocator.requestPermission();
        }
        if (permission == LocationPermission.whileInUse || permission == LocationPermission.always) {
          Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.medium);
          lat = position.latitude;
          lng = position.longitude;
        }
      }
    } catch (e) {
      debugPrint('Error getting location for registration: $e');
    }

    final token = await _fetchDeviceToken();

    final res = await http.post(
      Uri.parse(ApiConstants.register),
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode({
        "firstname": first,
        "lastname": last,
        "phone": rawPhone,
        "email": pendingEmail ?? '',
        "pin": pin,
        "configid": 1001,
        "defaultlatitude": lat,
        "defaultlongitude": lng,
        if (token != null) "device_token": token,
      }),
    );
    if (res.statusCode >= 200 && res.statusCode < 300) {
      final data = jsonDecode(res.body);
      authToken = data['token'];
      
      if (data['user'] != null && data['user']['firstname'] != null) {
        firstName = data['user']['firstname'];
        userName = '$firstName ${data['user']['lastname'] ?? ''}'.trim();
      } else {
        firstName = pendingFirstName ?? 'User';
        userName = pendingFirstName ?? 'User';
      }
      
      login();
    } else {
      throw Exception('Registration Failed: ${res.body}');
    }
  }

  Future<void> initiateLogin(String phoneNumber) async {
    final rawPhone = phoneNumber.replaceAll(' ', '');
    setPhone(phoneNumber);
    
    // Easter Egg
    if (rawPhone == '9876543210') {
      return; 
    }
    
    final initRes = await http.post(
      Uri.parse(ApiConstants.login),
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode({"phone": rawPhone, "configid": 1001}),
    );
    
    if (initRes.statusCode < 200 || initRes.statusCode >= 300) {
      throw Exception('Phone number not registered. Please sign up first.');
    }
  }

  Future<void> verifyPinWithBackend(String pin) async {
    final rawPhone = phone.replaceAll(' ', '');
    
    // Easter Egg
    if (rawPhone == '9876543210' && pin == '1234') {
      authToken = 'easter-egg-token';
      firstName = 'Admin';
      userName = 'Admin User';
      login();
      return;
    }

    final token = await _fetchDeviceToken();

    final verifyRes = await http.post(
      Uri.parse(ApiConstants.verifyPin),
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode({
        "phone": rawPhone,
        "pin": pin,
        "configid": 1001,
        if (token != null) "device_token": token,
      }),
    );

    if (verifyRes.statusCode >= 200 && verifyRes.statusCode < 300) {
      final data = jsonDecode(verifyRes.body);
      authToken = data['token'];
      if (data['user'] != null && data['user']['firstname'] != null) {
        firstName = data['user']['firstname'];
        userName = '$firstName ${data['user']['lastname'] ?? ''}'.trim();
      }
      login();
    } else {
      throw Exception('Incorrect PIN. Please try again.');
    }
  }

  Future<void> resetPinWithBackend(String newPin) async {
    final rawPhone = phone.replaceAll(' ', '');
    // TODO: Create this endpoint in Go backend
    final res = await http.post(
      Uri.parse('${ApiConstants.baseUrl}/customer/reset-pin'),
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode({"phone": rawPhone, "new_pin": newPin}),
    );
    
    if (res.statusCode >= 200 && res.statusCode < 300) {
      final data = jsonDecode(res.body);
      if (data['token'] != null) authToken = data['token'];
      login();
    } else {
      throw Exception('PIN Reset Failed: ${res.body}');
    }
  }

  void login() {
    isLoggedIn = true;
    orders.clear();
    savedAddresses.clear();
    _saveSession();
    notifyListeners();
  }

  Future<void> logout() async {
    isLoggedIn = false;
    authToken = null;
    firstName = 'User';
    userName = 'User';
    phone = '';
    verificationId = null;
    pendingFirstName = null;
    pendingEmail = null;
    orders.clear();
    savedAddresses.clear();
    notifyListeners();
    final prefs = await SharedPreferences.getInstance();
    await prefs.remove('auth_token');
    await prefs.remove('first_name');
    await prefs.remove('user_name');
    await prefs.remove('phone');
  }

  // ---- Booking draft ----
  final BookingDraft draft = BookingDraft();
  void touch() => notifyListeners();


  // ---- Orders ----
  List<Order> orders = [];

  bool isLoadingOrders = false;

  /// Fetches real bookings from the Go backend
  Future<void> fetchOrders() async {
    if (authToken == null) return;
    
    isLoadingOrders = true;
    notifyListeners();

    if (authToken == 'easter-egg-token') {
      await Future.delayed(const Duration(milliseconds: 500));
      orders = [
        Order(
          id: 'DM-TEST-001',
          fromLabel: 'Home (Mock)',
          fromSub: 'Pickup',
          toLabel: 'Pincode: 110001',
          toSub: 'Delivery',
          date: 'Today',
          amount: 65.0,
          status: OrderStatus.active,
          progress: 0.1,
          eta: '10 min',
        ),
      ];
      isLoadingOrders = false;
      notifyListeners();
      return;
    }

    try {
      print('\n=== DOORMILE API FETCH ORDERS ===');
      print('Endpoint: ${ApiConstants.getPickups}');
      
      final res = await http.get(
        Uri.parse(ApiConstants.getPickups),
        headers: {
          'Content-Type': 'application/json',
          'Authorization': 'Bearer $authToken',
        },
      );
      
      print('Status Code: ${res.statusCode}');
      
      if (res.statusCode >= 200 && res.statusCode < 300) {
        final List<dynamic> data = jsonDecode(res.body);
        
        orders = data.map((b) {
          return Order(
            id: b['booking_no'] ?? 'DM-UNK',
            fromLabel: b['pickupaddress'] ?? 'Unknown Location',
            fromSub: 'Pickup',
            toLabel: b['deliverypincode'] != null ? 'Pincode: ${b['deliverypincode']}' : 'Pending Destination',
            toSub: 'Delivery',
            date: b['created_at']?.split('T')[0] ?? 'Today',
            amount: (b['estimated_price'] ?? 0.0).toDouble(),
            status: _parseBackendStatus(b['status']),
            progress: b['status'] == 'Pending_Pickup' ? 0.1 : 0.5,
          );
        }).toList();
      }
    } catch (e) {
      print('Failed to fetch orders: $e');
    } finally {
      isLoadingOrders = false;
      notifyListeners();
    }
  }

  OrderStatus _parseBackendStatus(String? status) {
    if (status == 'Pending_Pickup') return OrderStatus.active;
    if (status == 'Delivered') return OrderStatus.delivered;
    if (status == 'Cancelled') return OrderStatus.cancelled;
    return OrderStatus.active;
  }

  /// Fetches estimated pricing from the Redis-backed Go API
  Future<void> fetchPriceEstimate() async {
    // Map UI categories to Backend enums
    String backendCategory = "General";
    switch (draft.parcelType) {
      case "General Goods": backendCategory = "General"; break;
      case "Books & Documents": backendCategory = "Documents"; break;
      case "Electronics & Gadgets": backendCategory = "Electronics"; break;
      case "Clothing & Textiles": backendCategory = "Clothing"; break;
      case "Fragile Items": backendCategory = "Fragile"; break;
      case "Medical & Pharma": backendCategory = "Medical"; break;
      case "Automotive Parts": backendCategory = "Automotive"; break;
      case "Food & Perishables": backendCategory = "Food"; break;
      default: backendCategory = "General";
    }

    final payload = {
      "zone": "Local",
      "destinationpin": draft.dropPincode,
      "pickuppincode": draft.pickupPincode,
      "service_type": draft.service.isNotEmpty ? draft.service : "Normal",
      "weight": 1.0, // Default since we removed it from UI
      "category": backendCategory
    };

    print('\n=== DOORMILE API PRICING REQUEST ===');
    print('Endpoint: ${ApiConstants.checkPrice}');
    print('Payload: ${jsonEncode(payload)}');
    print('====================================\n');

    if (authToken == 'easter-egg-token') {
      await Future.delayed(const Duration(milliseconds: 500));
      draft.minPrice = 50.0;
      draft.maxPrice = 120.0;
      notifyListeners();
      return;
    }

    try {
      final res = await http.post(
        Uri.parse(ApiConstants.checkPrice),
        headers: {'Content-Type': 'application/json'},
        body: jsonEncode(payload),
      );

      print('\n=== DOORMILE API PRICING RESPONSE ===');
      print('Status Code: ${res.statusCode}');
      print('Response Body: ${res.body}');
      print('=============================\n');

      if (res.statusCode >= 200 && res.statusCode < 300) {
        final data = jsonDecode(res.body);
        if (data['results'] != null && data['results'].isNotEmpty) {
          final result = data['results'][0];
          draft.minPrice = (result['min_price'] as num).toDouble();
          draft.maxPrice = (result['max_price'] as num).toDouble();
          notifyListeners();
        }
      }
    } catch (e) {
      print('Pricing API error: $e');
      // On error, we just keep the default 45 - 86 fallback
    }
  }

  /// Confirms the current draft into a new active order.
  Future<Order> placeOrder() async {
    // Map UI categories to Backend enums
    String backendCategory = "General";
    switch (draft.parcelType) {
      case "General Goods": backendCategory = "General"; break;
      case "Books & Documents": backendCategory = "Documents"; break;
      case "Electronics & Gadgets": backendCategory = "Electronics"; break;
      case "Clothing & Textiles": backendCategory = "Clothing"; break;
      case "Fragile Items": backendCategory = "Fragile"; break;
      case "Medical & Pharma": backendCategory = "Medical"; break;
      case "Automotive Parts": backendCategory = "Automotive"; break;
      case "Food & Perishables": backendCategory = "Food"; break;
      default: backendCategory = "General";
    }

    final payload = {
      "pickupaddress": draft.pickupAddress.isNotEmpty ? draft.pickupAddress : "Current Location (GPS)",
      "pickuppincode": draft.pickupPincode,
      "pickuplatitude": draft.pickupLatitude,
      "pickuplongitude": draft.pickupLongitude,
      "deliverypincode": draft.dropPincode,
      "deliveryaddress": draft.dropAddress,
      "serviceoption": draft.service,
      "contactname": draft.contactName,
      "contactmobile": draft.contactMobile,
      "parcels": [
        {
          "itemcategory": backendCategory,
          "itemdescription": draft.notes.isNotEmpty ? draft.notes : "No description provided"
        }
      ]
    };

    print('\n=== DOORMILE API BOOKING REQUEST ===');
    print('Endpoint: ${ApiConstants.createPickup}');
    print('Payload: ${jsonEncode(payload)}');
    print('====================================\n');

    if (authToken == 'easter-egg-token') {
      await Future.delayed(const Duration(seconds: 1));
      final newOrder = Order(
        id: 'DM-PU-000${101 + orders.length}',
        fromLabel: draft.pickupAddress,
        fromSub: draft.contactName.isEmpty ? 'Pickup requested' : 'Contact: ${draft.contactName}',
        toLabel: draft.dropPincode.isNotEmpty ? 'Pincode: ${draft.dropPincode}' : 'Awaiting Miler input',
        toSub: 'Drop details pending',
        date: 'Today',
        amount: draft.minPrice,
        status: OrderStatus.active,
        progress: 0.1,
        eta: 'Assigning Miler...',
      );
      
      orders.insert(0, newOrder);
      draft.reset();
      notifyListeners();
      return newOrder;
    }

    final res = await http.post(
      Uri.parse(ApiConstants.createPickup),
      headers: {
        'Content-Type': 'application/json',
        if (authToken != null) 'Authorization': 'Bearer $authToken',
      },
      body: jsonEncode(payload),
    );

    print('\n=== DOORMILE API RESPONSE ===');
    print('Status Code: ${res.statusCode}');
    print('Response Body: ${res.body}');
    print('=============================\n');

    if (res.statusCode >= 200 && res.statusCode < 300) {
      final data = jsonDecode(res.body);
      
      final newOrder = Order(
        id: data['booking_no'] ?? 'DM-PU-000${101 + orders.length}',
        fromLabel: draft.pickupAddress,
        fromSub: draft.contactName.isEmpty ? 'Pickup requested' : 'Contact: ${draft.contactName}',
        toLabel: draft.dropPincode.isNotEmpty ? 'Pincode: ${draft.dropPincode}' : 'Awaiting Miler input',
        toSub: 'Drop details pending',
        date: 'Today',
        amount: draft.minPrice,
        status: OrderStatus.active,
        progress: 0.1,
        eta: 'Assigning Miler...',
      );
      
      orders.insert(0, newOrder);
      draft.reset();
      notifyListeners();
      return newOrder;
    } else {
      throw Exception('Failed to create pickup request: ${res.body}');
    }
  }

  // ---- Saved addresses ----
  List<AddressEntry> savedAddresses = [
    AddressEntry(
      label: 'Home',
      line: 'Sector 45, Gurgaon, Haryana, 122003',
      icon: Icons.home_rounded,
      iconBg: AppColors.primaryFixed,
      iconColor: AppColors.primary,
      pincode: '122003',
    ),
    AddressEntry(
      label: 'Work',
      line: 'Cyber City Building 10, DLF Phase 2',
      icon: Icons.work_rounded,
      iconBg: AppColors.tertiaryFixed,
      iconColor: AppColors.tertiary,
      pincode: '122002',
    ),
    AddressEntry(
      label: 'Select City Walk Mall',
      line: 'Saket District Centre, New Delhi',
      icon: Icons.history_rounded,
      iconBg: AppColors.surfaceContainerHigh,
      iconColor: AppColors.onSurfaceVariant,
      pincode: '110017',
    ),
  ];


}
