# SPDX-FileCopyrightText: 2022 Chris Heywood
#
# SPDX-License-Identifier: GPL-3.0-or-later

from gettext import gettext as _
from gi.repository import GLib

import datetime
import json
import logging
import os
import re
from typing import Optional
import unicodedata

from iotas.markdown_helpers import get_image_attachments_from_note_content
from iotas.note import Note


def build_default_filename(
    note: Note, out_format: str, file_extension: str, add_timestamp: bool = False
) -> str:
    """Build an export filename for the note.

    :param Note note: Note to export
    :param str out_format: Export format
    :param str file_extension: File extension
    :param bool add_timestamp: Whether to prefix timestamp
    :return: Filename
    :rtype: str
    """
    filename = sanitise_title_for_filename(note.title)
    if add_timestamp:
        ts = datetime.datetime.now()
        filename = ts.strftime("%Y-%m-%dT%H:%M:%S") + " " + filename
    if not exporting_to_dir(out_format, note):
        filename += "." + file_extension
    return filename


def exporting_to_dir(out_format: str, note: Note) -> bool:
    """Determine whether note export to format will result in a directory.

    :param str out_format: Format
    :param Note note: Note to export
    :return: Whether exporting to dir
    :rtype: bool
    """
    if out_format == "md":
        export_to_dir = len(get_image_attachments_from_note_content(note)) > 0
    elif out_format == "html":
        export_to_dir = True
    else:
        export_to_dir = False
    return export_to_dir


def build_batch_dirname(ts: Optional[datetime.datetime] = None) -> str:
    """Build a directory name for bulk export.

    :param datetime.datetime ts: Timestamp, optional
    :return: Directory name
    :rtype: str
    """
    if not ts:
        ts = datetime.datetime.now()
    date = ts.strftime("%Y-%m-%d")
    time = ts.strftime("%H:%M:%S")
    # Translators: Directory name for batch export, {0} is a date (YYYY-MM-DD),
    # {1} is a time (HH:MM:SS)
    return _("{0} at {1} batch").format(date, time)


def sanitise_title_for_filename(title: str) -> str:
    """Sanitise title for a filesystem filename.

    For synced notes the server has already done the sanitising for us. This is for
    local-only instances.

    :param str title: Input title
    :return: Sanitised title
    :rtype: str
    """
    value = unicodedata.normalize("NFKC", str(title))
    return re.sub(r"[^\w\s\.-]", "", value).strip()


def parse_export_formats(formats: str) -> Optional[dict[str, dict[str, str]]]:
    """Parse custom Pandoc export formats from configuration string.

    :param str formats: Formats JSON string
    :return: Dict with Iotas format id as key and a dict with the Pandoc format and file extension
        as value, on success
    :rtype: dict[str, dict[str, str]]
    """
    if formats.strip() == "":
        return None
    try:
        formats_json = json.loads(formats)
    except json.JSONDecodeError as e:
        logging.warning(f"Failed to load extra formats: {e.msg}")
        return None

    if type(formats_json) is not list:
        logging.warning("Failed to load extra formats: JSON not being a list")
        return None

    out = {}

    for item in formats_json:
        if type(item) is not dict:
            logging.warning("Failed to load extra format: item not being an object")
            continue
        if "pandocOutFormat" not in item:
            logging.warning("Failed to load extra format: missing pandocOutFormat")
            continue
        if "fileExtension" not in item:
            logging.warning("Failed to load extra format: missing fileExtension")
            continue

        out_format = item["pandocOutFormat"]
        extension = item["fileExtension"]
        if type(out_format) is not str or out_format.strip() == "":
            logging.warning("Failed to load extra format: pandocOutFormat is not a string")
            continue
        if type(extension) is not str or extension.strip() == "":
            logging.warning("Failed to load extra format: due to fileExtension is not a string")
            continue

        if re.match(r"^[\w_]+$", out_format) is None:
            logging.warning(
                "Failed to load extra format: pandocOutFormat contains invalid characters"
            )
            continue
        if re.match(r"^[\w]+$", extension) is None:
            logging.warning(
                "Failed to load extra format: fileExtension contains invalid characters"
            )
            continue

        logging.debug(f"Adding extra pandoc format {out_format} with extension {extension}")

        format_id = out_format.lower()
        out[format_id] = {"format": out_format, "extension": extension}

    return out


def get_container_export_dir() -> str:
    """Get export directory within the user data directory.

    "Typically in the Flatpak container.

    :rtype: str
    """
    return os.path.join(
        GLib.get_user_data_dir(),
        "iotas",
        "exports",
    )
