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

import 'package:flutter/material.dart';
import 'package:drifter/domain_models/domain_models.dart';
import 'package:drifter/models/keys.dart';
import 'package:drifter/theme/app_colors.dart';
import 'package:drifter/pages/home_screen/widgets/message_ok_button_widget.dart';
import 'package:drifter/pages/home_screen/widgets/message_text_button_widget.dart';
import 'package:drifter/pages/home_screen/widgets/message_text_form_field_widget.dart';
import 'package:drifter/pages/profile_screen/widgets/message_snack_bar.dart';
import 'package:nostr_tools/nostr_tools.dart';

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  bool _isConnected = false;

  final List<Event> _events = [];
  final Map<String, Metadata> _metaDatas = {};
  late Stream<Event> _stream;
  final _controller = StreamController<Event>();

  bool _isNotePublishing = false;

  @override
  void initState() {
    _initStream();
    super.initState();
  }

  Future<Stream<Event>> _connectToRelay() async {
    final stream = await Relay.relay.connect();

    Relay.relay.on((event) {
      if (event == RelayEvent.connect) {
        setState(() => _isConnected = true);
      } else if (event == RelayEvent.error) {
        setState(() => _isConnected = false);
      }
    });

    Relay.relay.sub([
      Filter(
        kinds: [1],
        limit: 100,
        t: ['nostr'],
      )
    ]);

    return stream
        .where((message) => message.type == 'EVENT')
        .map((message) => message.message);
  }

  void _initStream() async {
    _stream = await _connectToRelay();
    _stream.listen((message) {
      final event = message;
      if (event.kind == 1) {
        setState(() => _events.add(event));
        Relay.relay.sub([
          Filter(kinds: [0], authors: [event.pubkey])
        ]);
      } else if (event.kind == 0) {
        final metadata = Metadata.fromJson(jsonDecode(event.content));
        setState(() => _metaDatas[event.pubkey] = metadata);
      }
      _controller.add(event);
    });
  }

// The _resubscribeStream() method clears the _events and _metaData scollection after a 1 second delay
  Future<void> _resubscribeStream() async {
    await Future.delayed(const Duration(seconds: 1), () {
      setState(() {
        _events.clear();
        _metaDatas.clear();
      });
      //  _initStream() method, responsible for initializing and subscribing to the stream, to reconnect and re-subscribe to the filter.
      _initStream();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: RefreshIndicator(
        onRefresh: () async {
          await _resubscribeStream();
        },
        child: StreamBuilder(
          stream: _controller.stream,
          builder: (context, snapshot) {
            // Inside the builder callback, the snapshot object contains the most recent event from the thread.
            // If snapshot.hasData is true, there is data to display. In this case, ListView.builder is returned, which displays a list of NoostCard widgets.
            if (snapshot.hasData) {
              return ListView.builder(
                // The itemCount property of ListView.builder is set to _events.length, , which is the number of events in the _events list.
                itemCount: _events.length,
                itemBuilder: (context, index) {
                  final event = _events[index];
                  final metadata = _metaDatas[event.pubkey];
                  // For each event, a Noost object is created that encapsulates the details of the event, including id, avatarUrl, name,username, time, content и pubkey.
                  // _metaDatas, you can map the event public key to the author's metadata.
                  final domain = Domain(
                    noteId: event.id,
                    avatarUrl: metadata?.picture ??
                        'https://robohash.org/${event.pubkey}',
                    name: metadata?.name ?? 'Anon',
                    username: metadata?.displayName ??
                        (metadata?.display_name ?? 'Anon'),
                    time: TimeAgo.format(event.created_at),
                    content: event.content,
                    pubkey: event.pubkey,
                  );
                  return DomainCard(domain: domain);
                },
              );
            } else if (snapshot.connectionState == ConnectionState.waiting) {
              return const Center(child: Text('Loading....'));
            } else if (snapshot.hasError) {
              return Center(child: Text('Error: ${snapshot.error}'));
            }
            return const CenteredCircularProgressIndicator();
          },
        ),
      ),
      floatingActionButton: Keys.keysExist
          ? CreatePost(
              // The publishNote function is called when the user launches the "Noost!" button in the dialog box.
              publishNote: (note) {
                setState(() => _isNotePublishing = true);

                //         EventApi Creates an instance of the class defined in the nostr_tools package.
                final eventApi = EventApi();
                //  The finishEvent method of the EventApi class is called with the Event object and the _privateKey variable.
                //  finishEvent will set the event id with the event hash and sign the event with the given _privateKey.
                final event = eventApi.finishEvent(
                  //   This creates a new instance of the Event class with certain properties, such as:
                  Event(
                    kind: 1,
                    tags: [
                      ['t', 'nostr']
                    ],
                    content: note!,
                    created_at: DateTime.now().millisecondsSinceEpoch ~/ 1000,
                  ),
                  Keys.publicKey,
                );
                if (eventApi.verifySignature(event)) {
                  try {
                    //       If the signature is verified, the _relay method is called for the object to publish the event.
                    Relay.relay.publish(event);
                    //  After the _resubscribeStream event is published, a method is called that will probably update the stream or subscription to reflect the recently published event.
                    _resubscribeStream();
                    //    Show SnackBar to display a message that the note has been successfully published.
                    ScaffoldMessenger.of(context).showSnackBar(
                      MessageSnackBar(
                          label: 'Congratulations! Noost Published!'),
                    );
                  } catch (_) {
                    //   If an error occurs during the publishing process (e.g., an exception is caught), SnackBar displays a warning instead.
                    ScaffoldMessenger.of(context).showSnackBar(MessageSnackBar(
                      label: 'Oops! Something went wrong!',
                      isWarning: true,
                    ));
                  }
                }
                setState(() => _isNotePublishing = false);
                Navigator.pop(context);
              },
              isNotePublishing: _isNotePublishing,
            )
          :
          // If _keysExist is false, then an empty widget is displayed, which means that the FAB will not be visible. Container()
          Container(),
    );
  }
}

