import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/misc/flex_grid.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';

/// {@template streamGalleryAttachment}
/// Constructs a gallery of images, videos, and gifs from a list of attachments.
///
/// This widget uses a [FlexGrid] to display the attachments in a grid format.
/// The grid will automatically resize based on the size of the attachment.
/// {@endtemplate}
///
/// See also:
///
///  * [StreamImageAttachmentThumbnail], which is used to display the image
///  thumbnails.
///  * [StreamVideoAttachmentThumbnail], which is used to display the video
///  thumbnails.
///  * [StreamGiphyAttachmentThumbnail], which is used to display the gif
///  thumbnails.
class StreamGalleryAttachment extends StatelessWidget {
  /// {@macro streamGalleryAttachment}
  const StreamGalleryAttachment({
    super.key,
    required this.attachments,
    required this.message,
    this.shape,
    this.constraints = const BoxConstraints(),
    this.spacing = 2.0,
    this.runSpacing = 2.0,
    required this.itemBuilder,
  });

  /// List of attachments to show
  final List<Attachment> attachments;

  /// The [Message] that the images are attached to
  final Message message;

  /// The shape of the attachment.
  ///
  /// Defaults to [RoundedRectangleBorder] with a radius of 14.
  final ShapeBorder? shape;

  /// The constraints of the [attachments]
  final BoxConstraints constraints;

  /// How much space to place between children in a run in the main axis.
  ///
  /// For example, if [spacing] is 10.0, the children will be spaced at least
  /// 10.0 logical pixels apart in the main axis.
  ///
  /// Defaults to 2.0.
  final double spacing;

  /// How much space to place between the runs themselves in the cross axis.
  ///
  /// For example, if [runSpacing] is 10.0, the runs will be spaced at least
  /// 10.0 logical pixels apart in the cross axis.
  ///
  /// Defaults to 2.0.
  final double runSpacing;

  /// Item builder for the gallery.
  final IndexedWidgetBuilder itemBuilder;

  @override
  Widget build(BuildContext context) {
    assert(
      attachments.length >= 2,
      'Gallery should have at least 2 attachments, found ${attachments.length}',
    );

    final chatTheme = StreamChatTheme.of(context);
    final colorTheme = chatTheme.colorTheme;
    final shape = this.shape ??
        RoundedRectangleBorder(
          side: BorderSide(
            color: colorTheme.borders,
            strokeAlign: BorderSide.strokeAlignOutside,
          ),
          borderRadius: BorderRadius.circular(14),
        );

    return Container(
      constraints: constraints,
      clipBehavior: Clip.hardEdge,
      decoration: ShapeDecoration(shape: shape),
      // Added a builder just for the sake of calculating the image count
      // and building the appropriate layout based on the image count.
      child: Builder(
        builder: (context) {
          final attachmentCount = attachments.length;
          if (attachmentCount == 2) {
            return _buildForTwo(context, attachments);
          }

          if (attachmentCount == 3) {
            return _buildForThree(context, attachments);
          }

          return _buildForFourOrMore(context, attachments);
        },
      ),
    );
  }

  Widget _buildForTwo(BuildContext context, List<Attachment> attachments) {
    final aspectRatio1 = attachments[0].originalSize?.aspectRatio;
    final aspectRatio2 = attachments[1].originalSize?.aspectRatio;

    // check if one image is landscape and other is portrait or vice versa
    final isLandscape1 = aspectRatio1 != null && aspectRatio1 > 1;
    final isLandscape2 = aspectRatio2 != null && aspectRatio2 > 1;

    // Both the images are landscape.
    if (isLandscape1 && isLandscape2) {
      // ----------
      // |        |
      // ----------
      // |        |
      // ----------
      return FlexGrid(
        pattern: const [
          [1],
          [1],
        ],
        spacing: spacing,
        runSpacing: runSpacing,
        children: [
          itemBuilder(context, 0),
          itemBuilder(context, 1),
        ],
      );
    }

    // Both the images are portrait.
    if (!isLandscape1 && !isLandscape2) {
      // -----------
      // |    |    |
      // |    |    |
      // |    |    |
      // -----------
      return FlexGrid(
        pattern: const [
          [1, 1],
        ],
        spacing: spacing,
        runSpacing: runSpacing,
        children: [
          itemBuilder(context, 0),
          itemBuilder(context, 1),
        ],
      );
    }

    // Layout on the basis of isLandscape1.
    // 1. True
    // -----------
    // |      |  |
    // |      |  |
    // |      |  |
    // -----------
    //
    // 2. False
    // -----------
    // |  |      |
    // |  |      |
    // |  |      |
    // -----------
    return FlexGrid(
      pattern: [
        if (isLandscape1) [2, 1] else [1, 2],
      ],
      spacing: spacing,
      runSpacing: runSpacing,
      children: [
        itemBuilder(context, 0),
        itemBuilder(context, 1),
      ],
    );
  }

  Widget _buildForThree(BuildContext context, List<Attachment> attachments) {
    final aspectRatio1 = attachments[0].originalSize?.aspectRatio;
    final isLandscape1 = aspectRatio1 != null && aspectRatio1 > 1;

    // We layout on the basis of isLandscape1.
    // 1. True
    // -----------
    // |         |
    // |         |
    // |---------|
    // |    |    |
    // |    |    |
    // -----------
    //
    // 2. False
    // -----------
    // |    |    |
    // |    |    |
    // |    |----|
    // |    |    |
    // |    |    |
    // -----------
    return FlexGrid(
      pattern: const [
        [1],
        [1, 1],
      ],
      spacing: spacing,
      runSpacing: runSpacing,
      reverse: !isLandscape1,
      children: [
        itemBuilder(context, 0),
        itemBuilder(context, 1),
        itemBuilder(context, 2),
      ],
    );
  }

  Widget _buildForFourOrMore(
      BuildContext context, List<Attachment> attachments) {
    final pattern = <List<int>>[];
    final children = <Widget>[];

    for (var i = 0; i < attachments.length; i++) {
      if (i.isEven) {
        pattern.add([1]);
      } else {
        pattern.last.add(1);
      }

      children.add(itemBuilder(context, i));
    }

    // -----------
    // |    |    |
    // |    |    |
    // ------------
    // |    |    |
    // |    |    |
    // ------------
    return FlexGrid(
      pattern: pattern,
      maxChildren: 4,
      spacing: spacing,
      runSpacing: runSpacing,
      children: children,
      overlayBuilder: (context, remaining) {
        return IgnorePointer(
          child: ColoredBox(
            color: Colors.black38,
            child: Center(
              child: Text(
                '+$remaining',
                style: const TextStyle(
                  fontSize: 26,
                  color: Colors.white,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
          ),
        );
      },
    );
  }
}
