import 'package:flutter/material.dart' hide Visibility;
import 'package:go_router/go_router.dart';
import 'package:logging/logging.dart';
import 'package:multi_trigger_autocomplete/multi_trigger_autocomplete.dart';
import 'package:provider/provider.dart';
import 'package:result_monad/result_monad.dart';
import 'package:uuid/uuid.dart';

import '../controls/autocomplete/hashtag_autocomplete_options.dart';
import '../controls/autocomplete/mention_autocomplete_options.dart';
import '../controls/entry_media_attachments/gallery_selector_control.dart';
import '../controls/entry_media_attachments/media_uploads_control.dart';
import '../controls/html_text_viewer_control.dart';
import '../controls/login_aware_cached_network_image.dart';
import '../controls/padding.dart';
import '../controls/standard_appbar.dart';
import '../controls/timeline/status_header_control.dart';
import '../controls/visibility_dialog.dart';
import '../globals.dart';
import '../models/circle_data.dart';
import '../models/exec_error.dart';
import '../models/image_entry.dart';
import '../models/link_preview_data.dart';
import '../models/media_attachment_uploads/new_entry_media_items.dart';
import '../models/timeline_entry.dart';
import '../models/visibility.dart';
import '../serializers/friendica/link_preview_friendica_extensions.dart';
import '../services/connections_manager.dart';
import '../services/feature_version_checker.dart';
import '../services/timeline_manager.dart';
import '../utils/active_profile_selector.dart';
import '../utils/html_to_edit_text_helper.dart';
import '../utils/opengraph_preview_grabber.dart';
import '../utils/snackbar_builder.dart';
import '../utils/string_utils.dart';

class EditorScreen extends StatefulWidget {
  final String id;
  final String parentId;
  final bool forEditing;

  const EditorScreen(
      {super.key, this.id = '', this.parentId = '', required this.forEditing});

  @override
  State<EditorScreen> createState() => _EditorScreenState();
}

class _EditorScreenState extends State<EditorScreen> {
  static final _logger = Logger('$EditorScreen');
  final contentController = TextEditingController();
  final spoilerController = TextEditingController();
  final localEntryTemporaryId = const Uuid().v4();
  TimelineEntry? parentEntry;
  final linkPreviewController = TextEditingController();
  LinkPreviewData? linkPreviewData;
  final newMediaItems = NewEntryMediaItems();
  final existingMediaItems = <ImageEntry>[];
  final focusNode = FocusNode();
  Visibility visibility = Visibility.public();
  CircleData? currentCircle;

  var isSubmitting = false;

  bool get isComment => widget.parentId.isNotEmpty;

  String get statusType => widget.parentId.isEmpty ? 'Post' : 'Comment';

  String get localEntryId =>
      widget.id.isNotEmpty ? widget.id : localEntryTemporaryId;

  bool loaded = false;

  @override
  void initState() {
    super.initState();
    if (isComment) {
      final manager = context
          .read<ActiveProfileSelector<TimelineManager>>()
          .activeEntry
          .value;
      manager.getEntryById(widget.parentId).match(onSuccess: (entry) {
        spoilerController.text = entry.spoilerText;
        parentEntry = entry;
        visibility = entry.visibility;
      }, onError: (error) {
        _logger.severe('Error trying to get parent entry: $error');
      });
    }

    if (widget.forEditing) {
      restoreStatusData();
    } else {
      loaded = true;
    }
  }

