// lib/services/nfc_service.dart
//
// NFC 封装服务（包含读取和写入方法，均提供回调与 Future 两种使用方式）
// 请将此文件放到你的项目中（例如 lib/services/nfc_service.dart）并直接使用。

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:nfc_manager/nfc_manager.dart';
import 'package:nfc_manager/ndef_record.dart';
import 'package:nfc_manager/nfc_manager_android.dart';
import 'package:nfc_manager/nfc_manager_ios.dart';
import 'package:nfc_manager_ndef/nfc_manager_ndef.dart';

/// NfcService 单例：提供 NFC 的读取与写入基础能力
///
/// - startScanOnceWithCallback(...)：回调式的一次性读取（发现标签即回调）
/// - readOnceText(...)：Future 式读取，返回解析出的文本（若失败会抛异常）
/// - writeText(...)：写入文本记录到标签（回调/返回写入结果）
/// - stopSession()：停止当前会话
///
/// 注：iOS 必须在真机测试，并在 Xcode 中打开 NFC 权限；Android 需设备支持 NFC。
class NfcService {
  NfcService._internal();

  static final NfcService instance = NfcService._internal();

  /// 是否正在进行 NFC 会话（扫描或写入）
  final ValueNotifier<bool> scanning = ValueNotifier(false);

  /// 日志广播流（方便 UI 订阅）
  final StreamController<String> _logController = StreamController.broadcast();

  Stream<String> get logs => _logController.stream;

  /// 检查设备是否支持 NFC
  Future<bool> isAvailable() => NfcManager.instance.isAvailable();

  /// 停止会话（若正在运行）
  Future<void> stopSession() async {
    if (scanning.value) {
      try {
        await NfcManager.instance.stopSession();
      } catch (_) {}
      scanning.value = false;
    }
  }

  // ---------------- 辅助方法 ----------------

  /// bytes -> "AA:BB:CC" 形式的大写十六进制字符串
  String _bytesToHex(Uint8List bytes) {
    return bytes
        .map((b) => b.toRadixString(16).padLeft(2, '0'))
        .join(':')
        .toUpperCase();
  }

  /// 递归在 Map/List 中查找第一个可用的字节数组 (List<int> / Uint8List)
  List<int>? _findFirstByteArray(dynamic value) {
    if (value == null) return null;
    if (value is Uint8List) return value.toList();
    if (value is List<int>) return value;
    if (value is List) {
      for (var item in value) {
        final found = _findFirstByteArray(item);
        if (found != null) return found;
      }
      return null;
    }
    if (value is Map) {
      for (final entry in value.entries) {
        final found = _findFirstByteArray(entry.value);
        if (found != null) return found;
      }
    }
    return null;
  }

  /// 从 tag（NfcTag / Map）中提取 UID，兼容多种实现
  String extractUidFromTag(dynamic tag) {
    try {
      dynamic raw = tag;
      // 有些实现把数据放 data 字段
      if (tag is Map && tag.containsKey('data')) raw = tag['data'];

      final bytes = _findFirstByteArray(raw);
      if (bytes != null && bytes.isNotEmpty) {
        return _bytesToHex(Uint8List.fromList(bytes));
      }

      // 再尝试一些常见的键名
      if (tag is Map) {
        final possible = ['id', 'identifier', 'Id', 'ID'];
        for (final k in possible) {
          if (tag[k] != null) {
            final b = _findFirstByteArray(tag[k]);
            if (b != null) return _bytesToHex(Uint8List.fromList(b));
          }
        }
      }

      // 最后一招：平台特定 API （如果存在）
      if (Platform.isAndroid) {
        try {
          final nfcA = NfcAAndroid.from(tag);
          if (nfcA != null && nfcA.tag.id != null)
            return _bytesToHex(Uint8List.fromList(nfcA.tag.id));
        } catch (_) {}
      } else if (Platform.isIOS) {
        try {
          final mifare = MiFareIos.from(tag);
          if (mifare != null && mifare.identifier != null)
            return _bytesToHex(Uint8List.fromList(mifare.identifier));
        } catch (_) {}
      }
    } catch (e) {
      debugPrint('extractUidFromTag error: $e');
    }
    return 'UNKNOWN';
  }

  /// 从 NDEF record 的 payload 解析文本（Text Record）并返回字符串
  String parseTextFromPayload(Uint8List payload) {
    if (payload.isEmpty) return '';
    final status = payload[0];
    final langLen = status & 0x3F;
    final textBytes = payload.sublist(1 + langLen);
    try {
      return utf8.decode(textBytes);
    } catch (e) {
      return String.fromCharCodes(textBytes);
    }
  }

