import 'dart:io';

import 'package:flutter/material.dart';
import 'package:loader_overlay/loader_overlay.dart';
import 'package:nextcloud/nextcloud.dart';

import '../../../api/marianumcloud/webdav/webdav_api.dart';
import '../../../widget/confirm_dialog.dart';
import '../../../widget/focus_behaviour.dart';
import '../../../widget/info_dialog.dart';

class FilesUploadDialog extends StatefulWidget {
  final List<String> filePaths;
  final String remotePath;
  final void Function(List<String> uploadedFilePaths) onUploadFinished;
  final bool uniqueNames;

  const FilesUploadDialog({
    super.key,
    required this.filePaths,
    required this.remotePath,
    required this.onUploadFinished,
    this.uniqueNames = false,
  });

  @override
  State<FilesUploadDialog> createState() => _FilesUploadDialogState();
}

class UploadableFile {
  TextEditingController fileNameController = TextEditingController();
  String filePath;
  String fileName;
  double? _uploadProgress;
  bool isConflicting = false;

  UploadableFile(this.filePath, this.fileName);
}

class _FilesUploadDialogState extends State<FilesUploadDialog> {
  late List<UploadableFile> _uploadableFiles;
  bool _isUploading = false;
  double _overallProgressValue = 0.0;
  String _infoText = '';

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

