#!/usr/bin/env python2
import argparse
import os
import shutil
import random
import sys
import hashlib

# Prefer local checkout GitGate to installed version
gglocal = os.path.dirname(os.path.realpath(__file__))
ggpath = os.path.join(gglocal, '../../')
sys.path.insert(0, ggpath)

def gen_random_string(length=20, letters=True, numbers=True, symbols=True):
    strn = ''
    chars = []
    if letters:
        chars += ['a','b','c','d','e','f','g','h','i','j','k',
            'L','m','n','o','p','q','r','s','t','u','v','w',
            'x','y','z']
    if numbers:
        chars += ['0','1','2','3','4','5','6','7','8','9']
    if symbols:
        chars += ['@','$','^','&','_','-','+','#']

    if not chars:
        raise Exception('No characters specified')
    while len(strn) < length:
        strn += random.choice(chars)
    return strn

def write_daemon(data):
    path = data['path']
    daemon = """#!/usr/bin/env python2
import sys
from gitgate import daemon
# Add hooks with daemon.on_commit or daemon.on_merge
#   Example: daemon.on_commit = my_local_function
#   Arguments: commit, the commit object
if __name__ == "__main__":
    daemon.start()
"""
    fh = open(os.path.join(path, 'gitgate-run.py'), 'w')
    fh.write(daemon)
    fh.close()

def write_testserver(data):
    path = data['path']
    testserver = """#!/usr/bin/env python2
#!/usr/bin/env python
import sys
import ggconf
from gitgate.webapp import app

if __name__ == "__main__":
    app.debug = True
    app.secret_key = ggconf.SECRET_KEY
    app.config['URL_PREFIX'] = ggconf.URL_PREFIX
    app.config['GG_DATABASE'] = ggconf.DATABASE
    app.run(host='0.0.0.0')
"""
    fh = open(os.path.join(path, 'testserver.py'), 'w')
    fh.write(testserver)
    fh.close()

def write_config(data):
    path = data['path']
    if not os.path.exists(path):
        os.makedirs(path)

    content = """
# Full path to Sqlite3 database file
DATABASE = '''%(database)s'''
# Secret key - keep this super secret
SECRET_KEY = '''%(secret_key)s'''
# Url prefix, empty string for none
URL_PREFIX = '''%(url_prefix)s'''
"""%(data)
    fh = open(os.path.join(path, 'ggconf.py'), 'w')
    fh.write(content)
    fh.close()

def create_site(path=None, defaults=False):

    """ Creates a new site at <path> """

    if not path:
        path = os.getcwd()

    database = os.path.join(path, 'global.db')
    secret_key = gen_random_string(40)
    url_prefix = ''
    admin_user = 'Admin'
    admin_pass = 'password'
    admin_email = 'root@localhost'

    if not defaults:
        path = raw_input("Path [%s] "%(path)) or path
        database = raw_input("Database location [%s] "%(os.path.join(path, 'global.db'))) \
            or os.path.join(path, 'global.db')
        secret_key = raw_input("Secret key [%s] "%(secret_key)) or secret_key
        url_prefix = raw_input("Url prefix [%s] "%(url_prefix)) or ''
        if url_prefix.endswith('/'):
            url_prefix = url_prefix[:-1]
        admin_user = raw_input("Admin user name [%s] "%(admin_user)) or admin_user
        admin_email = raw_input("Admin email [%s] "%(admin_email)) or admin_email
        admin_pass = raw_input("Admin password [%s] "%(admin_pass)) or admin_pass
 

    if os.path.exists(path) and not os.listdir(path) == []:
        print("Site path already exists and isn't empty!")
        return False
    
    # We need to write the config first
    # as a hack to PeeWee's weird database
    # configuration requirements
    write_config(locals()) 

    sys.path.insert(0, path)
    import gitgate.models as md
    md.create_tables()
    md.populate_data()
    admin_user = md.User.create(name=admin_user, 
        password=hashlib.sha1(admin_pass).hexdigest(),
        email=admin_email, is_admin=True)

    write_daemon(locals())
    write_testserver(locals())    

def delete_site(path=None, force=False):
    
    """ Deletes a site at <path> """

    if not path:
        path = os.getcwd()

    if not os.path.exists(os.path.join(path, 'ggconf.py')):
        print("Could not find site at %s"%(path))
        return False

    yn = raw_input('Are you sure you want to delete %s? [y/n] '%(path))
    if yn.lower() in ['y','yes']:
        shutil.rmtree(path)
        print("Site deleted!")

