#! python
"""
This is an alternative to `git_hooks_post-receive.sh`
The post commit hook in Gitea would include something like the following to ensure this script is called.

#!/bin/sh
read oldrev newrev ref
python "D:\dev\websites\sandpoints-dev\custom_syadmin\git_hooks_post-receive.py" \
--git_repo "D:\dev\websites\pirate-care-syllabus\gitea\gitea-repositories\gitea\pirate-care-syllabus.git" \
--website "D:\dev\websites\testing-ground\sandpoints" \
--website_preview "D:\dev\websites\testing-ground\sandpoints\_preview" \
--library "D:\dev\websites\pirate-care-syllabus\_library" \
--tmp_dir "D:\tmp\tmp"
--oldrev $oldrev \
--newrev $newrev \
--ref $ref

To test this script from the command line without relying on a commit hook being triggered, use:

python "D:\dev\websites\sandpoints-dev\custom_syadmin\git_hooks_post-receive.py" --ref refs/heads/master --git_repo "D:\dev\websites\pirate-care-syllabus\gitea\gitea-repositories\gitea\pirate-care-syllabus.git" --website D:\\dev\\websites\\testing-ground\\sandpoints --website_preview D:\\dev\\websites\\testing-ground\\sandpoints\\_preview --tmp_dir D:\\tmp\\tmp --git_url "http://localhost:3000/gitea/pirate-care-syllabus" --base_url "http://localhost:8000"

Just a mental note to read refs from the post-receive hook like this:

oldrev,newrev,refname = sys.stdin.readline().strip().split(' ')

"""
import os
import argparse
import random
import string
import subprocess
import shutil
import stat
from datetime import datetime

# format for dates
date_format = "%m/%d/%Y, %H:%M:%S"

# Set the variables we'll be allowing for (as CL arg or environment variable)
vars = {
    'oldrev': False, # set programmatically by git hook
    'newrev': False, # set programmatically by git hook
    'ref': False, # set programmatically by git hook
    'library': '_library',
    'website': '_website',
    'website_preview': '_websitepreview',
    'git_repo': False,
    'base_url': False, #override the site's base
    'git_url': False,
    'git_path': 'git',
    'hugo_path': 'hugo',
    'tmp_dir': '/tmp',
    'force': False,
    'branch': None,
    'preview_branch': None,
}

# logging time
start_time = datetime.now().strftime(date_format)

def random_string(length=3):
    """ Generates a random string """
    letters = string.ascii_lowercase
    return ''.join(random.choice(letters) for i in range(length))


def get_value(name, default=False):
    """ Variables can be set as environment variable or in command line """
    if hasattr(args, name.lower()) and getattr(args, name.lower()) is not None:
        print('CLI:', name, getattr(args, name.lower()))
        return getattr(args, name.lower())
    elif name.upper() in os.environ:
        print('env:', name, os.environ[name.upper()])
        return os.environ[name.upper()]
    else:
        print('default:', name, default)
        return default


def cmd(parts, cwd=None, env=None):
    """ Executes a shell command and returns the output """
    print(f"Command: {' '.join(parts)} ({cwd})")
    return subprocess.check_output(parts, cwd=cwd, env=env, universal_newlines=True).strip()


def rmrf(path, keep_dir=False):
    """ Use safe-rm for recursive removal """
    """ @TODO: make it safer """
    def remove_readonly(func, path, excinfo):
        os.chmod(path, stat.S_IWRITE)
        func(path)
    # Try removal
    if keep_dir and os.path.exists(path) and len(os.path.realpath(path)) > 1:
        for root, dirs, files in os.walk(path):
            for f in files:
                os.unlink(os.path.join(root, f))
            for d in dirs:
                shutil.rmtree(os.path.join(root, d), onerror=remove_readonly)
    elif os.path.exists(path) and len(os.path.realpath(path)) > 1:
        shutil.rmtree(path, onerror=remove_readonly)
    else:
        print("Either the path doesn't exist or you are trying to delete the root directory(?!):", path)