  void restoreStatusData() async {
    _logger.finer('Attempting to load status for editing');
    loaded = false;
    final result = await getIt<ActiveProfileSelector<TimelineManager>>()
        .activeEntry
        .andThenAsync((manager) async => manager.getEntryById(widget.id));
    result.match(onSuccess: (entry) {
      _logger.finer('Loading status ${widget.id} information into fields');
      contentController.text = htmlToSimpleText(entry.body);
      spoilerController.text = entry.spoilerText;
      existingMediaItems
          .addAll(entry.mediaAttachments.map((e) => e.toImageEntry()));
      if (entry.linkPreviewData?.link.isNotEmpty ?? false) {
        restoreLinkPreviewData(entry.linkPreviewData!);
      }
      visibility = entry.visibility;
      setState(() {
        loaded = true;
      });
    }, onError: (error) {
      buildSnackbar(context, 'Error getting post for editing: $error');
      logError(error, _logger);
    });
  }

  void restoreLinkPreviewData(LinkPreviewData preview) {
    linkPreviewController.text = preview.link;
    linkPreviewData = preview;
    Future.delayed(const Duration(seconds: 1), () async {
      final updatedPreview = await getLinkPreview(preview.link);
      if (updatedPreview.isSuccess) {
        linkPreviewData = linkPreviewData?.copy(
            availableImageUrls: updatedPreview.value.availableImageUrls);
        setState(() {});
      }
    });
  }

  String get bodyText =>
      '${contentController.text} ${linkPreviewData?.toBodyAttachment() ?? ''}';

  bool get isEmptyPost =>
      bodyText.isEmpty &&
      existingMediaItems.isEmpty &&
      newMediaItems.attachments.isEmpty;

  Future<void> createStatus(
      BuildContext context, TimelineManager manager) async {
    if (isSubmitting) {
      return;
    }

    if (isEmptyPost) {
      buildSnackbar(context, "Can't submit an empty $statusType");
      return;
    }

    setState(() {
      isSubmitting = true;
    });

    final result = await manager
        .createNewStatus(
      bodyText,
      spoilerText: spoilerController.text,
      inReplyToId: widget.parentId,
      newMediaItems: newMediaItems,
      existingMediaItems: existingMediaItems,
      visibility: visibility,
    )
        .withError((error) {
      buildSnackbar(context, 'Error posting: $error');
      logError(error, _logger);
    });

    setState(() {
      isSubmitting = false;
    });

    if (result.isFailure) {
      return;
    }

    if (mounted && context.canPop()) {
      context.pop();
    }
  }

  Future<void> editStatus(BuildContext context, TimelineManager manager) async {
    if (isSubmitting) {
      return;
    }

    if (isEmptyPost) {
      buildSnackbar(context, "Can't submit an empty $statusType");
      return;
    }

    setState(() {
      isSubmitting = true;
    });

    final result = await manager
        .editStatus(
      widget.id,
      bodyText,
      spoilerText: spoilerController.text,
      inReplyToId: widget.parentId,
      newMediaItems: newMediaItems,
      existingMediaItems: existingMediaItems,
      newMediaItemVisibility: visibility,
    )
        .withError((error) {
      buildSnackbar(context, 'Error updating $statusType: $error');
      logError(error, _logger);
    });

    setState(() {
      isSubmitting = false;
    });

    if (result.isFailure) {
      return;
    }

    if (mounted && context.canPop()) {
      context.pop();
    }
  }

