import 'dart:io';

import 'package:PiliPlus/common/widgets/button/icon_button.dart';
import 'package:PiliPlus/common/widgets/text_field/controller.dart';
import 'package:PiliPlus/common/widgets/text_field/text_field.dart';
import 'package:PiliPlus/models/common/image_preview_type.dart';
import 'package:PiliPlus/models_new/dynamic/dyn_mention/item.dart';
import 'package:PiliPlus/models_new/emote/emote.dart' as e;
import 'package:PiliPlus/models_new/live/live_emote/emoticon.dart';
import 'package:PiliPlus/pages/common/publish/common_publish_page.dart';
import 'package:PiliPlus/pages/dynamics_mention/view.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';

abstract class CommonRichTextPubPage
    extends CommonPublishPage<List<RichTextItem>> {
  const CommonRichTextPubPage({
    super.key,
    this.items,
    super.onSave,
    super.autofocus,
    super.imageLengthLimit,
  });

  final List<RichTextItem>? items;
}

abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
    extends CommonPublishPageState<T> {
  final key = GlobalKey<RichTextFieldState>();

  @override
  late final RichTextEditingController editController =
      RichTextEditingController(
    items: widget.items,
    onMention: onMention,
  );

  @override
  void initPubState() {
    if (editController.rawText.trim().isNotEmpty) {
      enablePublish.value = true;
    }
  }

  @override
  void didChangeDependencies() {
    editController.richStyle = null;
    super.didChangeDependencies();
  }

  Widget buildImage(int index, double height) {
    final color =
        Theme.of(context).colorScheme.secondaryContainer.withValues(alpha: 0.5);

    void onClear() {
      pathList.removeAt(index);
      if (pathList.isEmpty && editController.rawText.trim().isEmpty) {
        enablePublish.value = false;
      }
    }

    return Stack(
      clipBehavior: Clip.none,
      children: [
        GestureDetector(
          onTap: () async {
            controller.keepChatPanel();
            await context.imageView(
              imgList: pathList
                  .map((path) => SourceModel(
                        url: path,
                        sourceType: SourceType.fileImage,
                      ))
                  .toList(),
              initialPage: index,
            );
            controller.restoreChatPanel();
          },
          onLongPress: onClear,
          child: ClipRRect(
            borderRadius: const BorderRadius.all(Radius.circular(4)),
            child: Image(
              height: height,
              fit: BoxFit.fitHeight,
              filterQuality: FilterQuality.low,
              image: FileImage(File(pathList[index])),
            ),
          ),
        ),
        Positioned(
          top: 34,
          right: 5,
          child: iconButton(
            context: context,
            icon: Icons.edit,
            onPressed: () => onCropImage(index),
            size: 24,
            iconSize: 14,
            bgColor: color,
          ),
        ),
        Positioned(
          top: 5,
          right: 5,
          child: iconButton(
            context: context,
            icon: Icons.clear,
            onPressed: onClear,
            size: 24,
            iconSize: 14,
            bgColor: color,
          ),
        ),
      ],
    );
  }

  Future<void> onCropImage(int index) async {
    final theme = Theme.of(context);
    CroppedFile? croppedFile = await ImageCropper().cropImage(
      sourcePath: pathList[index],
      uiSettings: [
        AndroidUiSettings(
          toolbarTitle: '裁剪',
          toolbarColor: theme.colorScheme.secondaryContainer,
          toolbarWidgetColor: theme.colorScheme.onSecondaryContainer,
        ),
        IOSUiSettings(
          title: '裁剪',
        ),
      ],
    );
    if (croppedFile != null) {
      pathList[index] = croppedFile.path;
    }
  }

  void onPickImage([VoidCallback? callback]) {
    EasyThrottle.throttle('imagePicker', const Duration(milliseconds: 500),
        () async {
      try {
        List<XFile> pickedFiles = await imagePicker.pickMultiImage(
          limit: limit,
          imageQuality: 100,
        );
        if (pickedFiles.isNotEmpty) {
          for (int i = 0; i < pickedFiles.length; i++) {
            if (pathList.length == limit) {
              SmartDialog.showToast('最多选择$limit张图片');
              break;
            } else {
              pathList.add(pickedFiles[i].path);
            }
          }
          callback?.call();
        }
      } catch (e) {
        SmartDialog.showToast(e.toString());
      }
    });
  }

  void onChooseEmote(dynamic emote, double? width, double? height) {
    if (emote is e.Emote) {
      final isTextEmote = width == null;
      onInsertText(
        isTextEmote ? emote.text! : '\uFFFC',
        RichTextType.emoji,
        rawText: emote.text!,
        emote: isTextEmote
            ? null
            : Emote(
                url: emote.url!,
                width: width,
                height: height,
              ),
      );
    } else if (emote is Emoticon) {
      onInsertText(
        '\uFFFC',
        RichTextType.emoji,
        rawText: emote.emoji!,
        emote: Emote(
          url: emote.url!,
          width: width!,
          height: height,
        ),
      );
    }
  }

  List<Map<String, dynamic>>? getRichContent() {
    if (editController.items.isEmpty) return null;
    final list = <Map<String, dynamic>>[];
    for (var e in editController.items) {
      switch (e.type) {
        case RichTextType.text || RichTextType.composing:
          list.add({
            "raw_text": e.text,
            "type": 1,
            "biz_id": "",
          });
        case RichTextType.at:
          list.add({
            "raw_text": '@${e.rawText}',
            "type": 2,
            "biz_id": e.id,
          });
        case RichTextType.emoji:
          list.add({
            "raw_text": e.rawText,
            "type": 9,
            "biz_id": "",
          });
        case RichTextType.vote:
          list.add({
            "raw_text": e.rawText,
            "type": 4,
            "biz_id": e.id,
          });
          list.add({
            "raw_text": ' ',
            "type": 1,
            "biz_id": "",
          });
      }
    }
    return list;
  }

  double _mentionOffset = 0;
  Future<void> onMention([bool fromClick = false]) async {
    controller.keepChatPanel();
    await DynMentionPanel.onDynMention(
      context,
      offset: _mentionOffset,
      callback: (offset) => _mentionOffset = offset,
    ).then((MentionItem? res) {
      if (res != null) {
        onInsertText(
          '@${res.name} ',
          RichTextType.at,
          rawText: res.name,
          id: res.uid,
          fromClick: fromClick,
        );
      }
    });
    controller.restoreChatPanel();
  }

  void onInsertText(
    String text,
    RichTextType type, {
    String? rawText,
    Emote? emote,
    String? id,
    bool? fromClick,
  }) {
    if (text.isEmpty) {
      return;
    }

    enablePublish.value = true;

    var oldValue = editController.value;
    final selection = oldValue.selection;

    if (selection.isValid) {
      TextEditingDelta delta;

      if (selection.isCollapsed) {
        if (type == RichTextType.at && fromClick == false) {
          delta = RichTextEditingDeltaReplacement(
            oldText: oldValue.text,
            replacementText: text,
            replacedRange:
                TextRange(start: selection.start - 1, end: selection.end),
            selection: TextSelection.collapsed(
              offset: selection.start - 1 + text.length,
            ),
            composing: TextRange.empty,
            rawText: rawText,
            type: type,
            emote: emote,
            id: id,
          );
        } else {
          delta = RichTextEditingDeltaInsertion(
            oldText: oldValue.text,
            textInserted: text,
            insertionOffset: selection.start,
            selection: TextSelection.collapsed(
              offset: selection.start + text.length,
            ),
            composing: TextRange.empty,
            rawText: rawText,
            type: type,
            emote: emote,
            id: id,
          );
        }
      } else {
        delta = RichTextEditingDeltaReplacement(
          oldText: oldValue.text,
          replacementText: text,
          replacedRange: selection,
          selection: TextSelection.collapsed(
            offset: selection.start + text.length,
          ),
          composing: TextRange.empty,
          rawText: rawText,
          type: type,
          emote: emote,
          id: id,
        );
      }

      final newValue = delta.apply(oldValue);

      if (oldValue == newValue) {
        return;
      }

      editController
        ..syncRichText(delta)
        ..value = newValue;
    } else {
      editController.items
        ..clear()
        ..add(
          RichTextItem(
            type: type,
            text: text,
            rawText: rawText,
            range: TextRange(
              start: 0,
              end: text.length,
            ),
            emote: emote,
            id: id,
          ),
        );
      editController.value = TextEditingValue(
        text: text,
        selection: TextSelection.collapsed(offset: text.length),
      );
    }

    key.currentState?.scheduleShowCaretOnScreen(withAnimation: true);
  }

  @override
  void onSave() {
    widget.onSave?.call(editController.items);
  }
}
