import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart' hide FormData, MultipartFile;
import 'package:path_provider/path_provider.dart';
import '../../../core/network/dio_client.dart';

/// WiFi Scanner Controller
/// Connects to external physical scanners (eSCL/AirScan protocol)
/// over the local WiFi network, pulls scanned images, and uploads them.
///
/// Supported protocols:
/// - eSCL (AirScan) — most modern WiFi scanners (HP, Epson, Canon, Brother)
/// - Manual IP + HTTP endpoint (custom scanners)
///
/// Flow:
/// 1. Discover scanners on local network (or enter IP manually)
/// 2. Connect & configure scan settings
/// 3. Trigger scan → receive image
/// 4. Add to queue → upload to Musadaq
class WifiScannerController extends GetxController {
  // Scanner discovery
  final discoveredScanners = <Map<String, dynamic>>[].obs;
  final isScanning = false.obs;
  final connectedScanner = Rxn<Map<String, dynamic>>();
  final manualIpController = TextEditingController();

  // Scan settings
  final scanResolution = 300.obs; // DPI
  final scanColorMode = 'Color'.obs; // Color, Grayscale, BlackWhite
  final scanFormat = 'JPEG'.obs; // JPEG, PDF, PNG

  // Company selection
  final selectedCompanyId = ''.obs;
  final selectedCompanyName = ''.obs;
  final companies = <Map<String, dynamic>>[].obs;
  final isLoadingCompanies = false.obs;

  // Scan queue
  final scannedImages = <File>[].obs;
  final isScanningDocument = false.obs;
  final isUploading = false.obs;
  final uploadProgress = 0.0.obs;
  final statusMessage = 'جاهز للاتصال بالماسح الضوئي'.obs;

  // Connection
  final isConnected = false.obs;
  final connectionStatus = 'غير متصل'.obs;

  @override
  void onInit() {
    super.onInit();
    _loadCompanies();
  }

  @override
  void onClose() {
    manualIpController.dispose();
    super.onClose();
  }

  final Dio _dio = DioClient().client;

  Future<void> _loadCompanies() async {
    isLoadingCompanies.value = true;
    try {
      final res = await _dio.get('companies');
      if (res.data['success'] == true) {
        companies.value = List<Map<String, dynamic>>.from(res.data['data'] ?? []);
      }
    } catch (_) {}
    isLoadingCompanies.value = false;
  }

  void selectCompany(String id, String name) {
    selectedCompanyId.value = id;
    selectedCompanyName.value = name;
  }

  /// Discover eSCL (AirScan) scanners on the local network
  /// Uses mDNS to find _uscan._tcp and _uscans._tcp services
  Future<void> discoverScanners() async {
    isScanning.value = true;
    discoveredScanners.clear();
    statusMessage.value = 'جارٍ البحث عن الماسحات الضوئية...';

    try {
      // Method 1: Try common scanner ports on local subnet
      final localIp = await _getLocalIp();
      if (localIp != null) {
        final subnet = localIp.substring(0, localIp.lastIndexOf('.'));

        // Scan common eSCL ports (80, 443, 8080, 9095) on local subnet
        final futures = <Future>[];
        for (int i = 1; i <= 254; i++) {
          final ip = '$subnet.$i';
          futures.add(_probeScanner(ip));
        }

        // Wait with timeout
        await Future.wait(futures).timeout(
          const Duration(seconds: 10),
          onTimeout: () => [],
        );
      }

      // Method 2: Add known scanner brands' default addresses
      await _probeScanner('192.168.1.1'); // Common router/scanner
      
      if (discoveredScanners.isEmpty) {
        statusMessage.value = 'لم يتم العثور على ماسحات. أدخل IP يدوياً.';
      } else {
        statusMessage.value = 'تم العثور على ${discoveredScanners.length} ماسح ضوئي';
      }
    } catch (e) {
      statusMessage.value = 'خطأ في البحث: ${e.toString()}';
    }

    isScanning.value = false;
  }

