import yaml
import json
import os
from datetime import datetime
from time import time
from sys import argv
from os.path import getmtime
from zipfile import ZipFile, ZIP_DEFLATED

DEFAULTS = {
    'IsHide': False,
    'IsTestingExclusive': False,
    'ApplicableVersion': 'any',
}

DUPLICATES = {
    'DownloadLinkInstall': ['DownloadLinkTesting', 'DownloadLinkUpdate'],
}

TRIMMED_KEYS = [
    'Author',
    'Name',
    'Description',
    'InternalName',
    'AssemblyVersion',
    'RepoUrl',
    'ApplicableVersion',
    'Tags',
    'DalamudApiLevel',
    'Punchline',
    'IconUrl',
    'ImageUrls',
]

def main():
    plugins_dir = argv[1] if len(argv) > 1 else 'plugins'
    master_name = 'pluginmaster.json' if plugins_dir == 'plugins' else f'{plugins_dir}.json'
    info = {
        'plugins_dir': plugins_dir,
        'master_name': master_name,
    }

    # extract the manifests from inside the zip files
    master = extract_manifests(info)

    # trim the manifests
    master = [trim_manifest(manifest) for manifest in master]

    # convert the list of manifests into a master list
    add_extra_fields(info, master)

    # write the master
    write_master(info, master)

    # update the LastUpdate field in master
    last_update(info)

def extract_manifests(info):
    manifests = []

    for dirpath, dirnames, filenames in os.walk(f'./{info["plugins_dir"]}'):
        if len(filenames) == 0 or 'latest.zip' not in filenames:
            continue
        plugin_name = dirpath.split('/')[-1]
        latest_zip = f'{dirpath}/latest.zip'
        with ZipFile(latest_zip) as z:
            manifest = json.loads(z.read(f'{plugin_name}.json').decode('utf-8'))
            manifests.append(manifest)

    return manifests

def add_extra_fields(info, manifests):
    for manifest in manifests:
        # generate the download link from the internal assembly name
        manifest['DownloadLinkInstall'] = f'https://git.anna.lgbt/anna/plugin_repo/raw/branch/main/{info["plugins_dir"]}/{manifest["InternalName"]}/latest.zip'
        # add default values if missing
        for k, v in DEFAULTS.items():
            if k not in manifest:
                manifest[k] = v
        # duplicate keys as specified in DUPLICATES
        for source, keys in DUPLICATES.items():
            for k in keys:
                if k not in manifest:
                    manifest[k] = manifest[source]

def write_master(info, master):
    # write as pretty json
    with open(info['master_name'], 'w') as f:
        json.dump(master, f, indent=4)

def trim_manifest(plugin):
    return {k: plugin[k] for k in TRIMMED_KEYS if k in plugin}

def last_update(info):
    with open(info['master_name']) as f:
        master = json.load(f)

    for plugin in master:
        latest = f'{info["plugins_dir"]}/{plugin["InternalName"]}/latest.zip'

        modified = 0
        with ZipFile(latest) as z:
            for zip_info in z.infolist():
                info_mod = datetime(*zip_info.date_time).timestamp()
                if info_mod > modified:
                    modified = int(info_mod)

        if modified == 0:
            modified = int(getmtime(latest))

        if 'LastUpdate' not in plugin or modified != int(plugin['LastUpdate']):
            plugin['LastUpdate'] = str(modified)

    with open(info['master_name'], 'w') as f:
        json.dump(master, f, indent=4)

if __name__ == '__main__':
    main()