  String _formatMessageToString(dynamic msg) {
    if (msg == null) return '<empty>';
    try {
      final records =
          (msg is Map && msg['records'] != null)
              ? List<dynamic>.from(msg['records'])
              : (msg is dynamic ? (msg.records as List<dynamic>?) : null);
      if (records == null || records.isEmpty) return '<empty>';
      final sb = StringBuffer();
      for (var i = 0; i < records.length; i++) {
        final r = records[i];
        sb.writeln('Record $i: ${r.toString()}');
        try {
          // 解析 payload
          Uint8List? p;
          if (r is Map && r['payload'] != null) {
            final ptmp = r['payload'];
            if (ptmp is Uint8List)
              p = ptmp;
            else if (ptmp is List<int>)
              p = Uint8List.fromList(ptmp);
          } else {
            final pr = (r as dynamic).payload;
            if (pr is Uint8List)
              p = pr;
            else if (pr is List<int>)
              p = Uint8List.fromList(pr);
          }
          if (p != null) {
            final txt = parseTextFromPayload(p);
            sb.writeln('  text: $txt');
          }
        } catch (_) {}
      }
      return sb.toString();
    } catch (e) {
      return msg.toString();
    }
  }

  // ---------------- 读取 API ----------------

  /// 回调式的一次性扫描（发现标签后立即回调）
  ///
  /// onResult(uid, parsedText, rawMessage) - 当发现标签时回调
  /// onError(error) - 出错时回调
  /// timeout - 超时时间（可选）
  Future<void> startScanOnceWithCallback({
    required void Function(String uid, String parsedText, dynamic rawMessage)
    onResult,
    void Function(Object error)? onError,
    Duration? timeout,
  }) async {
    final available = await isAvailable();
    if (!available) {
      onError?.call('NFC not available');
      return;
    }
    if (scanning.value) {
      onError?.call('Another session running');
      return;
    }

    scanning.value = true;
    _logController.add('NFC scanning started');

    Timer? timer;
    if (timeout != null) {
      timer = Timer(timeout, () async {
        await stopSession();
        _logController.add('NFC scanning timeout');
        onError?.call('timeout');
      });
    }

    try {
      await NfcManager.instance.startSession(
        onDiscovered: (dynamic tag) async {
          try {
            final uid = extractUidFromTag(tag);
            dynamic rawMsg;
            String parsedText = '';

            // 尝试用 Ndef.from 获取消息
            try {
              final ndef = Ndef.from(tag);
              if (ndef != null) {
                rawMsg = ndef.cachedMessage;
                if (rawMsg != null) {
                  // 解析第一条文本记录（若存在）
                  if ((rawMsg.records as List).isNotEmpty) {
                    final first = rawMsg.records.first;
                    final payload = first.payload;
                    if (payload is Uint8List)
                      parsedText = parseTextFromPayload(payload);
                    else if (payload is List<int>)
                      parsedText = parseTextFromPayload(
                        Uint8List.fromList(payload),
                      );
                  }
                }
              } else {
                rawMsg = null;
              }
            } catch (_) {
              // 回退：尝试从 tag map 中嗅探 cachedMessage / records
              try {
                if (tag is Map) {
                  rawMsg =
                      tag['cachedMessage'] ??
                      tag['ndef']?['cachedMessage'] ??
                      tag['message'];
                  if (rawMsg != null) {
                    final recs =
                        (rawMsg['records'] ?? (rawMsg as dynamic).records)
                            as dynamic;
                    if (recs != null && recs is List && recs.isNotEmpty) {
                      final r = recs.first;
                      final p =
                          (r is Map) ? r['payload'] : (r as dynamic).payload;
                      if (p is Uint8List)
                        parsedText = parseTextFromPayload(p);
                      else if (p is List<int>)
                        parsedText = parseTextFromPayload(
                          Uint8List.fromList(p),
                        );
                    }
                  }
                }
              } catch (_) {}
            }

            // 回调结果
            onResult(uid, parsedText, rawMsg);
            _logController.add(
              'UID: $uid\nmessage: ${_formatMessageToString(rawMsg)}',
            );

            await stopSession();
          } catch (e) {
            onError?.call(e);
            await stopSession();
          } finally {
            timer?.cancel();
            scanning.value = false;
          }
        },
        // 尽量多协议尝试以提升兼容性
        pollingOptions: {NfcPollingOption.iso14443},
      );
    } catch (e) {
      scanning.value = false;
      timer?.cancel();
      onError?.call(e);
    }
  }