def create_user(email, defaults=False):

    """ Creates a new user """

    import ggconf
    import gitgate.models as md

    is_admin = False
    name = 'newuser1'
    password = 'password'
   
    if not defaults: 
        name = raw_input('New user name: ')
        if not name:
            print("You must specify a name")
            return

        email = raw_input('New user email: [%s] '%(email)) or email
        if len(email) < 6:
            print("Invalid email address")
            return

        try:
            u = md.User.get(email=email)
            print("Email address alread in use")
            return
        except:
            pass

        password = raw_input('New user password (they can change this later): ')
        if len(password) < 6:
            print("Passwords should be at least 6 characters")
            return
        admin = raw_input('Is this user an admin? [y/N] ')
        if admin.lower() in ['y','yes']:
            is_admin = True

    new_user = md.User.create(name=name, email=email, is_admin=is_admin, 
        password=hashlib.sha1(password).hexdigest())
    print("New user created")
    return True

def delete_user(email, force=False):

    """ Deletes a user by their email address """

    import ggconf
    import gitgate.models as md

    try:
        user = md.User.get(email=email)
    except:
        print("Unable to locate user: %s"%(email))
        return False

    if not force:
        yn = raw_input("Are you sure you want to delete user: %s? [y/n] "%(user.name))
        if yn.lower() not in ['y','yes']:
            sys.exit(0)

    print("User deleted!")    
    user.delete_instance()
    return True
    
def create_project(path=None, name=None, devel_clone_url=None, stable_clone_url=None):

    """ Creates a new project at <path> """

    if not name:
        name = raw_input("Project name: ")
        if len(name) < 3:
            print("Name must be at least 3 characters")
            return False

    if not path:
        path = os.getcwd()

    project_path = os.path.join(path, 'projects', name)
    if os.path.exists(project_path):
        print("Project already exists: %s"%(project_path))
        return False
    
    if not devel_clone_url:
        devel_clone_url = raw_input("Development clone url: ")
        if len(devel_clone_url) < 5:
            print("Invalid clone url")
            return False

    if not stable_clone_url:
        stable_clone_url = raw_input("Stable clone url: ")
        if len(stable_clone_url) < 5:
            print("Invalid clone url")
            return False

    import ggconf
    import gitgate.models as md

    try:
        prj1 = md.Project.get(path=project_path)
        prj2 = md.Project.get(name=name)
        print("Project already exists!")
        return False
    except:
        pass

    new_project = md.Project.create(path=project_path, 
        name=name, devel_clone_url=devel_clone_url,
        stable_clone_url=stable_clone_url)
    new_project.git_control.create_stable_checkout()
    new_project.git_control.create_devel_checkout()
    print("Project created!")
    return True

def delete_project(path=None, name=None, force=False):
    if not path:
        path = os.getcwd()

    project_path = os.path.join(path, 'projects', name)
    if not os.path.exists(project_path):
        print("Could not locate project at: %s"%(project_path))
        return False

    if not force:
        yn = raw_input("Are you sure you want to delete project: %s [y/n] "%(name))
        if yn.lower() not in ['y','yes']:
            return False

    try:
        prj = md.Project.get(name=name)
        prj.delete_instance()
    except:
        print("Could not find database entry for project: %s %s"%(name, project_path))
        return False

    shutil.rmtree(project_path)
    print("Deleted project!")
    return True

if __name__ == "__main__":

    parser = argparse.ArgumentParser()
    parser.add_argument('-p', '--site-path',
        help='the GitGate site directory, defaults to current directory',
        default=os.getcwd())
    
    subparsers = parser.add_subparsers(dest='command')

    p_site = subparsers.add_parser('site',
        help='create and delete sites')
    p_site.add_argument('site_command', choices=['create','delete'])
    p_site.add_argument('-f', '--force', action="store_true",
        help='do not prompt for input')

    p_user = subparsers.add_parser('user',
        help='create and delete users')
    p_user.add_argument('user_command', choices=['create','delete'])
    p_user.add_argument('email', help='users email address')
    p_user.add_argument('-f', '--force', action="store_true", 
        help="do not prompt for input where possible")

    p_project = subparsers.add_parser('project',
        help='create and delete projects')
    p_project.add_argument('project_command', choices=['create','delete'])
    p_project.add_argument('project_name', help='project name, must be unique')
    p_project.add_argument('-f', '--force', action="store_true", 
        help="do not prompt for input where possible")

    args = parser.parse_args()

    sys.path.insert(0, args.site_path)

    if args.command == "site":
        if args.site_command == "create":
            create_site(path=args.site_path, defaults=args.force)    
        elif args.site_command == "delete":
            delete_site(path=args.site_path, force=args.force)

    elif args.command == "user":
        if args.user_command == "create":
            create_user(email=args.email, defaults=args.force)
        elif args.user_command == "delete":
            delete_user(email=args.email, force=args.force)
   
    elif args.command == "project":
        if args.project_command == "create":
            create_project(path=args.site_path, name=args.project_name)
        elif args.project_command == "delete":
            delete_project(path=args.site_path, name=args.project_name, force=args.force)
