import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:recording_tool/core/logging/app_logger.dart';
import 'package:recording_tool/core/permission/permission_service.dart';
import 'package:recording_tool/features/recording/model/model_clipboard.dart';
import 'package:recording_tool/features/recording/model/model_recording.dart';
import 'package:recording_tool/features/recording/model/model_recording_session.dart';
import 'package:recording_tool/features/recording/platform/recording_platform.dart';
import 'package:recording_tool/features/recording/utils/recording_display_name.dart';

/// 录制页状态 Provider。
final recordingViewModelProvider =
    NotifierProvider<RecordingViewModel, RecordingModel>(
      RecordingViewModel.new,
    );

/// 剪切板读取结果，供 UI 决定是否提示用户。
enum ClipboardReadResult {
  /// 剪切板为空，不提示
  empty,

  /// 解析成功
  success,

  /// 有内容但格式不符合小程序录制信息
  invalid,
}

List<Permission> recordingGalleryPermissionsForHost({
  required bool isIOS,
  required bool isAndroid,
}) {
  if (isIOS) {
    return [Permission.photosAddOnly];
  }
  if (isAndroid) {
    return [Permission.videos, Permission.storage];
  }
  return const [];
}

/// 开始录制所需的相机/麦克风权限检测结果。
class RecordingRequiredPermissions {
  const RecordingRequiredPermissions({
    required this.cameraGranted,
    required this.microphoneGranted,
  });

  final bool cameraGranted;
  final bool microphoneGranted;

  bool get allGranted => cameraGranted && microphoneGranted;
}

/// 录制页 ViewModel：剪贴板、权限、相机预览与录制流程。
class RecordingViewModel extends Notifier<RecordingModel> {
  static final _defaultClipboard = ClipboardRecordingModel(
    title: '',
    address: '',
  );

  StreamSubscription<RecordingStatus>? _statusSubscription;

  /// 初始化状态并注册销毁回调。
  @override
  RecordingModel build() {
    ref.onDispose(_dispose);
    return RecordingModel(clipboardRecordingModel: _defaultClipboard);
  }

  /// 局部更新 session 子状态。
  void _updateSession(
    RecordingSessionState Function(RecordingSessionState session) update,
  ) {
    state = state.copyWith(session: update(state.session));
  }

  /// 读取并解析剪贴板中的小程序录制信息。
  Future<ClipboardReadResult> getClipboardContent() async {
    try {
      final clipboardData = await Clipboard.getData(Clipboard.kTextPlain);
      final text = clipboardData?.text;
      AppLogger.debug('读取剪切板内容：$text');

      if (text == null || text.trim().isEmpty) {
        AppLogger.info('剪切板内容为空，跳过录制信息解析');
        _resetClipboardInfo();
        return ClipboardReadResult.empty;
      }

      final decoded = jsonDecode(text.trim());
      if (decoded is! Map<String, dynamic>) {
        AppLogger.warning('剪切板内容不是 JSON 对象，跳过录制信息解析');
        _resetClipboardInfo();
        return ClipboardReadResult.invalid;
      }

      final clipboardRecordingModel = ClipboardRecordingModel.fromJson(decoded);
      if (clipboardRecordingModel.title.trim().isEmpty) {
        AppLogger.warning('剪切板录制信息缺少有效 title');
        _resetClipboardInfo();
        return ClipboardReadResult.invalid;
      }

      state = state.copyWith(
        clipboardRecordingModel: clipboardRecordingModel,
        hasValidClipboardInfo: true,
      );
      AppLogger.info('剪切板录制信息解析成功：${clipboardRecordingModel.toJson()}');
      return ClipboardReadResult.success;
    } on FormatException catch (error) {
      AppLogger.warning('剪切板录制信息格式错误：$error');
      _resetClipboardInfo();
      return ClipboardReadResult.invalid;
    } catch (error, stackTrace) {
      AppLogger.debug('读取剪切板录制信息失败', error: error, stackTrace: stackTrace);
      _resetClipboardInfo();
      return ClipboardReadResult.invalid;
    }
  }