  /// Probe a specific IP for eSCL scanner
  Future<void> _probeScanner(String ip) async {
    try {
      final client = HttpClient()..connectionTimeout = const Duration(seconds: 2);
      
      // Try eSCL endpoint
      final request = await client.getUrl(Uri.parse('http://$ip:80/eSCL/ScannerCapabilities'));
      final response = await request.close().timeout(const Duration(seconds: 2));
      
      if (response.statusCode == 200) {
        final body = await response.transform(utf8.decoder).join();
        final scannerName = _extractScannerName(body) ?? 'ماسح ضوئي ($ip)';
        
        discoveredScanners.add({
          'ip': ip,
          'port': 80,
          'name': scannerName,
          'protocol': 'eSCL',
          'capabilities': body,
        });
      }
      client.close();
    } catch (_) {
      // Not a scanner, skip silently
    }
  }

  /// Connect to scanner manually by IP
  Future<void> connectManualIp() async {
    final ip = manualIpController.text.trim();
    if (ip.isEmpty) {
      Get.snackbar('خطأ', 'أدخل عنوان IP الماسح الضوئي',
          backgroundColor: Colors.red, colorText: Colors.white);
      return;
    }

    statusMessage.value = 'جارٍ الاتصال بـ $ip...';
    isScanning.value = true;

    try {
      // Try eSCL first
      final client = HttpClient()..connectionTimeout = const Duration(seconds: 5);
      
      for (final port in [80, 443, 8080, 9095]) {
        try {
          final request = await client.getUrl(
            Uri.parse('http://$ip:$port/eSCL/ScannerCapabilities'),
          );
          final response = await request.close().timeout(const Duration(seconds: 3));
          
          if (response.statusCode == 200) {
            final body = await response.transform(utf8.decoder).join();
            final scanner = {
              'ip': ip,
              'port': port,
              'name': _extractScannerName(body) ?? 'ماسح ضوئي ($ip)',
              'protocol': 'eSCL',
              'capabilities': body,
            };
            
            connectedScanner.value = scanner;
            isConnected.value = true;
            connectionStatus.value = 'متصل بـ ${scanner['name']}';
            statusMessage.value = '✅ تم الاتصال! جاهز للمسح.';
            
            if (!discoveredScanners.any((s) => s['ip'] == ip)) {
              discoveredScanners.add(scanner);
            }
            
            client.close();
            isScanning.value = false;
            return;
          }
        } catch (_) {
          continue;
        }
      }

      // Try as generic HTTP scanner (custom endpoint)
      final scanner = {
        'ip': ip,
        'port': 80,
        'name': 'ماسح ضوئي ($ip)',
        'protocol': 'http',
      };
      
      connectedScanner.value = scanner;
      isConnected.value = true;
      connectionStatus.value = 'متصل بـ $ip (HTTP عام)';
      statusMessage.value = '✅ تم الاتصال (HTTP). جرّب المسح.';
      
      client.close();
    } catch (e) {
      statusMessage.value = '❌ فشل الاتصال بـ $ip';
      isConnected.value = false;
    }

    isScanning.value = false;
  }

  /// Connect to a discovered scanner
  void connectToScanner(Map<String, dynamic> scanner) {
    connectedScanner.value = scanner;
    isConnected.value = true;
    connectionStatus.value = 'متصل بـ ${scanner['name']}';
    statusMessage.value = '✅ تم الاتصال! اختر شركة ثم ابدأ المسح.';
  }