def clobber(filepath, data, append=False):
    """ Equivalent of > or >> when append is True """
    mode = 'a' if append else 'w'
    with open(filepath, mode) as f:
        f.write(data)


def build_site(dest, tmp, branch=None, hugo_environment='gitea'):
    """ builds the website to "dest" using "tmp" as an intermediate location """
    global GIT_PATH, GIT_REPO, HUGO_PATH
    if branch:
        print("Cloning branch: ", branch)
        cmd([GIT_PATH, 'clone', '--branch', branch, '.', tmp], cwd=GIT_REPO)
        #print(' '.join([GIT_PATH, 'archive', '--format', 'tar', branch, '|', 'tar', '-x', '-C', tmp]))
        #subprocess.call(' '.join([GIT_PATH, 'archive', '--format', 'tar', branch, '|', 'tar', '-x', '-C', tmp]), shell=True, cwd=GIT_REPO)
    else:
        cmd([GIT_PATH, 'clone', '.', tmp], cwd=GIT_REPO)
    rmrf(os.path.join(tmp, '.git'))
    rmrf(dest, keep_dir=True)
    try:
        if not os.path.exists(dest):
            os.makedirs(dest)
            print("Build destination created: ", dest)
        else:
            print("Build destination exists: ", dest)
    except:
        print(f"Error creating the directory: {dest}")
    lcl = f'{tmp}/last-commit-log.txt'
    # overriding hugo config for development environments 
    env = os.environ.copy()
    if BASE_URL:
        env["HUGO_PARAMS_BASEURL"] = BASE_URL
    if  GIT_URL:
        env["HUGO_PARAMS_GITURL"] = GIT_URL
    # run the hugo command
    hugo_output = cmd([HUGO_PATH, '-e', hugo_environment, '-d', dest], cwd=tmp, env=env)
    clobber(lcl, hugo_output)
    now_time = datetime.now().strftime(date_format)
    clobber(lcl, f"\n{start_time}\n{now_time}", append=True)
    shutil.move(lcl, dest)


# Parsing command line arguments
parser = argparse.ArgumentParser()
for v in vars:
    parser.add_argument(f"--{v.lower()}")
args = parser.parse_args()

# Load all variables from command line arguments or environment variables
for v in vars:
    globals()[v.upper()] = get_value(v.lower(), vars[v])

# set up directories
os.makedirs(WEBSITE, exist_ok=True)
os.makedirs(WEBSITE_PREVIEW, exist_ok=True)
TMP_WEBSITE = os.path.join(TMP_DIR, 'website', random_string())
TMP_WEBSITE_PREVIEW =  os.path.join(TMP_DIR, 'websitepreview', random_string())
os.makedirs(TMP_WEBSITE, exist_ok=True)
os.makedirs(TMP_WEBSITE_PREVIEW, exist_ok=True)

# start working
# Check if someone edited the publish trigger
published = False
refs = cmd([GIT_PATH, 'diff-tree', '--no-commit-id', '--name-only', REF], cwd=GIT_REPO).split("\n")
for r in refs:
    if FORCE or r == "PUBLISH.trigger.md":
        build_site(WEBSITE, TMP_WEBSITE, branch=BRANCH)
        published = True
# Check if there is a !publish!
if not published:
    refs = cmd([GIT_PATH, 'show', '--format="%s"', '-s'], cwd=GIT_REPO).split(' ')
    for r in refs:
        if r == "!publish!":
            build_site(WEBSITE, TMP_WEBSITE, branch=BRANCH)
            published = True
# Let the world know if there hasn't been a publish
if not published:
    print("The site wasn't build because there was no publish trigger, nor was it forced.")
# create preview version
build_site(WEBSITE_PREVIEW, TMP_WEBSITE_PREVIEW, branch=PREVIEW_BRANCH, hugo_environment='preview')

# Clean up temp directories
rmrf(TMP_WEBSITE)
rmrf(TMP_WEBSITE_PREVIEW)

# @TODO: link the library