  /// 清空剪贴板赛事信息（供 UI 调用）。
  void resetClipboardInfo() {
    _resetClipboardInfo();
  }

  /// 重置剪贴板赛事信息为默认空值。
  void _resetClipboardInfo() {
    state = state.copyWith(
      clipboardRecordingModel: _defaultClipboard,
      hasValidClipboardInfo: false,
    );
  }

  /// 申请权限、检查系统设置并初始化相机预览。
  Future<void> prepareSession() async {
    if (!RecordingPlatform.isSupported) {
      _updateSession((s) => s.copyWith(errorMessage: '当前设备不支持录制'));
      return;
    }

    final permissions = await PermissionService.requestMissing([
      Permission.camera,
      Permission.microphone,
      if (Platform.isAndroid) Permission.notification,
      ..._galleryPermissions(),
    ]);

    final cameraGranted = permissions[Permission.camera]?.isGranted ?? false;
    if (!cameraGranted) {
      _updateSession((s) => s.copyWith(errorMessage: '需要相机权限才能录制'));
      return;
    }

    final microphoneGranted =
        permissions[Permission.microphone]?.isGranted ?? false;
    final notificationsGranted = Platform.isAndroid
        ? (permissions[Permission.notification]?.isGranted ?? false)
        : true;

    final warnings = <String>[];
    if (Platform.isAndroid && !notificationsGranted) {
      warnings.add('未授予通知权限，录制时可能看不到前台服务通知，系统更容易结束后台录制');
    }
    if (!microphoneGranted) {
      warnings.add('未授予麦克风权限，当前将以静音模式录制');
    }
    if (!_isGalleryPermissionGranted(permissions)) {
      warnings.add('未授予相册权限，录制结束后可能无法保存到相册');
    }

    final hasDnd = await RecordingPlatform.hasNotificationPolicyAccess();
    final batteryIgnored =
        await RecordingPlatform.isIgnoringBatteryOptimizations();

    _updateSession(
      (s) => s.copyWith(
        hasDndAccess: hasDnd,
        isBatteryOptimizedIgnored: batteryIgnored,
        isMicrophoneGranted: microphoneGranted,
        notificationsGranted: notificationsGranted,
        permissionWarning: warnings.isEmpty ? null : warnings.join('\n'),
        errorMessage: null,
        clearPermissionWarning: warnings.isEmpty,
      ),
    );

    await _listenStatus();
    try {
      final status = await _initializePreviewWithRetry();
      await _refreshZoomCapabilities();
      _updateSession(
        (s) => s.copyWith(
          status: status,
          isPreviewReady: status.state == RecordingState.previewing,
          errorMessage: status.state == RecordingState.previewing
              ? null
              : (status.message ?? '相机预览初始化失败'),
        ),
      );
    } on PlatformException catch (error) {
      _updateSession(
        (s) => s.copyWith(
          isPreviewReady: false,
          errorMessage: error.message ?? '相机预览初始化失败',
        ),
      );
    }
  }

  /// 初始化相机预览，PlatformView 未就绪时自动重试。
  Future<RecordingStatus> _initializePreviewWithRetry() async {
    const maxAttempts = 8;
    for (var attempt = 0; attempt < maxAttempts; attempt++) {
      try {
        return await RecordingPlatform.initializePreview();
      } on PlatformException catch (error) {
        final shouldRetry =
            error.code == 'NO_PREVIEW' && attempt < maxAttempts - 1;
        if (!shouldRetry) {
          rethrow;
        }
        await Future<void>.delayed(Duration(milliseconds: 150 * (attempt + 1)));
      }
    }
    throw StateError('initializePreview retry exhausted');
  }

