#!/usr/bin/python3 -u

# This file originally lived in
# https://github.com/coreos/fedora-coreos-releng-automation. See that repo for
# archeological git research.

'''
    Implements the Fedora CoreOS versioning scheme as per:
        https://github.com/coreos/fedora-coreos-tracker/issues/81
        https://github.com/coreos/fedora-coreos-tracker/issues/211
'''

import argparse
import json
import os
import re
import subprocess
import sys
import time
import yaml

from datetime import datetime

# streams which don't use lockfiles
UNLOCKED_STREAMS = [
    'rawhide',
    'branched',
    'bodhi-updates-testing',
    'bodhi-updates',
]

# https://github.com/coreos/fedora-coreos-tracker/issues/211#issuecomment-543547587
STREAM_TO_NUM = {
    'next': 1,
    'testing': 2,
    'stable': 3,
    'next-devel': 10,
    'testing-devel': 20,
    'rawhide': 91,
    'branched': 92,
    'bodhi-updates-testing': 93,
    'bodhi-updates': 94,
}


def main():
    args = parse_args()
    if args.workdir is not None:
        os.chdir(args.workdir)
    assert os.path.isdir('builds'), 'Missing builds/ dir'

    manifest = get_flattened_manifest()
    x, y, z = (get_x(manifest), get_y(manifest), get_z(manifest))
    n = get_next_iteration(x, y, z)
    new_version = f'{x}.{y}.{z}.{n}'

    # sanity check the new version by trying to re-parse it
    assert parse_version(new_version) is not None
    print(new_version)


def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument('--workdir', help="path to cosa workdir")
    return parser.parse_args()


def get_x(manifest):
    """
        X is the Fedora release version on which we're based.
    """
    releasever = manifest['releasever']
    eprint(f"x: {releasever} (from manifest)")
    return int(releasever)


def get_y(manifest):
    """
        Y is the base snapshot date in YYYYMMDD format of Fedora. We derive
        this using the timestamp in the base lockfile.
    """

    stream = manifest['add-commit-metadata']['fedora-coreos.stream']

    # XXX: should sanity check that the lockfiles for all the basearches have
    # matching timestamps
    exts = ['json', 'yaml']
    for ext in exts:
        try:
            with open(f"src/config/manifest-lock.x86_64.{ext}") as f:
                lockfile = yaml.safe_load(f)
                generated = lockfile.get('metadata', {}).get('generated')
                if not generated:
                    raise Exception("Missing 'metadata.generated' key "
                                    f"from {lockfile}")
                dt = datetime.strptime(generated, '%Y-%m-%dT%H:%M:%SZ')
                assert stream not in UNLOCKED_STREAMS
                msg_src = "from lockfile"
                break
        except FileNotFoundError:
            continue
    else:
        # must be an unlocked stream
        assert stream in UNLOCKED_STREAMS
        msg_src = "unlocked stream"
        dt = datetime.now()

    ymd = dt.strftime('%Y%m%d')
    eprint(f"y: {ymd} ({msg_src})")
    return int(ymd)


def get_z(manifest):
    """
        Z is the stream indicator.
    """
    stream = manifest['add-commit-metadata']['fedora-coreos.stream']
    assert stream in STREAM_TO_NUM, f"Unknown stream: {stream}"
    mapped = STREAM_TO_NUM[stream]
    eprint(f"z: {mapped} (mapped from stream {stream})")
    return mapped


def get_next_iteration(x, y, z):
    try:
        with open('builds/builds.json') as f:
            builds = json.load(f)
    except FileNotFoundError:
        builds = {'builds': []}

    if len(builds['builds']) == 0:
        eprint("n: 0 (no previous builds)")
        return 0

    last_buildid = builds['builds'][0]['id']
    last_version = parse_version(last_buildid)
    if not last_version:
        eprint(f"n: 0 (previous version {last_buildid} does not match scheme)")
        return 0

    if (x, y, z) != last_version[:3]:
        eprint(f"n: 0 (previous version {last_buildid} x.y.z does not match)")
        return 0

    n = last_version[3] + 1
    eprint(f"n: {n} (incremented from previous version {last_buildid})")
    return n


def get_flattened_manifest():
    return yaml.safe_load(
        subprocess.check_output(['rpm-ostree', 'compose', 'tree',
                                 '--print-only', 'src/config/manifest.yaml']))


def parse_version(version):
    m = re.match(r'^([0-9]{2})\.([0-9]{8})\.([0-9]+)\.([0-9]+)$', version)
    if m is None:
        return None
    # sanity-check date
    try:
        time.strptime(m.group(2), '%Y%m%d')
    except ValueError:
        return None
    return tuple(map(int, m.groups()))


def eprint(*args):
    print(*args, file=sys.stderr)


if __name__ == "__main__":
    sys.exit(main())