class TimeAgo {
  static String format(int timestamp) {
    DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
    Duration difference = DateTime.now().difference(dateTime);

    String timeAgo = '';

    if (difference.inDays > 0) {
      timeAgo =
          '${difference.inDays} ${difference.inDays == 1 ? 'day' : 'days'} ago';
    } else if (difference.inHours > 0) {
      timeAgo =
          '${difference.inHours} ${difference.inHours == 1 ? 'hour' : 'hours'} ago';
    } else if (difference.inMinutes > 0) {
      timeAgo =
          '${difference.inMinutes} ${difference.inMinutes == 1 ? 'minute' : 'minutes'} ago';
    } else {
      timeAgo = 'just now';
    }

    return timeAgo;
  }
}

class DomainCard extends StatelessWidget {
  const DomainCard({
    super.key,
    required this.domain,
  });

  final Domain domain;

  List<String>? extractImage(String text) {
    final RegExp exp = RegExp(
      r"(http(s?):)([/|.|\w|\s|-])*\.(?:jpg|gif|png|jpeg)",
      caseSensitive: false,
      multiLine: true,
    );

    final Iterable<Match> matches = exp.allMatches(text);

    final List<String> imageLinks =
        matches.map((match) => match.group(0)!).toList();

    return imageLinks.isNotEmpty ? imageLinks : null;
  }