  @override
  Widget build(BuildContext context) {
    _logger.finer('Build editor $isComment $parentEntry');
    final manager = context
        .read<ActiveProfileSelector<TimelineManager>>()
        .activeEntry
        .value;

    final vc = getIt<FriendicaVersionChecker>();
    final canEdit = vc.canUseFeature(RelaticaFeatures.statusEditing);
    final canSpoilerText = vc.canUseFeature(RelaticaFeatures.postSpoilerText) ||
        widget.parentId.isNotEmpty;

    late final Widget body;

    if (widget.forEditing && !canEdit) {
      body = Center(
        child: Column(
          children: [
            Text(vc.versionErrorString(RelaticaFeatures.statusEditing)),
            const VerticalPadding(),
            ElevatedButton(
                onPressed: () => context.pop(), child: const Text('Back')),
          ],
        ),
      );
    } else {
      final mainBody = Padding(
        padding: const EdgeInsets.all(8.0),
        child: Container(
          child: SingleChildScrollView(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisAlignment: MainAxisAlignment.start,
              children: [
                if (isComment && parentEntry != null)
                  buildCommentPreview(context, parentEntry!),
                TextFormField(
                  readOnly: isSubmitting,
                  enabled: !isSubmitting && canSpoilerText,
                  controller: spoilerController,
                  textCapitalization: TextCapitalization.sentences,
                  decoration: InputDecoration(
                    labelText: canSpoilerText
                        ? '$statusType Spoiler Text (optional)'
                        : 'Your server doesnt support $statusType Spoiler Text',
                    border: OutlineInputBorder(
                      borderSide: const BorderSide(),
                      borderRadius: BorderRadius.circular(5.0),
                    ),
                  ),
                ),
                const VerticalPadding(),
                buildVisibilitySelector(context),
                const VerticalPadding(),
                buildContentField(context),
                const VerticalPadding(),
                buildLinkWithPreview(context),
                const VerticalPadding(),
                GallerySelectorControl(
                  entries: existingMediaItems,
                  visibilityFilter: visibility,
                ),
                const VerticalPadding(),
                MediaUploadsControl(
                  entryMediaItems: newMediaItems,
                ),
                buildButtonBar(context, manager),
              ],
            ),
          ),
        ),
      );

      if (widget.forEditing && !loaded) {
        body = buildBusyBody(context, mainBody, 'Loading status');
      } else if (isSubmitting) {
        body = buildBusyBody(context, mainBody, 'Submitting $statusType');
      } else {
        body = mainBody;
      }
    }

