# -*- coding: utf-8 -*-

from invoke import task
from shutil import copyfile

import random
import string
import os
import sys

def apps():
    """
    Returns a list of installed apps
    """

    return [
        'barcode',
        'build',
        'common',
        'company',
        'label',
        'order',
        'part',
        'report',
        'stock',
        'InvenTree',
        'users',
    ]

def localDir():
    """
    Returns the directory of *THIS* file.
    Used to ensure that the various scripts always run
    in the correct directory.
    """
    return os.path.dirname(os.path.abspath(__file__))

def managePyDir():
    """
    Returns the directory of the manage.py file
    """

    return os.path.join(localDir(), 'InvenTree')

def managePyPath():
    """
    Return the path of the manage.py file
    """

    return os.path.join(managePyDir(), 'manage.py')

def manage(c, cmd, pty=False):
    """
    Runs a given command against django's "manage.py" script.

    Args:
        c - Command line context
        cmd - django command to run
    """

    c.run('cd {path} && python3 manage.py {cmd}'.format(
        path=managePyDir(),
        cmd=cmd
    ), pty=pty)

@task(help={'length': 'Length of secret key (default=50)'})
def key(c, length=50, force=False):
    """
    Generates a SECRET_KEY file which InvenTree uses for generating security hashes
    """

    SECRET_KEY_FILE = os.path.join(localDir(), 'InvenTree', 'secret_key.txt')

    # If a SECRET_KEY file does not exist, generate a new one!
    if force or not os.path.exists(SECRET_KEY_FILE):
        print("Generating SECRET_KEY file - " + SECRET_KEY_FILE)
        with open(SECRET_KEY_FILE, 'w') as key_file:
            options = string.digits + string.ascii_letters + string.punctuation

            key = ''.join([random.choice(options) for i in range(length)])

            key_file.write(key)

    else:
        print("SECRET_KEY file already exists - skipping")


@task(post=[key])
def install(c):
    """
    Installs required python packages, and runs initial setup functions.
    """

    # Install required Python packages with PIP
    c.run('pip3 install -U -r requirements.txt')

    # If a config.yaml file does not exist, copy from the template!
    CONFIG_FILE = os.path.join(localDir(), 'InvenTree', 'config.yaml')
    CONFIG_TEMPLATE_FILE = os.path.join(localDir(), 'InvenTree', 'config_template.yaml')

    if not os.path.exists(CONFIG_FILE):
        print("Config file 'config.yaml' does not exist - copying from template.")
        copyfile(CONFIG_TEMPLATE_FILE, CONFIG_FILE)


@task
def shell(c):
    """
    Open a python shell with access to the InvenTree database models.
    """

    manage(c, 'shell', pty=True)


@task
def superuser(c):
    """
    Create a superuser (admin) account for the database.
    """

    manage(c, 'createsuperuser', pty=True)

@task
def check(c):
    """
    Check validity of django codebase
    """

    manage(c, "check")

@task
def migrate(c):
    """
    Performs database migrations.
    This is a critical step if the database schema have been altered!
    """

    print("Running InvenTree database migrations...")
    print("========================================")

    manage(c, "makemigrations")
    manage(c, "migrate")
    manage(c, "migrate --run-syncdb")
    manage(c, "check")

    print("========================================")
    print("InvenTree database migrations completed!")


@task
def static(c):
    """
    Copies required static files to the STATIC_ROOT directory,
    as per Django requirements.
    """

    manage(c, "collectstatic")


@task(pre=[install, migrate, static])
def update(c):
    """
    Update InvenTree installation.

    This command should be invoked after source code has been updated,
    e.g. downloading new code from GitHub.

    The following tasks are performed, in order:

    - install
    - migrate
    - static
    """
    pass

@task
def translate(c):
    """
    Regenerate translation files.

    Run this command after added new translatable strings,
    or after adding translations for existing strings.
    """

    # Translate applicable .py / .html / .js files
    manage(c, "makemessages -e py -e html -e js")
    manage(c, "compilemessages")