  /// 停止录制后重新绑定相机预览，并显示加载遮罩。
  Future<void> restorePreview() async {
    if (!RecordingPlatform.isSupported) return;

    _updateSession(
      (s) => s.copyWith(isPreviewReady: false, errorMessage: null),
    );
    try {
      final status = await _initializePreviewWithRetry();
      await _refreshZoomCapabilities();
      _updateSession(
        (s) => s.copyWith(
          status: status,
          isPreviewReady: status.state == RecordingState.previewing,
          errorMessage: status.state == RecordingState.previewing
              ? null
              : (status.message ?? '相机预览初始化失败'),
        ),
      );
    } on PlatformException catch (error) {
      _updateSession(
        (s) => s.copyWith(
          isPreviewReady: false,
          errorMessage: error.message ?? '相机预览初始化失败',
        ),
      );
    }
  }

  /// 当前平台所需的相册/视频保存权限列表。
  List<Permission> _galleryPermissions() {
    return recordingGalleryPermissionsForHost(
      isIOS: Platform.isIOS,
      isAndroid: Platform.isAndroid,
    );
  }

  /// 判断相册相关权限是否至少有一项已授予。
  bool _isGalleryPermissionGranted(
    Map<Permission, PermissionStatus> permissions,
  ) {
    for (final permission in _galleryPermissions()) {
      if (permissions[permission]?.isGranted ?? false) {
        return true;
      }
    }
    return _galleryPermissions().isEmpty;
  }

  /// 检测并尝试申请相机、麦克风权限，同步更新 session 中的 isMicrophoneGranted。
  Future<RecordingRequiredPermissions>
  ensureCameraAndMicrophonePermissions() async {
    final permissions = await PermissionService.requestMissing([
      Permission.camera,
      Permission.microphone,
    ]);

    final cameraGranted = _isPermissionGranted(permissions[Permission.camera]);
    final microphoneGranted = _isPermissionGranted(
      permissions[Permission.microphone],
    );

    _updateSession((s) => s.copyWith(isMicrophoneGranted: microphoneGranted));

    if (cameraGranted && !state.session.isPreviewReady) {
      _updateSession((s) => s.copyWith(errorMessage: null));
      await _listenStatus();
      await restorePreview();
    }

    return RecordingRequiredPermissions(
      cameraGranted: cameraGranted,
      microphoneGranted: microphoneGranted,
    );
  }

  bool _isPermissionGranted(PermissionStatus? status) {
    return status?.isGranted == true || status?.isLimited == true;
  }

  /// 读取相机支持的倍距范围并同步当前倍距。
  Future<void> _refreshZoomCapabilities() async {
    try {
      final zoom = await RecordingPlatform.getZoomCapabilities();
      _updateSession(
        (s) => s.copyWith(
          zoomRatio: zoom.zoomRatio,
          minZoomRatio: zoom.minZoomRatio,
          maxZoomRatio: zoom.maxZoomRatio,
          errorMessage: null,
        ),
      );
    } on PlatformException catch (error) {
      AppLogger.debug('读取相机倍距能力失败', error: error);
    }
  }

  /// 设置相机倍距，原生层会返回设备实际应用后的倍距范围与当前值。
  Future<void> setZoomRatio(double ratio) async {
    final session = state.session;
    final clamped = ratio
        .clamp(session.minZoomRatio, session.maxZoomRatio)
        .toDouble();

    try {
      final zoom = await RecordingPlatform.setZoomRatio(clamped);
      _updateSession(
        (s) => s.copyWith(
          zoomRatio: zoom.zoomRatio,
          minZoomRatio: zoom.minZoomRatio,
          maxZoomRatio: zoom.maxZoomRatio,
          errorMessage: null,
        ),
      );
    } on PlatformException catch (error) {
      _updateSession(
        (s) => s.copyWith(errorMessage: error.message ?? '相机倍距设置失败'),
      );
    }
  }