    return Scaffold(
      appBar: StandardAppBar.build(
        context,
        widget.id.isEmpty ? 'New $statusType' : 'Edit $statusType',
        withDrawer: true,
        withHome: !isSubmitting,
      ),
      body: body,
    );
  }

  Widget buildContentField(BuildContext context) {
    return MultiTriggerAutocomplete(
      textEditingController: contentController,
      focusNode: focusNode,
      optionsAlignment: OptionsAlignment.top,
      autocompleteTriggers: [
        AutocompleteTrigger(
          trigger: '@',
          optionsViewBuilder: (context, autocompleteQuery, controller) {
            return MentionAutocompleteOptions(
              query: autocompleteQuery.query,
              onMentionUserTap: (user) {
                final autocomplete = MultiTriggerAutocomplete.of(context);
                return autocomplete.acceptAutocompleteOption(user.handle);
              },
            );
          },
        ),
        AutocompleteTrigger(
          trigger: '#',
          optionsViewBuilder: (context, autocompleteQuery, controller) {
            return HashtagAutocompleteOptions(
              query: autocompleteQuery.query,
              onHashtagTap: (hashtag) {
                final autocomplete = MultiTriggerAutocomplete.of(context);
                return autocomplete.acceptAutocompleteOption(hashtag);
              },
            );
          },
        ),
      ],
      fieldViewBuilder: (context, controller, focusNode) => TextFormField(
        focusNode: focusNode,
        readOnly: isSubmitting,
        enabled: !isSubmitting,
        textCapitalization: TextCapitalization.sentences,
        maxLines: 10,
        controller: controller,
        decoration: InputDecoration(
          labelText: '$statusType Content',
          alignLabelWithHint: true,
          border: OutlineInputBorder(
            borderSide: const BorderSide(),
            borderRadius: BorderRadius.circular(5.0),
          ),
        ),
      ),
    );
  }

  Widget buildCommentPreview(BuildContext context, TimelineEntry entry) {
    _logger.finest('Build preview');
    return Column(
      mainAxisAlignment: MainAxisAlignment.start,
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          'Comment for status: ',
          style: Theme.of(context).textTheme.bodyLarge,
        ),
        Card(
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                StatusHeaderControl(
                  entry: entry,
                ),
                const VerticalPadding(height: 3),
                if (entry.spoilerText.isNotEmpty) ...[
                  Text(
                    'Content Summary: ${entry.spoilerText}',
                    style: Theme.of(context).textTheme.bodyLarge,
                  ),
                  const VerticalPadding(height: 3)
                ],
                HtmlTextViewerControl(content: entry.body),
              ],
            ),
          ),
        ),
        const VerticalPadding(),
      ],
    );
  }

  Widget buildButtonBar(BuildContext context, TimelineManager manager) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        if (!widget.forEditing)
          ElevatedButton(
            onPressed:
                isSubmitting ? null : () => createStatus(context, manager),
            child: const Text('Submit'),
          ),
        if (widget.forEditing)
          ElevatedButton(
            onPressed: isSubmitting ? null : () => editStatus(context, manager),
            child: const Text('Edit'),
          ),
        const HorizontalPadding(),
        ElevatedButton(
          onPressed: isSubmitting
              ? null
              : () {
                  context.pop();
                },
          child: const Text('Cancel'),
        ),
      ],
    );
  }

  Widget buildBusyBody(BuildContext context, Widget mainBody, String status) {
    return Stack(
      children: [
        mainBody,
        Card(
          color: Theme.of(context).canvasColor.withOpacity(0.8),
          child: SizedBox(
            width: MediaQuery.of(context).size.width,
            height: MediaQuery.of(context).size.height,
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  const CircularProgressIndicator(),
                  const VerticalPadding(),
                  Text(status),
                ],
              ),
            ),
          ),
        ),
      ],
    );
  }

  Widget buildLinkWithPreview(BuildContext context) {
    return Column(
      children: [
        Row(
          children: [
            Expanded(
              child: TextField(
                controller: linkPreviewController,
                decoration: InputDecoration(
                  labelText: 'Link with preview (optional)',
                  border: OutlineInputBorder(
                    borderSide: const BorderSide(),
                    borderRadius: BorderRadius.circular(5.0),
                  ),
                ),
              ),
            ),
            IconButton(
              onPressed: () async {
                final newPreviewResult =
                    await getLinkPreview(linkPreviewController.text);
                newPreviewResult.match(
                    onSuccess: (preview) => setState(() {
                          linkPreviewData = preview;
                        }),
                    onError: (error) {
                      if (mounted) {
                        buildSnackbar(
                            context, 'Error building link preview: $error');
                        logError(error, _logger);
                      }
                    });
              },
              icon: const Icon(Icons.refresh),
            ),
          ],
        ),
        const VerticalPadding(),
        if (linkPreviewData != null) buildPreviewCard(linkPreviewData!),
      ],
    );
  }

  Widget buildPreviewCard(LinkPreviewData preview) {
    return Row(
      children: [
        buildPreviewImageSelector(preview),
        Expanded(
          child: ListTile(
            title: Text(preview.title),
            subtitle: Text(preview.description.truncate(length: 128)),
            trailing: IconButton(
              onPressed: () {
                setState(() {
                  linkPreviewController.text = '';
                  linkPreviewData = null;
                });
              },
              icon: const Icon(Icons.delete),
            ),
          ),
        ),
      ],
    );
  }

  Widget buildPreviewImageSelector(LinkPreviewData preview) {
    const width = 128.0;
    const height = 128.0;
    if (preview.selectedImageUrl.isEmpty &&
        preview.availableImageUrls.isEmpty) {
      return Container(
        width: width,
        height: height,
        color: Colors.grey,
      );
    }

    final currentImage = SizedBox(
        width: width,
        height: height,
        child:
            LoginAwareCachedNetworkImage(imageUrl: preview.selectedImageUrl));

    if (preview.availableImageUrls.length < 2) {
      return currentImage;
    }

    return Column(
      children: [
        currentImage,
        // TODO Add in when Friendica no longer stomps on image previews
        // Row(
        //   mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        //   children: [
        //     IconButton(
        //       onPressed: () => updateLinkPreviewThumbnail(preview, -1),
        //       icon: Icon(size: iconSize, Icons.arrow_back_ios),
        //     ),
        //     IconButton(
        //       onPressed: () => updateLinkPreviewThumbnail(preview, 1),
        //       icon: Icon(size: iconSize, Icons.arrow_forward_ios),
        //     ),
        //   ],
        // )
      ],
    );
  }

  Widget buildVisibilitySelector(BuildContext context) {
    if (widget.forEditing || widget.parentId.isNotEmpty) {
      final cm = context
          .read<ActiveProfileSelector<ConnectionsManager>>()
          .activeEntry
          .value;

      return Row(
        children: [
          const Text('Visibility:'),
          IconButton(
            onPressed: () async {
              await showVisibilityDialog(context, cm, visibility);
            },
            icon: visibility.type == VisibilityType.public
                ? const Icon(Icons.public)
                : const Icon(Icons.lock),
          )
        ],
      );
    }
    final circles = context
        .watch<ActiveProfileSelector<TimelineManager>>()
        .activeEntry
        .andThen((tm) => tm.getCircles())
        .getValueOrElse(() => []);
    circles.sort((g1, g2) => g1.name.compareTo(g2.name));

    final circleMenuItems = <DropdownMenuItem<CircleData>>[];
    circleMenuItems.add(DropdownMenuItem(
        value: CircleData.followersPseudoCircle,
        child: Text(CircleData.followersPseudoCircle.name)));
    circleMenuItems.add(DropdownMenuItem(
        value: CircleData('', ''), child: const Divider(), enabled: false));
    circleMenuItems.addAll(circles.map((g) => DropdownMenuItem(
          value: g,
          child: Text(g.name),
        )));
    if (currentCircle != CircleData.followersPseudoCircle &&
        !circles.contains(currentCircle)) {
      currentCircle = null;
    }
    return Row(
      children: [
        const Text('Visibility:'),
        const HorizontalPadding(),
        DropdownButton<VisibilityType>(
          value: visibility.type,
          onChanged: widget.forEditing
              ? null
              : (value) {
                  setState(() {
                    if (value == VisibilityType.public) {
                      visibility = Visibility.public();
                      return;
                    }

                    if (value == VisibilityType.private &&
                        currentCircle == null) {
                      visibility = Visibility.private();
                      return;
                    }

                    visibility = Visibility(
                      type: VisibilityType.private,
                      allowedCircleIds: [currentCircle!.id],
                    );
                  });
                },
          items: VisibilityType.values
              .map((v) => DropdownMenuItem(
                    value: v,
                    child: Text(v.toLabel()),
                  ))
              .toList(),
        ),
        const HorizontalPadding(),
        if (visibility.type == VisibilityType.private)
          Expanded(
            child: DropdownButton<CircleData>(
              value: currentCircle,
              isExpanded: true,
              onChanged: widget.forEditing
                  ? null
                  : (value) {
                      setState(() {
                        currentCircle = value;
                        visibility = Visibility(
                          type: VisibilityType.private,
                          allowedCircleIds:
                              currentCircle == null ? [] : [currentCircle!.id],
                        );
                      });
                    },
              items: circleMenuItems,
            ),
          ),
      ],
    );
  }

  void updateLinkPreviewThumbnail(LinkPreviewData preview, int increment) {
    var currentIndex =
        preview.availableImageUrls.indexOf(preview.selectedImageUrl) +
            increment;
    if (currentIndex < 0) {
      currentIndex = preview.availableImageUrls.length - 1;
    }

    if (currentIndex > preview.availableImageUrls.length - 1) {
      currentIndex = 0;
    }
    setState(() {
      linkPreviewData = preview.copy(
          selectedImageUrl: preview.availableImageUrls[currentIndex]);
    });
  }
}
