#include "contentmediautils.h"

#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QHash>
#include <QSet>

#include <notebook/node.h>
#include <notebookbackend/inotebookbackend.h>

#include <buffer/filetypehelper.h>

#include <vtextedit/markdownutils.h>
#include <vtextedit/textutils.h>

#include <core/exception.h>
#include <core/file.h>
#include <utils/fileutils.h>
#include <utils/pathutils.h>

using namespace vnotex;

void ContentMediaUtils::copyMediaFiles(Node *p_node, INotebookBackend *p_backend,
                                       const QString &p_destFilePath) {
  Q_ASSERT(p_node->hasContent());
  auto file = p_node->getContentFile();
  if (file->getContentType().isMarkdown()) {
    copyMarkdownMediaFiles(file->read(), PathUtils::parentDirPath(file->getContentPath()),
                           p_backend, p_destFilePath);
  }
}

void ContentMediaUtils::copyMediaFiles(const QString &p_filePath, INotebookBackend *p_backend,
                                       const QString &p_destFilePath) {
  const auto &fileType = FileTypeHelper::getInst().getFileType(p_filePath);
  if (fileType.isMarkdown()) {
    copyMarkdownMediaFiles(FileUtils::readTextFile(p_filePath),
                           PathUtils::parentDirPath(p_filePath), p_backend, p_destFilePath);
  }
}

void ContentMediaUtils::copyMediaFiles(const File *p_file, const QString &p_destFilePath) {
  if (p_file->getContentType().isMarkdown()) {
    copyMarkdownMediaFiles(p_file->read(), p_file->getResourcePath(), nullptr, p_destFilePath);
  }
}

void ContentMediaUtils::copyMarkdownMediaFiles(const QString &p_content, const QString &p_basePath,
                                               INotebookBackend *p_backend,
                                               const QString &p_destFilePath) {
  auto content = p_content;

  // Images.
  const auto images = vte::MarkdownUtils::fetchImagesFromMarkdownText(
      content, p_basePath, vte::MarkdownLink::TypeFlag::LocalRelativeInternal);

  QDir destDir(PathUtils::parentDirPath(p_destFilePath));
  QSet<QString> handledImages;
  QHash<QString, QString> renamedImages;
  int lastPos = content.size();
  for (const auto &link : images) {
    Q_ASSERT(link.m_urlInLinkPos < lastPos);
    lastPos = link.m_urlInLinkPos;

    qDebug() << "link" << link.m_path << link.m_urlInLink;

    if (handledImages.contains(link.m_path)) {
      auto it = renamedImages.find(link.m_path);
      if (it != renamedImages.end()) {
        content.replace(link.m_urlInLinkPos, link.m_urlInLink.size(), it.value());
      }
      continue;
    }

    handledImages.insert(link.m_path);

    if (!QFileInfo::exists(link.m_path)) {
      qWarning() << "image of Markdown file does not exist" << link.m_path << link.m_urlInLink;
      continue;
    }

    // Get the relative path of the image and apply it to the dest file path.
    const auto decodedUrlInLink = vte::TextUtils::decodeUrl(link.m_urlInLink);
    const auto oldDestFilePath = destDir.filePath(decodedUrlInLink);
    destDir.mkpath(PathUtils::parentDirPath(oldDestFilePath));
    auto destFilePath = p_backend ? p_backend->renameIfExistsCaseInsensitive(oldDestFilePath)
                                  : FileUtils::renameIfExistsCaseInsensitive(oldDestFilePath);
    if (oldDestFilePath != destFilePath) {
      // Rename happens.
      const auto oldFileName = PathUtils::fileName(oldDestFilePath);
      const auto newFileName = PathUtils::fileName(destFilePath);
      qWarning() << QStringLiteral("image name conflicts when copy, renamed from (%1) to (%2)")
                        .arg(oldFileName, newFileName);

      // Update the text content.
      const auto encodedOldFileName = PathUtils::fileName(link.m_urlInLink);
      const auto encodedNewFileName = vte::TextUtils::encodeUrl(newFileName);
      auto newUrlInLink(link.m_urlInLink);
      newUrlInLink.replace(newUrlInLink.size() - encodedOldFileName.size(),
                           encodedOldFileName.size(), encodedNewFileName);

      content.replace(link.m_urlInLinkPos, link.m_urlInLink.size(), newUrlInLink);
      renamedImages.insert(link.m_path, newUrlInLink);
    }

    if (p_backend) {
      p_backend->copyFile(link.m_path, destFilePath);
    } else {
      FileUtils::copyFile(link.m_path, destFilePath);
    }
  }

  if (!renamedImages.isEmpty()) {
    if (p_backend) {
      p_backend->writeFile(p_destFilePath, content);
    } else {
      FileUtils::writeFile(p_destFilePath, content);
    }
  }
}

void ContentMediaUtils::removeMediaFiles(Node *p_node) {
  Q_ASSERT(p_node->hasContent());
  auto file = p_node->getContentFile();
  if (file->getContentType().isMarkdown()) {
    removeMarkdownMediaFiles(file.data(), p_node->getBackend());
  }
}

void ContentMediaUtils::removeMarkdownMediaFiles(const File *p_file, INotebookBackend *p_backend) {
  auto content = p_file->read();

  // Images.
  const auto images = vte::MarkdownUtils::fetchImagesFromMarkdownText(
      content, p_file->getResourcePath(), vte::MarkdownLink::TypeFlag::LocalRelativeInternal);

  QSet<QString> handledImages;
  for (const auto &link : images) {
    if (handledImages.contains(link.m_path)) {
      continue;
    }

    handledImages.insert(link.m_path);

    if (!QFileInfo::exists(link.m_path)) {
      qWarning() << "image of Markdown file does not exist" << link.m_path << link.m_urlInLink;
      continue;
    }
    p_backend->removeFile(link.m_path);
  }
}

void ContentMediaUtils::copyAttachment(Node *p_node, INotebookBackend *p_backend,
                                       const QString &p_destFilePath,
                                       const QString &p_destAttachmentFolderPath) {
  Q_ASSERT(p_node->hasContent());
  Q_ASSERT(!p_node->getAttachmentFolder().isEmpty());

  // Copy the whole folder.
  const auto srcAttachmentFolderPath = p_node->fetchAttachmentFolderPath();
  try {
    if (p_backend) {
      p_backend->copyDir(srcAttachmentFolderPath, p_destAttachmentFolderPath);
    } else {
      FileUtils::copyDir(srcAttachmentFolderPath, p_destAttachmentFolderPath);
    }
  } catch (Exception &e) {
    qWarning() << "failed to copy attachment folder" << srcAttachmentFolderPath << e.what();
    return;
  }

  // Check if we need to modify links in content.
  // FIXME: check the whole relative path.
  if (p_node->getAttachmentFolder() == PathUtils::dirName(p_destAttachmentFolderPath)) {
    return;
  }

  auto file = p_node->getContentFile();
  if (file->getContentType().isMarkdown()) {
    fixMarkdownLinks(srcAttachmentFolderPath, p_backend, p_destFilePath,
                     p_destAttachmentFolderPath);
  }
}

void ContentMediaUtils::fixMarkdownLinks(const QString &p_srcFolderPath,
                                         INotebookBackend *p_backend, const QString &p_destFilePath,
                                         const QString &p_destFolderPath) {
  // TODO.
  Q_UNUSED(p_srcFolderPath);
  Q_UNUSED(p_backend);
  Q_UNUSED(p_destFilePath);
  Q_UNUSED(p_destFolderPath);
}