  /// Future 式读取：解析第一条文本记录并返回字符串（格式：'UID\n文本'）
  Future<String> readOnceText({
    Duration timeout = const Duration(seconds: 10),
  }) async {
    final completer = Completer<String>();
    await startScanOnceWithCallback(
      onResult: (uid, parsedText, rawMsg) {
        if (parsedText.isEmpty) {
          completer.completeError('No text record found (UID: $uid)');
        } else {
          completer.complete('UID: $uid\n$parsedText');
        }
      },
      onError: (err) {
        if (!completer.isCompleted) completer.completeError(err);
      },
      timeout: timeout,
    );
    return completer.future;
  }

  // ---------------- 写入 API ----------------

  /// 构造 NDEF Text payload（status byte + lang + utf8 bytes）
  Uint8List _buildTextPayload(String text, {String lang = 'en'}) {
    final textBytes = utf8.encode(text);
    final langBytes = utf8.encode(lang);
    final status = langBytes.length & 0x3F;
    final payload = <int>[status, ...langBytes, ...textBytes];
    return Uint8List.fromList(payload);
  }

  /// 写入文本到标签（单条 Text record）
  ///
  /// - text: 要写入的文本
  /// - timeout: 超时时间（可选）
  /// - onComplete: 回调 (ok, err)
  ///
  /// 返回 true 表示写入成功（同时 onComplete 也会被触发）
  Future<bool> writeText(
    String text, {
    Duration? timeout,
    void Function(bool ok, Object? err)? onComplete,
  }) async {
    debugPrint('writeText called - scanning=${scanning.value}');
    final available = await isAvailable();
    debugPrint('writeText: isAvailable=$available');
    if (!available) {
      onComplete?.call(false, 'NFC not available');
      return false;
    }
    if (scanning.value) {
      onComplete?.call(false, 'Another session running');
      return false;
    }

    try {
      await NfcManager.instance.stopSession();
    } catch (e) {
      debugPrint('stopSession ignore: $e');
    }

    scanning.value = true;
    Timer? timer;
    if (timeout != null) {
      timer = Timer(timeout, () async {
        await stopSession();
        scanning.value = false;
        onComplete?.call(false, 'timeout');
      });
    }

    bool success = false;
    try {
      // 修改了 pollingOptions 配置
      final polling =
          Platform.isIOS
              ? {NfcPollingOption.iso14443} // iOS 使用更具体的配置
              : {
                NfcPollingOption.iso14443,
                NfcPollingOption.iso15693,
                NfcPollingOption.iso18092,
              };

      await NfcManager.instance.startSession(
        pollingOptions: polling,
        onDiscovered: (dynamic tag) async {
          timer?.cancel();
          try {
            final ndef = Ndef.from(tag);
            if (ndef == null) {
              onComplete?.call(false, 'Tag not NDEF');
              await stopSession();
              scanning.value = false;
              return;
            }

            // 检查是否可写
            if (Platform.isIOS) {
              final miFare = MiFareIos.from(tag);
              if (miFare == null) {
                onComplete?.call(false, 'Unsupported tag type on iOS');
                await stopSession();
                scanning.value = false;
                return;
              }
            }

            final payload = _buildTextPayload(text, lang: 'en');
            final record = NdefRecord(
              typeNameFormat: TypeNameFormat.wellKnown,
              type: Uint8List.fromList('T'.codeUnits),
              identifier: Uint8List(0),
              payload: payload,
            );
            final message = NdefMessage(records: [record]);

            await ndef.write(message: message);
            // 取出 UID (优先取 nfca.identifier，如果没有就取顶层 id)
            String? uid;
            if (Platform.isAndroid) {
              try {
                final nfcA = NfcAAndroid.from(tag);
                if (nfcA != null && nfcA.tag.id != null) {
                  uid = _bytesToHex(Uint8List.fromList(nfcA.tag.id));
                }
              } catch (_) {}
            } else if (Platform.isIOS) {
              try {
                final mifare = MiFareIos.from(tag);
                if (mifare != null && mifare.identifier != null) {
                  uid = _bytesToHex(Uint8List.fromList(mifare.identifier));
                }
              } catch (_) {}
            }
            success = true;
            onComplete?.call(true, uid); // ✅ 把 UID 返回出去
          } catch (e, st) {
            debugPrint('iOS NFC Write Error: $e');
            debugPrint('Stack trace: $st');
            onComplete?.call(false, e);
          } finally {
            await stopSession();
            scanning.value = false;
          }
        },
      );
    } catch (e, st) {
      debugPrint('startSession exception: $e\n$st');
      timer?.cancel();
      scanning.value = false;
      onComplete?.call(false, e);
    }
    return success;
  }

  /// 释放资源
  void dispose() {
    try {
      _logController.close();
    } catch (_) {}
  }
}
