#!/usr/bin/env python

import argparse
import cooked_input as ci
import os
import socket
import sys
import threading
import webbrowser

# If this script is being run out of a checked out `knowledge-repo` repository,
# we need to make sure the appropriate knowledge_repo package is being used. To
# do this, we add the parent directory of the folder containing this script if
# it contains a python package named "knowledge_repo".
script_dir = os.path.dirname(__file__)
if os.path.exists(os.path.join(os.path.dirname(script_dir), 'knowledge_repo', '__init__.py')):
    sys.path.insert(0, os.path.join(script_dir, '..'))

import knowledge_repo  # nopep8
from knowledge_repo import KnowledgePost  # nopep8

# Build argparser

parser = argparse.ArgumentParser(add_help=False, description='Tooling to aid with the authoring and submission of knowledge posts.')
parser.add_argument('--version', dest='version', action='store_true', help='Show version and exit.')
parser.add_argument('--non-interactive', dest='interactive', action='store_false', help='Run scripts in non-interactive mode.')
parser.add_argument('-h', '--help', action='store_true', help='Show help and exit.')

args, remaining_args = parser.parse_known_args()

if args.version:
    print('{}'.format(knowledge_repo.__version__))
    sys.exit(0)

# ------------------------------------------------------------------------------
# Everything below this line pertains to actions to be performed on a specific
# knowledge post.

parser.add_argument('post_path', help='The path of an existing knowledge post, or the target for a new knowledge post.')

# Add the action parsers
subparsers = parser.add_subparsers(help='actions')

from_ = subparsers.add_parser('from', help='Create a knowledge post from an existing document.')
from_.set_defaults(action='from')
from_.add_argument('source', help='The path or url of the source file.')
from_.add_argument('--format', help='The format to assume for the source file (overriding default detection algorithms).')
from_.add_argument('--src', nargs='+', help='Specify additional files to be added as source files.')
from_.add_argument('--overwrite', action='store_true', help='Overwrite any existing knowledge post.')

to = subparsers.add_parser('to', help='Export a knowledge post as another format.')
to.set_defaults(action='to')
to.add_argument('target', help='The path or url of the target location.')
to.add_argument('--format', help='The target format (overrides default detection algorithms).')

preview = subparsers.add_parser('preview', help='Preview a knowledge post in a local web server.')
preview.set_defaults(action='preview')

submit = subparsers.add_parser('submit', help='Submit a knowledge post to a nominated repository.')
submit.set_defaults(action='submit')
submit.add_argument(
    'repo', nargs='?', default=os.environ.get('KNOWLEDGE_REPO'),
    help=(
        "The repository into which the post should be submitted. (Defaults to "
        "$KNOWLEDGE_REPO, which is currently {})".format(
            os.environ['KNOWLEDGE_REPO'].__repr__() if 'KNOWLEDGE_REPO' in os.environ else 'unset'
        )
    )
)
submit.add_argument(
    'path', help="The path of the post within the repository."
)
submit.add_argument('--update', action='store_true', help='Whether this post should replace existing posts at the same path.')
submit.add_argument('--message', help="A commit message describing this post and/or its changes.")

args = parser.parse_args()

if args.help:
    parser.print_help()
    sys.exit(0)

if not args.post_path.endswith('.kp'):
    args.post_path += '.kp'

if args.action == 'from':
    if not args.overwrite and os.path.exists(args.post_path):
        if args.interactive:
            args.overwrite = ci.get_boolean(prompt="File already exists at '{}'. Do you want to overwrite it? (y/n)".format(args.post_path))
        if not args.overwrite:
            raise RuntimeError("File already exists at '{}', but the `overwrite` flag is not set.".format(args.post_path))
    kp = KnowledgePost.from_file(args.source, format=args.format, src_paths=args.src, interactive=args.interactive)
    kp.to_file(args.post_path, format='kp')
    sys.exit(0)

# ------------------------------------------------------------------------------
# Everything below this line requires the knowledge post to already exist to
# actions to be performed on a specific knowledge post.
if not os.path.exists(args.post_path):
    raise IOError("Knowledge post does not exist at '{}'.".format(os.path.abspath(args.post_path)))

kp = KnowledgePost.from_file(args.post_path, format='kp', interactive=args.interactive)

if args.action == 'to':
    kp.to_file(args.target, format=args.format)
    sys.exit(0)

if args.action == 'preview':
    from knowledge_repo.app.deploy import KnowledgeDeployer, get_app_builder

    def get_available_port():
        s = socket.socket()
        s.bind(("", 0))
        free_port = s.getsockname()[1]
        s.close()
        return free_port
    port = get_available_port()

    post_path = os.path.abspath(args.post_path)
    repo_dir = os.path.dirname(post_path)
    post_path = os.path.basename(post_path)

    app_builder = get_app_builder('file://' + repo_dir,
                                  debug=False,
                                  db_uri='sqlite:///:memory:',
                                  config=None,
                                  INDEXING_ENABLED=False)

    url = 'http://localhost:{}/post/{}'.format(port, post_path)
    print(
        "Previewing knowledge post at: {}\n\n"
        "If you are using `kp` locally, a browser window will shortly open at "
        "this address. Otherwise, please replace `localhost` with the hostname "
        "of the server upon which you are running this script, and manually "
        "point your browser at the resulting url.\n\n"
        "When you are ready to exit the preview, simply kill this process using "
        "`<Ctrl> + C`.\n"
        .format(url)
    )
    threading.Timer(1.25, lambda: webbrowser.open(url)).start()

    KnowledgeDeployer.using('flask')(
        app_builder,
        host='0.0.0.0',
        port=port
    ).run()
    sys.exit(0)

if args.action == 'submit':
    if not args.repo:
        raise RuntimeError("Repository not specified.")
    repo = knowledge_repo.KnowledgeRepository.for_uri(args.repo)

    repo.add(kp, path=args.path, update=args.update, message=args.message)
    repo.submit(args.path)
    sys.exit(0)
