"""
# 🖋 editor - Open a text editor 🖋

`editor` opens the default text editor or your favorite editor to edit an existing file,
a new file, or a tempfile, blocks while the user edits text, then returns the contents
of the file.

You can pass a parameter `editor=` to specify an editor or leave it empty, in which
case the editor is:

* The contents of the environment variable `VISUAL`, if it's set, otherwise:
* The the contents of the environment variable `EDITOR`, if it's set, otherwise:
* The string `'Notepad'`, if the code is running on Windows, otherwise:
* The string `'vim'`

### Example 1: Using a temporary file

If no filename is provided, a temporary file gets edited, and its contents
returned.

    import editor

    comments = editor.editor(text='Comments here\\n\\n')
    # Pop up the default editor with a tempfile containing "Comments here",
    # then return the contents and delete the tempfile.

### Example 2: Using a named file

If a filename is provided, then that file gets edited.

    import os

    FILE = 'file.txt'
    assert not os.path.exists(FILE)

    comments = editor.editor(text=MESSAGE, filename=FILE)
    # Pop up an editor for a new FILE containing MESSAGE, user edits
    # This file is saved when the user exits the editor.

    assert os.path.exists(FILE)

    # You can edit an existing file too, and select your own editor.
    comments2 = editor.editor(filename=FILE, editor='emacs -nw')
"""

import os
import platform
import tempfile
import traceback
import typing as t
from pathlib import Path

import runs
import xmod

__all__ = 'editor', 'default_editor'

DEFAULT_EDITOR = 'vim'
EDITORS = {'Windows': 'notepad'}


@xmod.xmod(mutable=True)  # type: ignore[misc]
def editor(
    text: t.Optional[str] = None,
    filename: t.Union[None, Path, str] = None,
    editor: t.Optional[str] = None,
    **kwargs: t.Any,
) -> str:
    """
    Open a text editor, block while the user edits, then return the results

    Args:

      text: A string which is written to the file before the editor is opened.
          If `None`, the file is left unchanged.

      filename: The name of the file to edit.
          If `None`, a temporary file is used.

      editor: A string containing the command used to invoke the text editor.
         If `None`, use `editor.default_editor()`.

      kwargs: Arguments passed on to `subprocess.call()`"""
    editor = editor or default_editor()
    is_temp = not filename
    if filename is not None:
        fname = filename
    else:
        fd, fname = tempfile.mkstemp()
        os.close(fd)

    try:
        path = Path(fname)
        if text is not None:
            path.write_text(text)

        cmd = '{} "{}"'.format(editor, path.resolve())
        runs.call(cmd, **kwargs)
        return path.read_text()

    finally:
        if is_temp:
            try:
                path.unlink()
            except Exception:
                traceback.print_exc()


def default_editor() -> str:
    """
    Return the default text editor.

    The default text editor is the contents of the environment variable
    `EDITOR`, it it's non-empty, otherwise if the platform is Windows, it's
    `'notepad'`, otherwise `'vim'`.
    """
    return os.environ.get('VISUAL') or (
        os.environ.get('EDITOR') or EDITORS.get(platform.system(), DEFAULT_EDITOR)
    )