@task
def style(c):
    """
    Run PEP style checks against InvenTree sourcecode
    """

    print("Running PEP style checks...")
    c.run('flake8 InvenTree')

@task
def test(c, database=None):
    """
    Run unit-tests for InvenTree codebase.
    """
    # Run sanity check on the django install
    manage(c, 'check')

    # Run coverage tests
    manage(c, 'test', pty=True)

@task
def coverage(c):
    """
    Run code-coverage of the InvenTree codebase,
    using the 'coverage' code-analysis tools.

    Generates a code coverage report (available in the htmlcov directory)
    """

    # Run sanity check on the django install
    manage(c, 'check')

    # Run coverage tests
    c.run('coverage run {manage} test {apps}'.format(
        manage=managePyPath(),
        apps=' '.join(apps())
    ))

    # Generate coverage report
    c.run('coverage html')

@task
def mysql(c):
    """
    Install packages required for using InvenTree with a MySQL database.
    """
    
    print('Installing packages required for MySQL')

    c.run('sudo apt-get install mysql-server libmysqlclient-dev')
    c.run('pip3 install mysqlclient')

@task
def postgresql(c):
    """
    Install packages required for using InvenTree with a PostgreSQL database
    """

    print("Installing packages required for PostgreSQL")

    c.run('sudo apt-get install postgresql postgresql-contrib libpq-dev')
    c.run('pip3 install psycopg2')

@task(help={'filename': "Output filename (default = 'data.json')"})
def export_records(c, filename='data.json'):
    """
    Export all database records to a file
    """

    # Get an absolute path to the file
    if not os.path.isabs(filename):
        filename = os.path.join(localDir(), filename)
        filename = os.path.abspath(filename) 

    print(f"Exporting database records to file '{filename}'")

    if os.path.exists(filename):
        response = input("Warning: file already exists. Do you want to overwrite? [y/N]: ")
        response = str(response).strip().lower()

        if response not in ['y', 'yes']:
            print("Cancelled export operation")
            sys.exit(1)

    cmd = f'dumpdata --exclude contenttypes --exclude auth.permission --indent 2 --output {filename}'

    manage(c, cmd, pty=True)

@task(help={'filename': 'Input filename'})
def import_records(c, filename='data.json'):
    """
    Import database records from a file
    """

    # Get an absolute path to the supplied filename
    if not os.path.isabs(filename):
        filename = os.path.join(localDir(), filename)

    if not os.path.exists(filename):
        print(f"Error: File '{filename}' does not exist")
        sys.exit(1)

    print(f"Importing database records from '{filename}'")

    cmd = f'loaddata {filename}'

    manage(c, cmd, pty=True)

@task
def import_fixtures(c):
    """
    Import fixture data into the database.

    This command imports all existing test fixture data into the database.

    Warning:
        - Intended for testing / development only!
        - Running this command may overwrite existing database data!!
        - Don't say you were not warned...
    """

    fixtures = [
        # Build model
        'build',
        
        # Common models
        'settings',

        # Company model
        'company',
        'price_breaks',
        'supplier_part',

        # Order model
        'order',

        # Part model
        'bom',
        'category',
        'params',
        'part',
        'test_templates',

        # Stock model
        'location',
        'stock_tests',
        'stock',
    ]

    command = 'loaddata ' + ' '.join(fixtures)

    manage(c, command, pty=True)

@task
def backup(c):
    """
    Create a backup of database models and uploaded media files.

    Backup files will be written to the 'backup_dir' file specified in 'config.yaml'

    """

    manage(c, 'dbbackup')
    manage(c, 'mediabackup')

@task(help={'address': 'Server address:port (default=127.0.0.1:8000)'})
def server(c, address="127.0.0.1:8000"):
    """
    Launch a (deveopment) server using Django's in-built webserver.

    Note: This is *not* sufficient for a production installation.
    """

    manage(c, "runserver {address}".format(address=address), pty=True)