import 'dart:io';
import 'dart:typed_data';

import 'package:electronic_assistant/base/base_controller.dart';
import 'package:electronic_assistant/data/consts/error_code.dart';
import 'package:electronic_assistant/data/repositories/talk_repository.dart';
import 'package:electronic_assistant/dialog/alert_dialog.dart';
import 'package:electronic_assistant/module/record/constants.dart';
import 'package:electronic_assistant/module/talk/view.dart';
import 'package:electronic_assistant/router/app_pages.dart';
import 'package:electronic_assistant/utils/http_handler.dart';
import 'package:electronic_assistant/utils/mmkv_util.dart';
import 'package:electronic_assistant/utils/toast_util.dart';
import 'package:get/get.dart';
import 'package:path_provider/path_provider.dart';
import 'package:record/record.dart';
import 'package:uuid/uuid.dart';

import '../../utils/pcm_wav_converter.dart';
import '../../widget/frame_animation_view.dart';

class RecordController extends BaseController {
  static const String keyLastRecordId = "last_record_id";
  static const int minRecordDuration = 3;

  final FrameAnimationController frameAnimationController =
      FrameAnimationController(autoPlay: false);
  final Rx<RecordStatus> currentStatus = RecordStatus.pending.obs;
  final RxDouble currentDuration = 0.0.obs;
  final AudioRecorder _record = AudioRecorder();
  final RecordConfig _recordConfig = const RecordConfig(
      encoder: AudioEncoder.pcm16bits,
      bitRate: 128000,
      sampleRate: 44100,
      numChannels: 2);
  late final String _lastRecordId;

  @override
  void onInit() {
    super.onInit();
    _initLastRecordId();
    _initLastRecordStatus();
  }

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

  void _initLastRecordId() {
    String? lastRecordId = KVUtil.getString(keyLastRecordId, null);
    if (lastRecordId == null || lastRecordId.isEmpty) {
      _lastRecordId = const Uuid().v4();
      KVUtil.putString(keyLastRecordId, _lastRecordId);
    } else {
      _lastRecordId = lastRecordId;
    }
  }

  Future<void> _initLastRecordStatus() async {
    var currentRecordFile = await _getCurrentRecordFile();
    var fileLength = currentRecordFile.lengthSync();
    if (currentRecordFile.existsSync() && fileLength > 0) {
      _changeRecordStatus(RecordStatus.paused);
      currentDuration.value = _getPcmDuration(
          fileLength, _recordConfig.sampleRate, 16, _recordConfig.numChannels);
    }
  }

  void addShortcut() {}

  void onBackClick() {
    if (currentStatus.value == RecordStatus.pending ||
        currentStatus.value == RecordStatus.paused) {
      Get.back();
    } else {
      EAAlertDialog.show(
        title: "是否保存当前录音？",
        confirmText: "确定",
        cancelText: "取消",
        confirmOnTap: () {
          _saveCurrentRecord();
          EAAlertDialog.dismiss();
        },
        cancelOnTap: () {
          EAAlertDialog.dismiss();
          _stopRecord().then((_) => Get.back());
        },
      );
    }
  }

  void onActionClick() {
    RecordStatus nextStatus = currentStatus.value.nextStatus;
    if (nextStatus == RecordStatus.recording) {
      _startOrContinueRecord();
    } else {
      _stopRecord();
    }
  }

  void onCancelClick() {
    if (currentStatus.value == RecordStatus.pending) {
      return;
    }
    EAAlertDialog.show(
      title: "是否删除当前录音？",
      confirmText: "删除",
      cancelText: "取消",
      confirmOnTap: () {
        _deleteCurrentRecord();
        EAAlertDialog.dismiss();
      },
      cancelOnTap: () {
        EAAlertDialog.dismiss();
      },
    );
  }

  void onSaveClick() {
    if (currentStatus.value == RecordStatus.pending) {
      return;
    }
    _saveCurrentRecord();
  }

