import 'dart:convert';
import 'dart:io';

import 'package:collection/collection.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:image_picker/image_picker.dart';
import 'package:provider/provider.dart';
import 'package:recon/apis/record_api.dart';
import 'package:recon/auxiliary.dart';
import 'package:recon/client_holder.dart';
import 'package:recon/clients/api_client.dart';
import 'package:recon/clients/messaging_client.dart';
import 'package:recon/models/message.dart';
import 'package:recon/models/users/friend.dart';
import 'package:recon/widgets/messages/message_attachment_list.dart';
import 'package:record/record.dart';

class MessageInputBar extends StatefulWidget {
  const MessageInputBar({this.disabled = false, required this.recipient, this.onMessageSent, super.key});

  final bool disabled;
  final Friend recipient;
  final Function()? onMessageSent;

  @override
  State<StatefulWidget> createState() => _MessageInputBarState();
}

class _MessageInputBarState extends State<MessageInputBar> {
  final TextEditingController _messageTextController = TextEditingController();
  final List<(FileType, File)> _loadedFiles = [];
  final AudioRecorder _recorder = AudioRecorder();
  final ImagePicker _imagePicker = ImagePicker();

  DateTime? _recordingStartTime;

  bool _isSending = false;
  bool _attachmentPickerOpen = false;
  String _currentText = "";
  double? _sendProgress;
  bool get _isRecording => _recordingStartTime != null;
  set _isRecording(value) => _recordingStartTime = value ? DateTime.now() : null;
  bool _recordingCancelled = false;

  @override
  void dispose() {
    _recorder.dispose();
    _messageTextController.dispose();
    super.dispose();
  }

  Future<void> sendTextMessage(ApiClient client, MessagingClient mClient, String content) async {
    if (content.isEmpty) return;
    final message = Message(
      id: Message.generateId(),
      recipientId: widget.recipient.id,
      senderId: client.userId,
      type: MessageType.text,
      content: content,
      sendTime: DateTime.now().toUtc(),
      state: MessageState.local,
    );
    mClient.sendMessage(message);
  }

  Future<void> sendImageMessage(ApiClient client, MessagingClient mClient, File file, String machineId,
      void Function(double progress) progressCallback) async {
    final record = await RecordApi.uploadImage(
      client,
      image: file,
      machineId: machineId,
      progressCallback: progressCallback,
    );
    final message = Message(
        id: record.extractMessageId() ?? Message.generateId(),
        recipientId: widget.recipient.id,
        senderId: client.userId,
        type: MessageType.object,
        content: jsonEncode(record.toMap()),
        sendTime: DateTime.now().toUtc(),
        state: MessageState.local);
    mClient.sendMessage(message);
  }

  Future<void> sendVoiceMessage(ApiClient client, MessagingClient mClient, File file, String machineId,
      void Function(double progress) progressCallback) async {
    final record = await RecordApi.uploadVoiceClip(
      client,
      voiceClip: file,
      machineId: machineId,
      progressCallback: progressCallback,
    );
    final message = Message(
      id: record.extractMessageId() ?? Message.generateId(),
      recipientId: widget.recipient.id,
      senderId: client.userId,
      type: MessageType.sound,
      content: jsonEncode(record.toMap()),
      sendTime: DateTime.now().toUtc(),
      state: MessageState.local,
    );
    mClient.sendMessage(message);
  }

  Future<void> sendRawFileMessage(ApiClient client, MessagingClient mClient, File file, String machineId,
      void Function(double progress) progressCallback) async {
    final record = await RecordApi.uploadRawFile(
      client,
      file: file,
      machineId: machineId,
      progressCallback: progressCallback,
    );
    final message = Message(
      id: record.extractMessageId() ?? Message.generateId(),
      recipientId: widget.recipient.id,
      senderId: client.userId,
      type: MessageType.object,
      content: jsonEncode(record.toMap()),
      sendTime: DateTime.now().toUtc(),
      state: MessageState.local,
    );
    mClient.sendMessage(message);
  }

  void _pointerMoveEventHandler(PointerMoveEvent event) {
    if (!_isRecording) return;
    final width = MediaQuery.of(context).size.width;

    if (event.localPosition.dx < width - width / 4) {
      if (!_recordingCancelled) {
        HapticFeedback.vibrate();
        setState(() {
          _recordingCancelled = true;
        });
      }
    } else {
      if (_recordingCancelled) {
        HapticFeedback.vibrate();
        setState(() {
          _recordingCancelled = false;
        });
      }
    }
  }

  Stream<Duration> _recordingDurationStream() async* {
    while (_isRecording) {
      yield DateTime.now().difference(_recordingStartTime!);
      await Future.delayed(const Duration(milliseconds: 100));
    }
  }

