#!/usr/bin/env python3

"""
Update (or check) the contents of release_date.rs,
in preparation for a software release.
"""

import argparse
import calendar
import os
import sys
import re
import time
from pathlib import Path


def parse_date(s: str) -> time.struct_time:
    return time.strptime(s, "%Y-%m-%d")


WARNING_TEXT = """\
// DO NOT EDIT MANUALLY.
// This file was automatically generated by maint/update-release-date.
"""

SEC_PER_DAY = 86400

DATE_RE = re.compile(r"const ARTI_CLIENT_RELEASE_DATE: &str = \"([-\d]*)\";")


def check_date(filename: Path, date: time.struct_time, tolerance: int):
    contents = filename.open("r").read()
    if not contents.startswith(WARNING_TEXT):
        print(f"{filename} does not start with our usual warning. Not proceding.")
        sys.exit(1)

    m = DATE_RE.search(contents)
    if m is None:
        print(f"Didn't find a date string in {filename}")
        sys.exit(1)
    found_date_str = m.group(1)
    found_date = time.strptime(found_date_str, "%Y-%m-%d")
    released = calendar.timegm(found_date)
    now = calendar.timegm(date)
    if abs(now - released) > tolerance * SEC_PER_DAY:
        print(f"Release date of {found_date_str} is too far in the past or future!")
        print("Run maint/update-release-date to correct this.")
        sys.exit(1)
    else:
        print(f"Release date of {found_date_str} is okay.")


def update_date(filename: Path, date: time.struct_time):
    contents = filename.open("r").read()
    if not contents.startswith(WARNING_TEXT):
        print(f"{filename} does not start with our usual warning. Not proceding.")
        sys.exit(1)

    date_text = time.strftime("%Y-%m-%d", date)

    with filename.open("w") as f:
        f.write(
            f"""\
{WARNING_TEXT}
//! Release date for arti-client.
//!
//! This module is automatically generated; do not edit by hand.

/// The approximate release date for this version of arti-client.
///
/// We use this release date in [`protostatus`](crate::protostatus)
/// to decide whether a consensus is new enough
/// that we should obey its recommendations.
///
/// (We do not warn or shut down based on recommendations
/// from consensuses older than our software:
/// the fact that we released _this_ version
/// indicates that a newer consensus probably permits this version.)
pub(crate) const ARTI_CLIENT_RELEASE_DATE: &str = "{date_text}";
"""
        )


def run():
    parser = argparse.ArgumentParser()
    parser.add_argument("--check", action="store_true")
    parser.add_argument(
        "--tolerance", default=2, help="Tolerance in days (used with --check)"
    )
    parser.add_argument(
        "--date", help="The target release date, if not today. YYYY-MM-DD form"
    )
    parser.add_argument("--filename", help="The rust module to check or update.")

    args = parser.parse_args()

    filename = (args.filename,)
    check_only = args.check
    tolerance = int(args.tolerance)
    if args.date is None:
        date = time.gmtime()
    else:
        date = parse_date(args.date)
    if args.filename is None:
        filename = (
            Path(os.path.dirname(sys.argv[0])).parent.parent
            / "crates/arti-client/src/release_date.rs"
        )
    else:
        filename = Path(args.filename)

    if not filename.is_file():
        print(f"{filename} does not exist; not proceding")
        sys.exit(1)

    if check_only:
        check_date(filename, date, tolerance)
    else:
        update_date(filename, date)


if __name__ == "__main__":
    run()