    _uploadableFiles = widget.filePaths.map((filePath) {
      var fileName = filePath.split(Platform.pathSeparator).last;
      return UploadableFile(filePath, fileName);
    }).toList();
  }

  void showHttpErrorCode(int httpErrorCode) {
    InfoDialog.show(
      context,
      'Error code: $httpErrorCode',
      title: 'Ein Fehler ist aufgetreten',
      copyable: true,
    );
  }

  void _resetProgress() {
    _isUploading = false;
    _overallProgressValue = 0.0;
    _infoText = '';
  }

  void _showUploadError(String message) {
    setState(_resetProgress);
    InfoDialog.show(
      context,
      message,
      title: 'Upload fehlgeschlagen',
      copyable: true,
    );
  }

  Future<void> uploadFiles({bool override = false}) async {
    setState(() {
      _isUploading = true;
      _infoText = 'Vorbereiten';
      for (var file in _uploadableFiles) {
        file.isConflicting = false;
      }
    });

    final webdavClient = await WebdavApi.webdav;

    if (!override) {
      List<dynamic> result;
      try {
        result = (await webdavClient.propfind(
          PathUri.parse(widget.remotePath),
        )).responses;
      } catch (e) {
        if (!mounted) return;
        _showUploadError('Verbindung fehlgeschlagen: $e');
        return;
      }
      final conflictingFiles = _uploadableFiles.where((file) {
        final fileName = file.fileName;
        return result.any(
          (element) => Uri.decodeComponent(
            (element as WebDavResponse).href!,
          ).endsWith('/$fileName'),
        );
      }).toList();

      if (conflictingFiles.isNotEmpty) {
        if (!mounted) return;
        final replaceFiles = await showDialog<bool>(
          context: context,
          barrierDismissible: false,
          builder: (context) => AlertDialog(
            contentPadding: const EdgeInsets.all(10),
            title: const Text('Konflikt', textAlign: TextAlign.center),
            content: conflictingFiles.length == 1
                ? Text(
                    'Eine Datei mit dem Namen "${conflictingFiles.map((e) => e.fileName).first}" existiert bereits.',
                    textAlign: TextAlign.left,
                  )
                : SingleChildScrollView(
                    child: Text(
                      '${conflictingFiles.length} Dateien mit folgenden Namen existieren bereits: \n${conflictingFiles.map((e) => '\n -  ${e.fileName}').join('')}',
                      textAlign: TextAlign.left,
                    ),
                  ),
            actions: [
              TextButton(
                onPressed: () {
                  Navigator.pop(context, false);
                },
                child: const Text('Bearbeiten', textAlign: TextAlign.center),
              ),
              TextButton(
                onPressed: () {
                  showDialog(
                    context: context,
                    builder: (context) => ConfirmDialog(
                      title: 'Bestätigen?',
                      content:
                          'Bist du sicher, dass du ${conflictingFiles.length} Dateien überschreiben möchtest?',
                      onConfirm: () {
                        Navigator.pop(context, true);
                      },
                      confirmButton: 'Ja',
                      cancelButton: 'Nein',
                    ),
                  );
                },
                child: const Text('Überschreiben', textAlign: TextAlign.center),
              ),
            ],
          ),
        );

        if (replaceFiles != true) {
          setState(() {
            _resetProgress();
            for (var element in conflictingFiles) {
              element.isConflicting = true;
            }
          });
          return;
        }
      }
    }

    var uploadetFilePaths = <String>[];
    for (var file in _uploadableFiles) {
      var fileName = file.fileName;
      var filePath = file.filePath;

      if (widget.uniqueNames) {
        final unique = DateTime.now().microsecondsSinceEpoch.toRadixString(36);
        fileName =
            '${fileName.split('.').first}-$unique.${fileName.split('.').last}';
      }

      var fullRemotePath = '${widget.remotePath}/$fileName';

      setState(() {
        _infoText =
            '${_uploadableFiles.indexOf(file) + 1}/${_uploadableFiles.length}';
      });

      // A vanished source file would otherwise surface as a cryptic
      // "Content-Length must contain only digits" HttpException, because
      // statSync reports size -1 for missing files.
      final fileStat = FileStat.statSync(filePath);
      if (fileStat.type == FileSystemEntityType.notFound || fileStat.size < 0) {
        _showUploadError(
          'Die Datei "$fileName" ist nicht mehr verfügbar. '
          'Bitte wähle sie erneut aus.',
        );
        return;
      }

      final HttpClientResponse uploadTask;
      try {
        uploadTask = await webdavClient.putFile(
          File(filePath),
          fileStat,
          PathUri.parse(fullRemotePath),
          onProgress: (progress) {
            setState(() {
              file._uploadProgress = progress;
              _overallProgressValue =
                  ((progress + _uploadableFiles.indexOf(file)) /
                          _uploadableFiles.length)
                      .toDouble();
            });
          },
        );
      } catch (e) {
        if (!mounted) return;
        _showUploadError('Upload fehlgeschlagen für "$fileName": $e');
        return;
      }

      if (uploadTask.statusCode < 200 || uploadTask.statusCode > 299) {
        setState(_resetProgress);
        if (!mounted) return;
        Navigator.of(context).pop();
        showHttpErrorCode(uploadTask.statusCode);
      } else {
        uploadetFilePaths.add(fullRemotePath);
      }
    }

    setState(_resetProgress);
    if (!mounted) return;
    Navigator.of(context).pop();
    widget.onUploadFinished(uploadetFilePaths);
  }

  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(
      title: const Text('Dateien hochladen'),
      automaticallyImplyLeading: false,
    ),
    body: LoaderOverlay(
      overlayWholeScreen: true,
      child: Column(
        children: [
          Expanded(
            child: ListView.builder(
              itemCount: _uploadableFiles.length,
              itemBuilder: (context, index) {
                final currentFile = _uploadableFiles[index];
                currentFile.fileNameController.text = currentFile.fileName;
                return ListTile(
                  title: TextField(
                    readOnly: _isUploading,
                    controller: currentFile.fileNameController,
                    decoration: InputDecoration(
                      border: const UnderlineInputBorder(),
                      label: Text('Datei ${index + 1}'),
                      errorText: currentFile.isConflicting
                          ? 'existiert bereits'
                          : null,
                      errorStyle: TextStyle(
                        color: Theme.of(context).colorScheme.error,
                      ),
                    ),
                    onChanged: (input) {
                      currentFile.fileName = input;
                    },
                    onTapOutside: (PointerDownEvent event) {
                      FocusBehaviour.textFieldTapOutside(context);
                      if (currentFile.isConflicting) {
                        setState(() {
                          currentFile.isConflicting = false;
                        });
                      }
                    },
                    onEditingComplete: () {
                      if (currentFile.isConflicting) {
                        setState(() {
                          currentFile.isConflicting = false;
                        });
                      }
                    },
                  ),
                  subtitle:
                      _isUploading && (currentFile._uploadProgress ?? 0) < 1
                      ? LinearProgressIndicator(
                          value: currentFile._uploadProgress,
                          borderRadius: const BorderRadius.all(
                            Radius.circular(2),
                          ),
                        )
                      : null,
                  trailing: Container(
                    width: 24,
                    height: 24,
                    padding: EdgeInsets.zero,
                    child: IconButton(
                      tooltip: 'Datei entfernen',
                      padding: EdgeInsets.zero,
                      onPressed: () {
                        if (!_isUploading) {
                          if (_uploadableFiles.length - 1 <= 0) {
                            Navigator.of(context).pop();
                          }
                          setState(() {
                            _uploadableFiles.removeAt(index);
                          });
                        }
                      },
                      icon: const Icon(Icons.delete_outlined),
                    ),
                  ),
                );
              },
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(
              left: 15,
              right: 15,
              bottom: 15,
              top: 5,
            ),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.end,
              children: [
                Visibility(
                  visible: !_isUploading,
                  child: TextButton(
                    onPressed: () => Navigator.of(context).pop(),
                    child: const Text('Abbrechen'),
                  ),
                ),
                const Expanded(child: SizedBox.shrink()),
                Visibility(
                  visible: _isUploading,
                  replacement: TextButton(
                    onPressed: () => uploadFiles(override: widget.uniqueNames),
                    child: const Text('Hochladen'),
                  ),
                  child: Visibility(
                    visible: _infoText.length < 5,
                    replacement: Row(
                      children: [
                        Text(_infoText),
                        const SizedBox(width: 15),
                        CircularProgressIndicator(value: _overallProgressValue),
                      ],
                    ),
                    child: Stack(
                      alignment: Alignment.center,
                      children: [
                        CircularProgressIndicator(value: _overallProgressValue),
                        Center(child: Text(_infoText)),
                      ],
                    ),
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    ),
  );
}