  /// Trigger a scan on the connected scanner
  Future<void> triggerScan() async {
    if (!isConnected.value || connectedScanner.value == null) {
      Get.snackbar('خطأ', 'يجب الاتصال بماسح ضوئي أولاً',
          backgroundColor: Colors.red, colorText: Colors.white);
      return;
    }

    isScanningDocument.value = true;
    statusMessage.value = 'جارٍ المسح...';

    try {
      final scanner = connectedScanner.value!;
      final ip = scanner['ip'];
      final port = scanner['port'];
      final protocol = scanner['protocol'];

      Uint8List? imageBytes;

      if (protocol == 'eSCL') {
        imageBytes = await _esclScan(ip, port);
      } else {
        imageBytes = await _httpScan(ip, port);
      }

      if (imageBytes != null && imageBytes.isNotEmpty) {
        // Save to temp file
        final dir = await getTemporaryDirectory();
        final fileName = 'scan_${DateTime.now().millisecondsSinceEpoch}.jpg';
        final file = File('${dir.path}/$fileName');
        await file.writeAsBytes(imageBytes);

        scannedImages.add(file);
        statusMessage.value = '✅ تم المسح! (${scannedImages.length} صورة في الطابور)';

        Get.snackbar('✅ تم المسح', 'الصورة أُضيفت للطابور',
            backgroundColor: const Color(0xFF10B981), colorText: Colors.white,
            snackPosition: SnackPosition.TOP);
      } else {
        statusMessage.value = '⚠️ لم يتم استلام صورة من الماسح';
      }
    } catch (e) {
      statusMessage.value = '❌ خطأ أثناء المسح: ${e.toString()}';
    }

    isScanningDocument.value = false;
  }

  /// eSCL scan request
  Future<Uint8List?> _esclScan(String ip, int port) async {
    final client = HttpClient()..connectionTimeout = const Duration(seconds: 30);

    // 1. Create scan job
    final scanSettings = '''<?xml version="1.0" encoding="UTF-8"?>
<scan:ScanSettings xmlns:scan="http://schemas.hp.com/imaging/escl/2011/05/03"
                   xmlns:pwg="http://www.pwg.org/schemas/2010/12/sm">
  <pwg:Version>2.0</pwg:Version>
  <scan:Intent>TextAndGraphic</scan:Intent>
  <pwg:ScanRegions>
    <pwg:ScanRegion>
      <pwg:ContentRegionUnits>escl:ThreeHundredthsOfInches</pwg:ContentRegionUnits>
      <pwg:Height>3507</pwg:Height>
      <pwg:Width>2481</pwg:Width>
      <pwg:XOffset>0</pwg:XOffset>
      <pwg:YOffset>0</pwg:YOffset>
    </pwg:ScanRegion>
  </pwg:ScanRegions>
  <pwg:InputSource>Platen</pwg:InputSource>
  <scan:ColorMode>${scanColorMode.value == 'Color' ? 'RGB24' : 'Grayscale8'}</scan:ColorMode>
  <scan:XResolution>${scanResolution.value}</scan:XResolution>
  <scan:YResolution>${scanResolution.value}</scan:YResolution>
  <pwg:DocumentFormat>image/jpeg</pwg:DocumentFormat>
</scan:ScanSettings>''';

    final postRequest = await client.postUrl(
      Uri.parse('http://$ip:$port/eSCL/ScanJobs'),
    );
    postRequest.headers.set('Content-Type', 'text/xml');
    postRequest.write(scanSettings);
    final postResponse = await postRequest.close();

    if (postResponse.statusCode != 201) {
      client.close();
      return null;
    }

    // 2. Get scan job URL from Location header
    final jobUrl = postResponse.headers.value('location');
    if (jobUrl == null) {
      client.close();
      return null;
    }

    // 3. Wait for scan to complete, then fetch image
    await Future.delayed(const Duration(seconds: 3));

    final imageUrl = jobUrl.endsWith('/') 
        ? '${jobUrl}NextDocument' 
        : '$jobUrl/NextDocument';
    
    final getRequest = await client.getUrl(Uri.parse(imageUrl));
    final getResponse = await getRequest.close();

    if (getResponse.statusCode == 200) {
      final bytes = await getResponse.fold<List<int>>(
        <int>[],
        (list, chunk) => list..addAll(chunk),
      );
      client.close();
      return Uint8List.fromList(bytes);
    }

    client.close();
    return null;
  }

