#!/usr/bin/env python3
import secrets
import configparser
import shutil
import argparse
import logging
import threading
import signal
import sys
from pathlib import Path
from flask import Flask, g

parser = argparse.ArgumentParser(description="jellyfin-accounts")

parser.add_argument("-c", "--config",
                    help="specifies path to configuration file.")
parser.add_argument("-d", "--data",
                    help=("specifies directory to store data in. " +
                          "defaults to ~/.jf-accounts."))
parser.add_argument("--host",
                    help="address to host web ui on.")
parser.add_argument("-p", "--port",
                    help="port to host web ui on.")
parser.add_argument("-g", "--get_policy",
                    help=("tool to grab a JF users " +
                    "policy (access, perms, etc.) and " +
                    "output as json to be used as a user template."),
                    action='store_true')

args, leftovers = parser.parse_known_args()

if args.data is not None:
    data_dir = Path(args.data)
else:
    data_dir = Path.home() / '.jf-accounts'

local_dir = (Path(__file__).parents[2] / 'data').resolve()

first_run = False
if data_dir.exists() is False or (data_dir / 'config.ini').exists() is False:
    if not data_dir.exists():
        Path.mkdir(data_dir)
        print(f'Config dir not found, so created at {str(data_dir)}')
    if args.config is None:
        config_path = data_dir / 'config.ini'
        shutil.copy(str(local_dir / 'config-default.ini'),
                    str(config_path))
        print("Setup through the web UI, or quit and edit the configuration manually.")
        first_run = True
    else:
        config_path = Path(args.config)
    print(f'config.ini can be found at {str(config_path)}')
else:
    config_path = data_dir / 'config.ini'

config = configparser.RawConfigParser()
config.read(config_path)

def create_log(name):
    log = logging.getLogger(name)
    handler = logging.StreamHandler(sys.stdout)
    if config.getboolean('ui', 'debug'):
        log.setLevel(logging.DEBUG)
        handler.setLevel(logging.DEBUG)
    else:
        log.setLevel(logging.INFO)
        handler.setLevel(logging.INFO)
    fmt = ' %(name)s - %(levelname)s - %(message)s'
    format = logging.Formatter(fmt)
    handler.setFormatter(format)
    log.addHandler(handler)
    log.propagate = False
    return log

log = create_log('main')
web_log = create_log('waitress')
if not first_run:
    email_log = create_log('emails')
    auth_log = create_log('auth')

if args.host is not None:
    log.debug(f'Using specified host {args.host}')
    config['ui']['host'] = args.host
if args.port is not None:
    log.debug(f'Using specified port {args.port}')
    config['ui']['port'] = args.port

for key in config['files']:
    if config['files'][key] == '':
        if key != 'custom_css':
            log.debug(f'Using default {key}')
            config['files'][key] = str(data_dir / (key + '.json'))


def default_css():
    css = {}
    css['href'] = "https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
    css['integrity'] = "sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
    css['crossorigin'] = "anonymous"
    return css


css = {}
css = default_css()
if 'custom_css' in config['files']:
    if config['files']['custom_css'] != '':
        try:
            shutil.copy(config['files']['custom_css'],
                        (local_dir / 'static' / 'bootstrap.css'))
            log.debug('Loaded custom CSS')
            css['href'] = '/bootstrap.css'
            css['integrity'] = ''
            css['crossorigin'] = ''
        except FileNotFoundError:
            log.error(f'Custom CSS {config["files"]["custom_css"]} not found, using default.')


if ('email_html' not in config['password_resets'] or
        config['password_resets']['email_html'] == ''):
    log.debug('Using default password reset email HTML template')
    config['password_resets']['email_html'] = str(local_dir / 'email.html')
if ('email_text' not in config['password_resets'] or
        config['password_resets']['email_text'] == ''):
    log.debug('Using default password reset email plaintext template')
    config['password_resets']['email_text'] = str(local_dir / 'email.txt')

if ('email_html' not in config['invite_emails'] or
        config['invite_emails']['email_html'] == ''):
    log.debug('Using default invite email HTML template')
    config['invite_emails']['email_html'] = str(local_dir /
                                                'invite-email.html')
if ('email_text' not in config['invite_emails'] or
        config['invite_emails']['email_text'] == ''):
    log.debug('Using default invite email plaintext template')
    config['invite_emails']['email_text'] = str(local_dir /
                                                'invite-email.txt')
if ('public_server' not in config['jellyfin'] or
        config['jellyfin']['public_server'] == ''):
    config['jellyfin']['public_server'] = config['jellyfin']['server']

if args.get_policy:
    import json
    from jellyfin_accounts.jf_api import Jellyfin
    jf = Jellyfin(config['jellyfin']['server'],
              config['jellyfin']['client'],
              config['jellyfin']['version'],
              config['jellyfin']['device'],
              config['jellyfin']['device_id'])
    # No auth needed.
    print("Make sure the user is publicly visible!")
    users = jf.getUsers()
    for index, user in enumerate(users):
        print(f'{index+1}) {user["Name"]}')
    success = False
    while not success:
        try:
            policy = users[int(input(">: "))-1]['Policy']
            success = True
        except (ValueError, IndexError):
            pass
    with open(config['files']['user_template'], 'w') as f:
        f.write(json.dumps(policy, indent=4))
    print(f'Policy written to "{config["files"]["user_template"]}".')
    print('In future, this policy will be copied to all new users.')
else:
    def signal_handler(sig, frame):
        print('Quitting...')
        sys.exit(0)

    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)
    app = Flask(__name__, root_path=str(local_dir))
    app.config['DEBUG'] = config.getboolean('ui', 'debug')
    app.config['SECRET_KEY'] = secrets.token_urlsafe(16)

    if __name__ == '__main__':
        from waitress import serve
        if first_run:
            import jellyfin_accounts.setup
            host = config['ui']['host']
            port = config['ui']['port']
            log.info('Starting web UI for first run setup...')
            serve(app,
                   host=host,
                   port=port)
        else:
            import jellyfin_accounts.web_api
            import jellyfin_accounts.web
            host = config['ui']['host']
            port = config['ui']['port']
            log.info(f'Starting web UI on {host}:{port}')
            if config.getboolean('password_resets', 'enabled'):
                def start_pwr():
                    import jellyfin_accounts.pw_reset
                    jellyfin_accounts.pw_reset.start()
                pwr = threading.Thread(target=start_pwr, daemon=True)
                log.info('Starting email thread')
                pwr.start()

            serve(app,
                  host=host,
                  port=int(port))
