#!/usr/bin/env python3

import argparse
import os
import json
import subprocess
from typing_extensions import NoReturn

os.chdir(os.path.join(os.path.dirname(__file__), '..'))
STATIC_PATH = 'static/'


def build_for_prod_or_casper(quiet: bool) -> NoReturn:
    """Builds for production, writing the output to disk"""

    webpack_args = ['node', 'node_modules/.bin/webpack-cli',
                    '--config', 'tools/webpack.config.ts', '-p',
                    '--env', 'production']
    if quiet:
        webpack_args += ['--display', 'errors-only']
    os.execvp(webpack_args[0], webpack_args)

def build_for_dev_server(host: str, port: str, minify: bool, disable_host_check: bool) -> None:
    """watches and rebuilds on changes, serving files from memory via webpack-dev-server"""

    # This is our most dynamic configuration, which we use for our
    # dev server.  The key piece here is that we identify changes to
    # files as devs make edits and serve new assets on the fly.
    webpack_args = ['node', 'node_modules/.bin/webpack-dev-server']
    webpack_args += [
        '--config',
        'tools/webpack.config.ts',
        # webpack-cli has a bug where it ignores --watch-poll with
        # multi-config, and we don't need the katex-cli part anyway.
        '--config-name', 'frontend',
        '--allowed-hosts', ','.join([host, '.zulipdev.com', '.zulipdev.org']),
        '--host', host,
        '--port', port,
        # We add the hot flag using the cli because it takes care
        # of addition to entry points and adding the plugin
        # automatically
        '--hot'
    ]
    if minify:
        webpack_args.append('--optimize-minimize')
    if disable_host_check:
        webpack_args.append('--disable-host-check')

    # Tell webpack-dev-server to fall back to periodic polling on
    # filesystems where inotify is known to be broken.
    cwd = os.getcwd()
    inotify_broken = False
    with open('/proc/self/mounts') as mounts:
        for line in mounts:
            fields = line.split()
            if fields[1] == cwd:
                inotify_broken = fields[2] in ["nfs", "vboxsf"]
    if inotify_broken:
        webpack_args.append('--watch-poll=1000')

    # TODO: This process should also fall back to periodic polling if
    # inotify_broken.
    import pyinotify
    try:
        webpack_process = subprocess.Popen(webpack_args)

        class WebpackConfigFileChangeHandler(pyinotify.ProcessEvent):
            def process_default(self, event: pyinotify.Event) -> None:
                nonlocal webpack_process
                print('Restarting webpack-dev-server due to config changes...')
                webpack_process.terminate()
                webpack_process.wait()
                webpack_process = subprocess.Popen(webpack_args)

        watch_manager = pyinotify.WatchManager()
        event_notifier = pyinotify.Notifier(watch_manager, WebpackConfigFileChangeHandler())
        for file in ['webpack.config.ts', 'webpack-helpers.ts', 'webpack.assets.json']:
            filepath = os.path.join(os.path.dirname(__file__), file)
            watch_manager.add_watch(filepath, pyinotify.IN_MODIFY)
        event_notifier.loop()
    finally:
        webpack_process.terminate()
        webpack_process.wait()

def build_for_most_tests() -> None:
    """Generates a stub asset stat file for django so backend test can render a page"""

    # Tests like test-backend, test-api, and test-home-documentation use
    # our "test" configuration.  The one exception is Casper, which uses
    # our production configuration.
    #
    # It's not completely clear why we don't also use the same
    # configuration for ALL tests, but figuring out the full history here
    # was out of the scope of the effort here to add some comments and
    # clean up names.
    entries = {}
    with open('tools/webpack.assets.json') as json_data:
        for entry in json.load(json_data).keys():
            entries[entry] = [{
                "name": "%s.js" % (entry,),
                "publicPath": "http://localhost:3000/webpack-stub/%s-stubentry.js" % (entry,),
                "path": "/stubfolder/%s-stubfile.js" % (entry,)
            }]
    stat_data = {
        "status": "done",
        "chunks": entries,
        "entryPoints": {name: [chunk] for name, chunk in entries.items()},
    }
    directory = 'var'
    if not os.path.exists(directory):
        os.makedirs(directory)
    with open(os.path.join(directory, 'webpack-stats-test.json'), 'w') as outfile:
        json.dump(stat_data, outfile)


parser = argparse.ArgumentParser()
parser.add_argument('--test',
                    action='store_true', dest='test', default=False,
                    help='generate a stub webpack-stats.json file (for backend testing)')
parser.add_argument('--quiet',
                    action='store_true', dest='quiet', default=False,
                    help='Minimizes webpack output while running')
parser.add_argument('--watch',
                    action='store_true', dest='watch', default=False,
                    help='watch for changes to source files (for development)')
parser.add_argument('--host',
                    action='store', dest='host',
                    default='127.0.0.1', help='set the host for the webpack server to run on')
parser.add_argument('--port',
                    action='store', dest='port',
                    default='9994', help='set the port for the webpack server to run on')
parser.add_argument('--minify',
                    action='store_true', dest='minify', default=False,
                    help='Minify and optimize the assets (for development)')
parser.add_argument('--disable-host-check',
                    action='store_true', dest='disable_host_check', default=None,
                    help='Disable host check for webpack-dev-server')

args = parser.parse_args()
if "CASPER_TESTS" in os.environ:
    build_for_prod_or_casper(args.quiet)
elif args.test:
    build_for_most_tests()
elif args.watch:
    build_for_dev_server(args.host, args.port, args.minify, args.disable_host_check)
else:
    build_for_prod_or_casper(args.quiet)