  /// Generic HTTP scan (for custom scanner endpoints)
  Future<Uint8List?> _httpScan(String ip, int port) async {
    final client = HttpClient()..connectionTimeout = const Duration(seconds: 15);

    // Try common scan endpoints
    for (final endpoint in ['/scan', '/capture', '/api/scan', '/cgi-bin/scan']) {
      try {
        final request = await client.getUrl(
          Uri.parse('http://$ip:$port$endpoint'),
        );
        final response = await request.close().timeout(const Duration(seconds: 10));

        if (response.statusCode == 200) {
          final contentType = response.headers.contentType?.mimeType ?? '';
          if (contentType.startsWith('image/')) {
            final bytes = await response.fold<List<int>>(
              <int>[],
              (list, chunk) => list..addAll(chunk),
            );
            client.close();
            return Uint8List.fromList(bytes);
          }
        }
      } catch (_) {
        continue;
      }
    }

    client.close();
    return null;
  }

  /// Upload all scanned images to Musadaq
  Future<void> uploadQueue() async {
    if (scannedImages.isEmpty || selectedCompanyId.value.isEmpty) {
      Get.snackbar('تنبيه', 'اختر شركة وامسح فواتير أولاً',
          backgroundColor: Colors.orange, colorText: Colors.white);
      return;
    }

    isUploading.value = true;
    uploadProgress.value = 0;
    final total = scannedImages.length;

    try {
      for (int i = 0; i < total; i++) {
        statusMessage.value = 'جارٍ رفع الصورة ${i + 1} من $total...';

        final file = scannedImages[i];
        final fileName = file.path.split('/').last;
        final formData = FormData.fromMap({
          'company_id': selectedCompanyId.value,
          'file': await MultipartFile.fromFile(file.path, filename: fileName),
        });

        await _dio.post('invoices/upload', data: formData);

        uploadProgress.value = (i + 1) / total;
      }

      Get.snackbar('✅ تم!', 'تم رفع $total فاتورة بنجاح',
          backgroundColor: const Color(0xFF10B981), colorText: Colors.white,
          snackPosition: SnackPosition.TOP);

      scannedImages.clear();
      statusMessage.value = '✅ تم رفع جميع الفواتير. جاهز لمسح جديد.';
    } catch (e) {
      statusMessage.value = '❌ خطأ أثناء الرفع: ${e.toString()}';
      Get.snackbar('خطأ', 'فشل الرفع', 
          backgroundColor: Colors.red, colorText: Colors.white);
    }

    isUploading.value = false;
  }

  void removeImage(int index) {
    if (index >= 0 && index < scannedImages.length) {
      scannedImages.removeAt(index);
    }
  }

  void clearQueue() {
    scannedImages.clear();
    statusMessage.value = 'تم مسح الطابور';
  }

  void disconnect() {
    connectedScanner.value = null;
    isConnected.value = false;
    connectionStatus.value = 'غير متصل';
    statusMessage.value = 'تم قطع الاتصال';
  }

  /// Get local IP address
  Future<String?> _getLocalIp() async {
    try {
      final interfaces = await NetworkInterface.list(
        type: InternetAddressType.IPv4,
        includeLinkLocal: false,
      );
      for (final interface in interfaces) {
        for (final addr in interface.addresses) {
          if (!addr.isLoopback) return addr.address;
        }
      }
    } catch (_) {}
    return null;
  }

  /// Extract scanner name from eSCL capabilities XML
  String? _extractScannerName(String xml) {
    final match = RegExp(r'<pwg:MakeAndModel>(.*?)</pwg:MakeAndModel>').firstMatch(xml);
    return match?.group(1);
  }
}