  @override
  Widget build(BuildContext context) {
    final mClient = Provider.of<MessagingClient>(context, listen: false);
    return Listener(
      onPointerMove: _pointerMoveEventHandler,
      onPointerUp: (_) async {
        // Do this here as the pointerUp event of the gesture detector on the mic button can be unreliable
        final cHolder = ClientHolder.of(context);
        if (_isRecording) {
          if (_recordingCancelled) {
            setState(() {
              _isRecording = false;
            });
            final recording = await _recorder.stop();
            if (recording == null) return;
            final file = File(recording);
            if (await file.exists()) {
              await file.delete();
            }
          }
          setState(() {
            _recordingCancelled = false;
            _isRecording = false;
          });

          if (await _recorder.isRecording()) {
            final recording = await _recorder.stop();
            if (recording == null) return;

            final file = File(recording);
            setState(() {
              _isSending = true;
              _sendProgress = 0;
            });
            final apiClient = cHolder.apiClient;
            await sendVoiceMessage(
                apiClient, mClient, file, cHolder.settingsClient.currentSettings.machineId.valueOrDefault, (progress) {
              setState(() {
                _sendProgress = progress;
              });
            });
            setState(() {
              _isSending = false;
              _sendProgress = null;
            });
          }
        }
      },
      child: Container(
        decoration: BoxDecoration(
          border: const Border(top: BorderSide(width: 1, color: Colors.black)),
          color: Theme.of(context).colorScheme.surfaceVariant,
        ),
        padding: const EdgeInsets.symmetric(horizontal: 4),
        child: SafeArea(
          top: false,
          child: Column(
            children: [
              if (_isSending && _sendProgress != null) LinearProgressIndicator(value: _sendProgress),
              Container(
                decoration: BoxDecoration(
                  color: Theme.of(context).colorScheme.surfaceVariant,
                ),
                child: AnimatedSwitcher(
                  duration: const Duration(milliseconds: 200),
                  switchInCurve: Curves.easeOut,
                  switchOutCurve: Curves.easeOut,
                  transitionBuilder: (Widget child, animation) => SizeTransition(
                    sizeFactor: animation,
                    child: child,
                  ),
                  child: switch ((_attachmentPickerOpen, _loadedFiles)) {
                    (true, []) => Row(
                        key: const ValueKey("attachment-picker"),
                        children: [
                          TextButton.icon(
                            onPressed: _isSending
                                ? null
                                : () async {
                                    final result =
                                        await FilePicker.platform.pickFiles(type: FileType.image, allowMultiple: true);
                                    if (result != null) {
                                      setState(() {
                                        _loadedFiles.addAll(result.files
                                            .map((e) => e.path != null ? (FileType.image, File(e.path!)) : null)
                                            .whereNotNull());
                                      });
                                    }
                                  },
                            icon: const Icon(Icons.image),
                            label: const Text("Gallery"),
                          ),
                          TextButton.icon(
                            onPressed: _isSending
                                ? null
                                : () async {
                                    final picture = await _imagePicker.pickImage(source: ImageSource.camera);
                                    if (picture == null) {
                                      if (context.mounted) {
                                        ScaffoldMessenger.of(context)
                                            .showSnackBar(const SnackBar(content: Text("Failed to get image path")));
                                      }
                                      return;
                                    }
                                    final file = File(picture.path);
                                    if (await file.exists()) {
                                      setState(() {
                                        _loadedFiles.add((FileType.image, file));
                                      });
                                    } else {
                                      if (context.mounted) {
                                        ScaffoldMessenger.of(context)
                                            .showSnackBar(const SnackBar(content: Text("Failed to load image file")));
                                      }
                                    }
                                  },
                            icon: const Icon(Icons.camera),
                            label: const Text("Camera"),
                          ),
                          TextButton.icon(
                            onPressed: _isSending
                                ? null
                                : () async {
                                    final result =
                                        await FilePicker.platform.pickFiles(type: FileType.any, allowMultiple: true);
                                    if (result != null) {
                                      setState(() {
                                        _loadedFiles.addAll(result.files
                                            .map((e) => e.path != null ? (FileType.any, File(e.path!)) : null)
                                            .whereNotNull());
                                      });
                                    }
                                  },
                            icon: const Icon(Icons.file_present_rounded),
                            label: const Text("Document"),
                          ),
                        ],
                      ),
                    (false, []) => null,
                    (_, _) => MessageAttachmentList(
                        disabled: _isSending,
                        initialFiles: _loadedFiles,
                        onChange: (List<(FileType, File)> loadedFiles) => setState(() {
                          _loadedFiles.clear();
                          _loadedFiles.addAll(loadedFiles);
                        }),
                      ),
                  },
                ),
              ),
              Row(
                children: [
                  AnimatedSwitcher(
                    duration: const Duration(milliseconds: 200),
                    transitionBuilder: (Widget child, Animation<double> animation) => FadeTransition(
                      opacity: animation,
                      child: RotationTransition(
                        turns: Tween<double>(begin: 0.6, end: 1).animate(animation),
                        child: child,
                      ),
                    ),
                    child: switch ((_attachmentPickerOpen, _isRecording)) {
                      (_, true) => IconButton(
                          onPressed: () {},
                          icon: Icon(
                            Icons.delete,
                            color: _recordingCancelled ? Theme.of(context).colorScheme.error : null,
                          ),
                        ),
                      (false, _) => IconButton(
                          key: const ValueKey("add-attachment-icon"),
                          onPressed: _isSending
                              ? null
                              : () {
                                  ScaffoldMessenger.of(context).showSnackBar(
                                      const SnackBar(content: Text("Sorry, this feature is not yet available")));
                                  return;
                                  // setState(() {
                                  //   _attachmentPickerOpen = true;
                                  // });
                                },
                          icon: const Icon(
                            Icons.attach_file,
                          ),
                        ),
                      (true, _) => IconButton(
                          key: const ValueKey("remove-attachment-icon"),
                          onPressed: _isSending
                              ? null
                              : () async {
                                  if (_loadedFiles.isNotEmpty) {
                                    await showDialog(
                                        context: context,
                                        builder: (context) => AlertDialog(
                                              title: const Text("Remove all attachments"),
                                              content: const Text("This will remove all attachments, are you sure?"),
                                              actions: [
                                                TextButton(
                                                  onPressed: () {
                                                    Navigator.of(context).pop();
                                                  },
                                                  child: const Text("No"),
                                                ),
                                                TextButton(
                                                  onPressed: () {
                                                    setState(() {
                                                      _loadedFiles.clear();
                                                      _attachmentPickerOpen = false;
                                                    });
                                                    Navigator.of(context).pop();
                                                  },
                                                  child: const Text("Yes"),
                                                )
                                              ],
                                            ));
                                  } else {
                                    setState(() {
                                      _attachmentPickerOpen = false;
                                    });
                                  }
                                },
                          icon: const Icon(
                            Icons.close,
                          ),
                        ),
                    },
                  ),
                  Expanded(
                    child: Padding(
                      padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4),
                      child: Stack(
                        children: [
                          TextField(
                            enabled: (!widget.disabled) && !_isSending,
                            autocorrect: true,
                            controller: _messageTextController,
                            showCursor: !_isRecording,
                            maxLines: 4,
                            minLines: 1,
                            onChanged: (text) {
                              if (text.isEmpty != _currentText.isEmpty) {
                                setState(() {
                                  _currentText = text;
                                });
                                return;
                              }
                              _currentText = text;
                            },
                            style: Theme.of(context).textTheme.bodyLarge,
                            decoration: InputDecoration(
                                isDense: true,
                                hintText: _isRecording ? "" : "Message ${widget.recipient.username}...",
                                hintMaxLines: 1,
                                contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
                                fillColor: Colors.black26,
                                filled: true,
                                border: OutlineInputBorder(
                                  borderSide: BorderSide.none,
                                  borderRadius: BorderRadius.circular(24),
                                )),
                          ),
                          AnimatedSwitcher(
                            duration: const Duration(milliseconds: 200),
                            transitionBuilder: (Widget child, Animation<double> animation) => FadeTransition(
                              opacity: animation,
                              child: SlideTransition(
                                position: Tween<Offset>(
                                  begin: const Offset(0, .2),
                                  end: const Offset(0, 0),
                                ).animate(animation),
                                child: child,
                              ),
                            ),
                            child: _isRecording
                                ? Padding(
                                    padding: const EdgeInsets.symmetric(vertical: 12.0),
                                    child: _recordingCancelled
                                        ? Row(
                                            mainAxisAlignment: MainAxisAlignment.start,
                                            children: [
                                              const SizedBox(
                                                width: 8,
                                              ),
                                              const Padding(
                                                padding: EdgeInsets.symmetric(horizontal: 8.0),
                                                child: Icon(
                                                  Icons.cancel,
                                                  color: Colors.red,
                                                  size: 16,
                                                ),
                                              ),
                                              Text("Cancel Recording", style: Theme.of(context).textTheme.titleMedium),
                                            ],
                                          )
                                        : Row(
                                            mainAxisAlignment: MainAxisAlignment.start,
                                            children: [
                                              const SizedBox(
                                                width: 8,
                                              ),
                                              const Padding(
                                                padding: EdgeInsets.symmetric(horizontal: 8.0),
                                                child: Icon(
                                                  Icons.circle,
                                                  color: Colors.red,
                                                  size: 16,
                                                ),
                                              ),
                                              StreamBuilder<Duration>(
                                                  stream: _recordingDurationStream(),
                                                  builder: (context, snapshot) {
                                                    return Text("Recording: ${snapshot.data?.format()}",
                                                        style: Theme.of(context).textTheme.titleMedium);
                                                  }),
                                            ],
                                          ),
                                  )
                                : const SizedBox.shrink(),
                          ),
                        ],
                      ),
                    ),
                  ),
                  AnimatedSwitcher(
                    duration: const Duration(milliseconds: 200),
                    transitionBuilder: (Widget child, Animation<double> animation) => FadeTransition(
                      opacity: animation,
                      child: RotationTransition(
                        turns: Tween<double>(begin: 0.5, end: 1).animate(animation),
                        child: child,
                      ),
                    ),
                    child: _currentText.isNotEmpty || _loadedFiles.isNotEmpty
                        ? IconButton(
                            key: const ValueKey("send-button"),
                            splashRadius: 24,
                            padding: EdgeInsets.zero,
                            onPressed: _isSending
                                ? null
                                : () async {
                                    final cHolder = ClientHolder.of(context);
                                    final sMsgnr = ScaffoldMessenger.of(context);
                                    final settings = cHolder.settingsClient.currentSettings;
                                    final toSend = List<(FileType, File)>.from(_loadedFiles);
                                    setState(() {
                                      _isSending = true;
                                      _sendProgress = 0;
                                      _attachmentPickerOpen = false;
                                      _loadedFiles.clear();
                                    });
                                    try {
                                      for (int i = 0; i < toSend.length; i++) {
                                        final totalProgress = i / toSend.length;
                                        final file = toSend[i];
                                        if (file.$1 == FileType.image) {
                                          await sendImageMessage(
                                            cHolder.apiClient,
                                            mClient,
                                            file.$2,
                                            settings.machineId.valueOrDefault,
                                            (progress) => setState(() {
                                              _sendProgress = totalProgress + progress * 1 / toSend.length;
                                            }),
                                          );
                                        } else {
                                          await sendRawFileMessage(
                                              cHolder.apiClient,
                                              mClient,
                                              file.$2,
                                              settings.machineId.valueOrDefault,
                                              (progress) => setState(
                                                  () => _sendProgress = totalProgress + progress * 1 / toSend.length));
                                        }
                                      }
                                      setState(() {
                                        _sendProgress = null;
                                      });

                                      if (_currentText.isNotEmpty) {
                                        await sendTextMessage(cHolder.apiClient, mClient, _messageTextController.text);
                                      }
                                      _messageTextController.clear();
                                      _currentText = "";
                                      _loadedFiles.clear();
                                      _attachmentPickerOpen = false;
                                    } catch (e, s) {
                                      FlutterError.reportError(FlutterErrorDetails(exception: e, stack: s));
                                      sMsgnr.showSnackBar(SnackBar(content: Text("Failed to send a message: $e")));
                                    }
                                    setState(() {
                                      _isSending = false;
                                      _sendProgress = null;
                                    });
                                    widget.onMessageSent?.call();
                                  },
                            icon: const Icon(Icons.send),
                          )
                        : GestureDetector(
                            onTapUp: (_) {
                              _recordingCancelled = true;
                            },
                            onTapDown: widget.disabled
                                ? null
                                : (_) async {
                                    ScaffoldMessenger.of(context).showSnackBar(
                                        const SnackBar(content: Text("Sorry, this feature is not yet available")));
                                    return;
                                    // HapticFeedback.vibrate();
                                    // final hadToAsk =
                                    //     await Permission.microphone.isDenied;
                                    // final hasPermission =
                                    //     !await _recorder.hasPermission();
                                    // if (hasPermission) {
                                    //   if (context.mounted) {
                                    //     ScaffoldMessenger.of(context)
                                    //         .showSnackBar(const SnackBar(
                                    //       content: Text(
                                    //           "No permission to record audio."),
                                    //     ));
                                    //   }
                                    //   return;
                                    // }
                                    // if (hadToAsk) {
                                    //   // We had to ask for permissions so the user removed their finger from the record button.
                                    //   return;
                                    // }

                                    // final dir = await getTemporaryDirectory();
                                    // await _recorder.start(
                                    //     path: "${dir.path}/A-${const Uuid().v4()}.wav",
                                    //     const RecordConfig(
                                    //         numChannels: 1,
                                    //         sampleRate: 44100,
                                    //         encoder: AudioEncoder.wav));
                                    // setState(() {
                                    //   _isRecording = true;
                                    // });
                                  },
                            child: IconButton(
                              icon: const Icon(Icons.mic_outlined),
                              onPressed: _isSending
                                  ? null
                                  : () {
                                      // Empty onPressed for that sweet sweet ripple effect
                                    },
                            ),
                          ),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}