  Future<void> _startOrContinueRecord() async {
    bool hasPermission = await _record.hasPermission();
    if (!hasPermission) {
      _onRecordPermissionDenied();
      return;
    }
    File targetFile = await _getCurrentRecordFile();
    Stream<Uint8List> recordStream = await _record.startStream(_recordConfig);
    _changeRecordStatus(RecordStatus.recording);
    recordStream.listen((data) async {
      targetFile.writeAsBytesSync(data, mode: FileMode.append);
      currentDuration.value = currentDuration.value +
          _getPcmDuration(data.length, _recordConfig.sampleRate, 16,
              _recordConfig.numChannels);
    }, onDone: () {
      _changeRecordStatus(RecordStatus.paused);
    }, onError: (error) {
      _changeRecordStatus(RecordStatus.paused);
    });
  }

  _onRecordPermissionDenied() {}

  Future<void> _stopRecord() {
    return _record
        .pause()
        .then((_) => _changeRecordStatus(RecordStatus.paused));
  }

  Future<File> _getCurrentRecordFile() async {
    Directory documentDir = await getApplicationDocumentsDirectory();
    File file = File("${documentDir.path}/.atmob/record/$_lastRecordId");
    if (!file.existsSync()) {
      file.createSync(recursive: true);
    }
    return file;
  }

  double _getPcmDuration(
      int fileSize, int sampleRate, int bitDepth, int channels) {
    final bytesPerSecond = sampleRate * (bitDepth / 8) * channels;
    final durationInSeconds = fileSize / bytesPerSecond;
    return durationInSeconds;
  }

  Future<void> _deleteCurrentRecord() async {
    await _stopRecord();
    _getCurrentRecordFile().then((file) {
      if (file.existsSync()) {
        file.deleteSync();
      }
    }).then((_) {
      currentDuration.value = 0;
      _changeRecordStatus(RecordStatus.pending);
    });
  }

  Future<void> _saveCurrentRecord() async {
    final currentDurationValue = currentDuration.value;
    if (currentDurationValue < minRecordDuration) {
      ToastUtil.showToast("录音时长不足$minRecordDuration秒");
      return;
    }
    await _stopRecord();
    talkRepository
        .talkCreate(_lastRecordId, currentDuration.value.toInt())
        .then((talkInfo) async {
      //添加新的录音到最新记录
      talkRepository.addNewTalkData(talkInfo);
      File pcmFile = await _getCurrentRecordFile();
      if (pcmFile.existsSync()) {
        File wavFile = await getRecordFile(talkInfo.id);
        PcmWavConverter.convert(pcmFile, wavFile, _recordConfig.sampleRate,
            _recordConfig.numChannels, 16);
        pcmFile.delete();
        KVUtil.putString(keyLastRecordId, "");
        Get.back();
        TalkPage.start(talkInfo);
      } else {
        throw Exception("pcm file not found");
      }
    }).catchError((error) {
      if (error is ServerErrorException) {
        ToastUtil.showToast("${error.message}");
        if (error.code == ErrorCode.errorCodeNoLogin) {
          Get.toNamed(RoutePath.login)?.then((loginSuccess) {
            loginSuccess != null && loginSuccess
                ? _saveCurrentRecord()
                : null;
          });
        }
      } else {
        ToastUtil.showToast("保存失败, 请重试");
      }
    });
  }

  void _changeRecordStatus(RecordStatus status) {
    currentStatus.value = status;
    status == RecordStatus.recording ? frameAnimationController.play() : null;
  }

  /// 获取录音文件地址
  static Future<File> getRecordFile(String talkId) async {
    Directory documentDir = await getApplicationDocumentsDirectory();
    return File("${documentDir.path}/.atmob/record/$talkId.wav");
  }

  /// 判断是否有未上传的录音
  static Future<bool> hasUnUploadRecord() async {
    String? lastRecordId = KVUtil.getString(keyLastRecordId, null);
    if (lastRecordId == null || lastRecordId.isEmpty) {
      return false;
    }
    Directory documentDir = await getApplicationDocumentsDirectory();
    File file = File("${documentDir.path}/.atmob/record/$lastRecordId");
    return await file.exists() && await file.length() > 0;
  }
}