  /// 开始录制，可选开启勿扰模式。
  Future<void> startRecording({bool enableDoNotDisturb = true}) async {
    final session = state.session;
    if (session.isRecording || session.isStartingRecording) {
      return;
    }
    if (!session.isPreviewReady) {
      _updateSession((s) => s.copyWith(errorMessage: '相机预览未就绪，请稍后重试'));
      return;
    }

    final displayName = recordingFileNameForPlatform(
      state.clipboardRecordingModel.filename,
    );

    _updateSession(
      (s) => s.copyWith(isStartingRecording: true, errorMessage: null),
    );
    try {
      final result = await RecordingPlatform.startRecording(
        enableDoNotDisturb: enableDoNotDisturb && state.session.hasDndAccess,
        displayName: displayName,
      );
      _updateSession(
        (s) => s.copyWith(
          status: result.status,
          lastOutputPath: result.outputPath,
          isTouchLocked: true,
          errorMessage: null,
          gallerySaveFailed: false,
          clearLastSaved: true,
        ),
      );
    } on PlatformException catch (error) {
      _updateSession(
        (s) => s.copyWith(errorMessage: error.message ?? '开始录制失败'),
      );
    } finally {
      _updateSession((s) => s.copyWith(isStartingRecording: false));
    }
  }

  /// 停止录制、保存到相册，并恢复相机预览。
  Future<void> stopRecording() async {
    if (!state.session.isRecording) return;

    try {
      final result = await RecordingPlatform.stopRecording();
      final galleryFailed = !result.gallerySaved;
      final savedName = recordingFileNameForPlatform(
        state.clipboardRecordingModel.filename,
      );
      _updateSession(
        (s) => s.copyWith(
          status: result.status,
          lastOutputPath: result.outputPath ?? s.lastOutputPath,
          lastSavedDisplayName: galleryFailed ? null : savedName,
          errorMessage: galleryFailed
              ? (result.galleryErrorMessage ?? '保存到相册失败，请开启相册权限')
              : null,
          gallerySaveFailed: galleryFailed,
        ),
      );
    } on PlatformException catch (error) {
      _updateSession(
        (s) => s.copyWith(errorMessage: error.message ?? '停止录制失败'),
      );
    } finally {
      await restorePreview();
    }
  }

  /// 切换录制中触屏锁定状态。
  void setTouchLocked(bool locked) {
    _updateSession((s) => s.copyWith(isTouchLocked: locked));
  }

  /// 清除上次保存成功的录制结果标记。
  void clearSavedRecordingResult() {
    _updateSession((s) => s.copyWith(clearLastSaved: true));
  }

  /// 跳转系统勿扰/通知策略设置页。
  Future<void> openDndSettings() =>
      RecordingPlatform.openNotificationPolicySettings();

  /// 重新检测勿扰模式权限并更新状态。
  Future<void> refreshDndAccess() async {
    final hasDnd = await RecordingPlatform.hasNotificationPolicyAccess();
    _updateSession((s) => s.copyWith(hasDndAccess: hasDnd));
  }

  /// 跳转电池优化白名单设置页。
  Future<void> openBatterySettings() =>
      RecordingPlatform.openBatteryOptimizationSettings();

  /// 重新检测是否已忽略电池优化并更新状态。
  Future<void> refreshBatteryOptimization() async {
    final ignored = await RecordingPlatform.isIgnoringBatteryOptimizations();
    _updateSession((s) => s.copyWith(isBatteryOptimizedIgnored: ignored));
  }

  /// 退出录制页时释放相机、勿扰和状态订阅。
  Future<void> teardown() async {
    await RecordingPlatform.setImmersiveMode(enabled: false);
    await RecordingPlatform.disableDoNotDisturb();
    await RecordingPlatform.disposePreview();
    await _statusSubscription?.cancel();
    _statusSubscription = null;
    state = state.copyWith(session: const RecordingSessionState());
  }

  /// 订阅原生层录制状态流并同步到 session。
  Future<void> _listenStatus() async {
    await _statusSubscription?.cancel();
    _statusSubscription = RecordingPlatform.statusStream().listen((status) {
      _updateSession((s) => s.copyWith(status: status));
    });
  }

  /// Provider 销毁时取消状态流订阅。
  Future<void> _dispose() async {
    await _statusSubscription?.cancel();
  }
}