  @override
  Widget build(BuildContext context) {
    final List<String>? imageLinks = extractImage(domain.content);
    return Container(
      margin: const EdgeInsets.all(8),
      decoration: BoxDecoration(
        color: AppColors.mainLightBlue,
        borderRadius: BorderRadius.circular(10),
        boxShadow: [
          BoxShadow(
            color: Colors.grey.withOpacity(0.5),
            spreadRadius: 2,
            blurRadius: 5,
            offset: const Offset(0, 3),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          ListTile(
            leading: CircleAvatar(
              backgroundImage: FadeInImage(
                placeholder:
                    const NetworkImage('https://i.ibb.co/mJkxDkb/satoshi.png'),
                image: NetworkImage(domain.avatarUrl),
              ).image,
            ),
            title:
                Text(domain.name, style: const TextStyle(color: Colors.white)),
            subtitle: Text('@${domain.username.toLowerCase()} • ${domain.time}',
                style: TextStyle(color: Colors.grey.shade400)),
            trailing: const Icon(Icons.more_vert, color: Colors.grey),
          ),
          Divider(height: 1, color: Colors.grey.shade400),
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
            child: Text(domain.content,
                style: const TextStyle(color: Colors.white)),
          ),
          if (imageLinks != null && imageLinks.isNotEmpty)
            Center(
              child: Stack(
                children: [
                  const Placeholder(
                    fallbackHeight: 200,
                    color: Colors.transparent,
                  ),
                  Center(
                    child: FadeInImage(
                      placeholder: const NetworkImage(
                        'https://i.ibb.co/D9jqXgR/58038897-167f0280-7ae6-11e9-94eb-88e880a25f0f.gif',
                      ),
                      image: NetworkImage(imageLinks.first),
                      fit: BoxFit.cover,
                    ),
                  ),
                ],
              ),
            ),
        ],
      ),
    );
  }
}

class CenteredCircularProgressIndicator extends StatelessWidget {
  const CenteredCircularProgressIndicator({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Center(
      child: CircularProgressIndicator(),
    );
  }
}

class CreatePost extends StatefulWidget {
  const CreatePost({
    Key? key,
    required this.publishNote,
    required this.isNotePublishing,
  }) : super(key: key);

  final Function(String?) publishNote;
  final bool isNotePublishing;

  @override
  State<CreatePost> createState() => _CreatePostState();
}

class _CreatePostState extends State<CreatePost> {
  final _noteController = TextEditingController();
  final GlobalKey<FormFieldState> _formKey = GlobalKey<FormFieldState>();

  @override
  Widget build(BuildContext context) {
    return FloatingActionButton(
      backgroundColor: Colors.deepPurpleAccent,
      tooltip: 'Create a new post',
      elevation: 2,
      highlightElevation: 4,
      foregroundColor: Colors.white,
      child: Stack(
        alignment: Alignment.center,
        children: [
          Container(
            width: 60,
            height: 60,
            decoration: const BoxDecoration(
                shape: BoxShape.circle, color: AppColors.mainDarkBlue),
          ),
          const Icon(
            Icons.add,
            color: Colors.white,
          ),
        ],
      ),
      onPressed: () async {
        _noteController.clear();
        await showDialog(
          barrierDismissible: false,
          context: context,
          builder: ((context) {
            return Dialog(
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(12),
              ),
              child: Container(
                constraints: const BoxConstraints(maxWidth: 600),
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(12),
                  color: Colors.white,
                ),
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Container(
                      padding: const EdgeInsets.symmetric(vertical: 24),
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(12),
                        color: AppColors.background,
                      ),
                      child: const Center(
                        child: Text(
                          'Create a Noost',
                          style: TextStyle(
                            fontSize: 24,
                            fontWeight: FontWeight.bold,
                            color: Colors.white,
                          ),
                        ),
                      ),
                    ),
                    const SizedBox(height: 24),
                    Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 24),
                      child: MessageTextFormField(
                        maxLines: 5,
                        hintText: 'Type your Noost here...',
                        controller: _noteController,
                        formKey: _formKey,
                        validator: (value) {
                          if (value == null || value.trim().isEmpty) {
                            return 'Please enter your note.';
                          }
                          return null;
                        },
                      ),
                    ),
                    const SizedBox(height: 24),
                    widget.isNotePublishing
                        ? const CenteredCircularProgressIndicator()
                        : Row(
                            mainAxisAlignment: MainAxisAlignment.end,
                            children: [
                              MessageTextButton(
                                onPressed: () {
                                  Navigator.pop(context);
                                },
                                label: 'Cancel',
                              ),
                              const SizedBox(width: 16),
                              MessageOkButton(
                                onPressed: () {
                                  if (_formKey.currentState!.validate()) {
                                    widget.publishNote(
                                        _noteController.text.trim());
                                  } else {
                                    _formKey.currentState?.setState(() {});
                                  }
                                },
                                label: 'Noost!',
                              ),
                              const SizedBox(width: 24),
                            ],
                          )
                  ],
                ),
              ),
            );
          }),
        );
      },
    );
  }
}
