#! /usr/bin/python

import urllib.request
import urllib.parse
import re
import os
import glob as _glob
import shutil
import subprocess
import ssl
import sys
import argparse
import hashlib
import tempfile
import time

try:
    from tqdm import tqdm
    TQDM_AVAILABLE = True
except ImportError:
    TQDM_AVAILABLE = False

try:
    import certifi
    CERTIFI_AVAILABLE = True
except ImportError:
    CERTIFI_AVAILABLE = False

# ── Google Drive file IDs ─────────────────────────────────────────────────────
SNAPSHOT_FILE_ID  = "1clynf169SAfZTDbXLW3JQT1V3sNFUVi9"
SIGNATURE_FILE_ID = "1Hx4GWqTkTTBb2AOvGuI8hcgr8YbS6ULr"
README_FILE_ID    = "1FYOlLX4xR4PvYlafsGRRCHN0pkLyQ3u8"

SIGNING_KEY_FINGERPRINT = "332282A12DD6124BCBEBD27BC0D62C36BBFB97D1"
KEYSERVER               = "keyserver.ubuntu.com"
WIKI_URL = "https://github.com/Stealth-R-D-LLC/Stealth/wiki/Mainnet-Snapshot-Instructions"

# Blockchain data cleaned out before extraction
CLEANUP_GLOBS = ["blk*.dat", "bootstrap.dat*", "database", "txleveldb"]

# ── Terminal colours ──────────────────────────────────────────────────────────
BOLD   = "\033[1m"
RED    = "\033[31m"
GREEN  = "\033[32m"
YELLOW = "\033[33m"
BLUE   = "\033[94m"
CYAN   = "\033[96m"
RESET  = "\033[0m"


# =============================================================================
# SSL
# =============================================================================

def create_ssl_context():
    """
    Build an SSL context that works on macOS, Linux, and Windows.

    macOS's Python does not use the system keychain by default.  We try
    certifi first (most reliable), then the system store, then a glob search
    for certifi's cacert.pem left by the official Python installer.
    """
    if CERTIFI_AVAILABLE:
        return ssl.create_default_context(cafile=certifi.where())

    ctx = ssl.create_default_context()

    if sys.platform == "darwin" and not ctx.get_ca_certs():
        import glob
        for pattern in [
            "/Library/Frameworks/Python.framework/Versions/*/lib/python*/site-packages/certifi/cacert.pem",
            "/usr/local/lib/python*/site-packages/certifi/cacert.pem",
            "/opt/homebrew/lib/python*/site-packages/certifi/cacert.pem",
        ]:
            matches = glob.glob(pattern)
            if matches:
                try:
                    return ssl.create_default_context(cafile=matches[0])
                except Exception:
                    pass

    return ctx


# =============================================================================
# README parsing
# =============================================================================

def parse_readme(text):
    """
    Extract snapshot metadata from the README text.

    Returns a dict with keys: name, md5, sha256  (all may be None if missing).
    """
    name_match   = re.search(r'Name:\s*(\S+)',            text)
    md5_match    = re.search(r'MD5 Checksum\s*\n=+\s*\n([a-fA-F0-9]+)',    text)
    sha256_match = re.search(r'SHA256 Checksum\s*\n=+\s*\n([a-fA-F0-9]+)', text)
    return {
        "name":   name_match.group(1).strip()   if name_match   else None,
        "md5":    md5_match.group(1).strip()    if md5_match    else None,
        "sha256": sha256_match.group(1).strip() if sha256_match else None,
    }


# =============================================================================
# Checksum helpers
# =============================================================================

def file_checksum(path, algorithm="sha256", chunk=1 << 20):
    """Return the hex digest of a file using the given algorithm."""
    h = hashlib.new(algorithm)
    with open(path, "rb") as f:
        while True:
            buf = f.read(chunk)
            if not buf:
                break
            h.update(buf)
    return h.hexdigest()


def verify_checksum(path, expected_md5=None, expected_sha256=None):
    """
    Check one or both checksums.  Prints results.
    Returns True only if every supplied checksum matches.
    """
    ok = True
    if expected_sha256:
        print(f"  Verifying SHA-256 ...", end=" ", flush=True)
        actual = file_checksum(path, "sha256")
        if actual.lower() == expected_sha256.lower():
            print(f"{GREEN}OK{RESET}")
        else:
            print(f"{RED}MISMATCH{RESET}")
            print(f"    Expected : {expected_sha256}")
            print(f"    Got      : {actual}")
            ok = False
    if expected_md5:
        print(f"  Verifying MD5     ...", end=" ", flush=True)
        actual = file_checksum(path, "md5")
        if actual.lower() == expected_md5.lower():
            print(f"{GREEN}OK{RESET}")
        else:
            print(f"{RED}MISMATCH{RESET}")
            print(f"    Expected : {expected_md5}")
            print(f"    Got      : {actual}")
            ok = False
    return ok


# =============================================================================
# Google Drive download
# =============================================================================

def _filename_from_response(response):
    """Extract the real filename from a Content-Disposition header, if present."""
    cd = response.headers.get("Content-Disposition", "")
    m = re.search(r'filename\*?=["\']?(?:UTF-8\'\')?([^"\';\r\n]+)', cd, re.IGNORECASE)
    return urllib.parse.unquote(m.group(1).strip().strip('"\'')) if m else None


def _stream_to_disk(url, fallback_filename, ssl_context):
    """Stream a URL to disk. Returns (local_path, filename) or (None, None)."""
    try:
        with urllib.request.urlopen(url, context=ssl_context) as response:
            filename  = _filename_from_response(response) or fallback_filename
            file_size = int(response.headers.get("Content-Length", 0))
            if file_size > 500_000_000:
                print(f"  File size : {file_size / (1024**3):.2f} GB")
            elif file_size:
                print(f"  File size : {file_size / 1024:.1f} KB")

            block_size = 65536
            if TQDM_AVAILABLE:
                pbar = tqdm(total=file_size or None,
                            unit="iB", unit_scale=True, unit_divisor=1024,
                            desc=filename)
            else:
                pbar = None

            downloaded = 0
            with open(filename, "wb") as out:
                while True:
                    buf = response.read(block_size)
                    if not buf:
                        break
                    out.write(buf)
                    downloaded += len(buf)
                    if pbar:
                        pbar.update(len(buf))
                    else:
                        if file_size:
                            pct = min(downloaded / file_size * 100, 100)
                            sys.stdout.write(f"\r  Progress  : {pct:6.2f}%")
                        else:
                            sys.stdout.write(
                                f"\r  Downloaded: {downloaded / (1 << 20):.1f} MB"
                            )
                        sys.stdout.flush()

            if pbar:
                pbar.close()
            else:
                print()

        print(f"  {GREEN}Complete \u2192 {CYAN}{filename}{RESET}")
        return filename, filename

    except urllib.error.URLError as e:
        print(f"\n{RED}  Download error: {e}{RESET}")
        return None, None


def download_google_drive_file(file_id, fallback_filename, ssl_context, label=None):
    """
    Download a file from Google Drive, transparently handling the large-file
    confirmation form.  Returns (local_path, filename) or (None, None).
    """
    display = label or fallback_filename
    print(f"Fetching {display} ...")
    url = f"https://drive.google.com/uc?export=download&id={file_id}"
    req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})

    try:
        with urllib.request.urlopen(req, context=ssl_context) as response:
            content_type = response.headers.get("Content-Type", "")

            # Small file — served directly without a confirmation page
            if "text/html" not in content_type:
                filename = _filename_from_response(response) or fallback_filename
                data = response.read()
                with open(filename, "wb") as f:
                    f.write(data)
                print(f"  {GREEN}Complete \u2192 {CYAN}{filename}{RESET}")
                return filename, filename

            # Large file — parse the virus-scan confirmation form
            html = response.read().decode("utf-8")

    except urllib.error.URLError as e:
        print(f"{RED}  Error contacting Google Drive: {e}{RESET}")
        if sys.platform == "darwin" and not CERTIFI_AVAILABLE:
            print(f"  Tip: run  {BOLD}pip3 install certifi{RESET}  to fix SSL issues on macOS.")
        return None, None

    form_action = re.search(r'<form id="download-form" action="([^"]*)"', html)
    confirm     = re.search(r'name="confirm" value="([^"]*)"', html)
    uuid_val    = re.search(r'name="uuid" value="([^"]*)"', html)

    if not all([form_action, confirm, uuid_val]):
        print(f"{RED}  Could not parse Google Drive confirmation form.{RESET}")
        return None, None

    download_url = (
        f"{form_action.group(1)}?"
        f"id={file_id}&export=download"
        f"&confirm={confirm.group(1)}"
        f"&uuid={uuid_val.group(1)}"
    )
    return _stream_to_disk(download_url, fallback_filename, ssl_context)


# =============================================================================
# GPG
# =============================================================================

def gpg_available():
    try:
        subprocess.run(["gpg", "--version"],
                       check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        return True
    except (subprocess.CalledProcessError, FileNotFoundError):
        return False


def import_signing_key(fingerprint, keyserver):
    print(f"  Fetching signing key {fingerprint}")
    print(f"  from {keyserver} ...")
    r = subprocess.run(
        ["gpg", "--keyserver", keyserver, "--recv-keys", fingerprint],
        capture_output=True, text=True
    )
    if r.returncode != 0:
        print(f"{RED}  Key import failed:{RESET}\n{r.stderr}")
        return False
    print(f"  {GREEN}Signing key imported.{RESET}")
    return True


def verify_signature(snapshot_file, sig_file):
    print(f"  Verifying {CYAN}{snapshot_file}{RESET} ...")
    r = subprocess.run(
        ["gpg", "--verify", sig_file, snapshot_file],
        capture_output=True, text=True
    )
    output = (r.stderr or r.stdout).strip()
    if r.returncode == 0:
        print(f"  {GREEN}Signature PASSED.{RESET}")
    else:
        print(f"  {RED}Signature FAILED.{RESET}")
    if output:
        for line in output.splitlines():
            print(f"    {line}")
    return r.returncode == 0


# =============================================================================
# Data-directory helpers
# =============================================================================

def clean_data_dir(data_dir, debug_log_action):
    """
    Delete blockchain data files from data_dir.
    debug_log_action: "delete" | "backup" | "keep"
    Returns backup_path if a backup was made, else None.
    """
    backup_path = None

    debug_log = os.path.join(data_dir, "debug.log")
    if os.path.exists(debug_log):
        if debug_log_action == "delete":
            (shutil.rmtree if os.path.isdir(debug_log) else os.remove)(debug_log)
        elif debug_log_action == "backup":
            ts = time.time_ns() // 1000
            backup_path = f"{debug_log}.{ts:x}"
            shutil.copy2(debug_log, backup_path)
            os.remove(debug_log)
        # "keep" -> nothing

    for pattern in CLEANUP_GLOBS:
        for path in _glob.glob(os.path.join(data_dir, pattern)):
            (shutil.rmtree if os.path.isdir(path) else os.remove)(path)

    return backup_path


def extract_snapshot(archive_path, data_dir):
    """
    Extract archive_path, placing the blockchain data files directly into
    data_dir (not into a subdirectory).

    7z normally unpacks a file like Stealth-Snapshot-Data-x86-33308800.7z
    into a single folder called Stealth-Snapshot-Data-x86-33308800/.  This
    function detects that wrapper folder and moves its contents up one level
    into data_dir, then removes the now-empty folder.

    Returns True on success.
    """
    staging = tempfile.mkdtemp(dir=data_dir, prefix=".snapshot-extract-")
    try:
        print(f"  Extracting {CYAN}{archive_path}{RESET} ...")
        result = subprocess.run(
            ["7z", "x", archive_path, f"-o{staging}"],
            check=False
        )
        if result.returncode != 0:
            print(f"{RED}  Extraction failed (7z exit {result.returncode}).{RESET}")
            return False

        top_entries = os.listdir(staging)

        # Case A: single wrapper directory (typical snapshot layout)
        if len(top_entries) == 1 and os.path.isdir(os.path.join(staging, top_entries[0])):
            inner = os.path.join(staging, top_entries[0])
            print(f"  Moving contents of {CYAN}{top_entries[0]}/{RESET}"
                  f" into {CYAN}{data_dir}{RESET} ...")
            for item in os.listdir(inner):
                src  = os.path.join(inner, item)
                dest = os.path.join(data_dir, item)
                if os.path.exists(dest):
                    (shutil.rmtree if os.path.isdir(dest) else os.remove)(dest)
                shutil.move(src, dest)
            shutil.rmtree(inner)

        # Case B: files/dirs unpacked directly (no wrapper)
        else:
            print(f"  Moving extracted files into {CYAN}{data_dir}{RESET} ...")
            for item in top_entries:
                src  = os.path.join(staging, item)
                dest = os.path.join(data_dir, item)
                if os.path.exists(dest):
                    (shutil.rmtree if os.path.isdir(dest) else os.remove)(dest)
                shutil.move(src, dest)

        print(f"  {GREEN}Extraction complete.{RESET}")
        return True

    except FileNotFoundError:
        print(f"{RED}  '7z' not found.  Install p7zip and try again.{RESET}")
        return False
    finally:
        if os.path.isdir(staging):
            shutil.rmtree(staging, ignore_errors=True)


# =============================================================================
# UI helpers
# =============================================================================

def check_writable(path):
    try:
        tf = tempfile.TemporaryFile(dir=path)
        tf.close()
        return True
    except OSError:
        return False


def yn(question):
    return input(f"{BOLD}{question} (y/n): {RESET}").strip().lower() == "y"


def divider(char=u"\u2500", width=56):
    print(char * width)


def step_header(n, total, title):
    print()
    divider()
    print(f"{BOLD}Step {n}/{total}: {title}{RESET}")
    divider()


# =============================================================================
# Main
# =============================================================================

def main():
    parser = argparse.ArgumentParser(
        description="Download the Stealth mainnet snapshot from Google Drive."
    )
    parser.add_argument(
        "--datadir", "-d", type=str,
        help="Path to the Stealth data directory (default: ~/.StealthCoin)"
    )
    args = parser.parse_args()

    if not check_writable("."):
        print(f"{RED}Error: current directory is not writable.{RESET}")
        sys.exit(1)

    # ══════════════════════════════════════════════════════════════════════════
    # PHASE 1 — All user interaction up front
    # ══════════════════════════════════════════════════════════════════════════

    print()
    print(f"{BOLD}Stealth Mainnet Snapshot Downloader{RESET}")
    divider(u"\u2550")
    print()
    print("The README and signature are always re-downloaded (they are small).")
    print("The snapshot archive (>10 GB) is skipped if a local copy already")
    print("passes the checksums recorded in the README.")
    print()
    print("Google Drive cannot scan files of this size for viruses.")
    print()

    if not yn("Proceed?"):
        print(f"{RED}Cancelled.{RESET}")
        sys.exit(0)

    # Signature verification
    gpg_ok = gpg_available()
    do_verify = False
    print()
    if gpg_ok:
        do_verify = yn("Verify the snapshot signature after downloading?")
    else:
        print(f"{BOLD}Note:{RESET} gpg not found — signature verification will be skipped.")

    # Data-directory setup
    print()
    do_setup = yn("Set up the Stealth data directory using this snapshot?")

    data_dir         = None
    debug_log_action = None

    if do_setup:
        default_dir = (os.path.abspath(args.datadir) if args.datadir
                       else os.path.expanduser("~/.StealthCoin"))
        print()
        print(f"Default data directory: {CYAN}{default_dir}{RESET}")
        custom = input(
            f"{BOLD}Press Enter to accept, or type an alternative path: {RESET}"
        ).strip()
        data_dir = os.path.abspath(custom) if custom else default_dir

        if not os.path.isdir(data_dir):
            print(f"{RED}Error: '{data_dir}' does not exist.{RESET}")
            sys.exit(1)
        if not check_writable(data_dir):
            print(f"{RED}Error: '{data_dir}' is not writable.{RESET}")
            sys.exit(1)

        debug_log = os.path.join(data_dir, "debug.log")
        if os.path.exists(debug_log):
            print()
            print(f"  {CYAN}debug.log{RESET} exists in {CYAN}{data_dir}{RESET}.")
            print("  [d]  Delete it")
            print("  [b]  Back it up in place, then remove the original")
            print("  [k]  Keep it (leave untouched)")
            while True:
                c = input(f"{BOLD}  Choice (d/b/k): {RESET}").strip().lower()
                if c in ("d", "b", "k"):
                    debug_log_action = {"d": "delete", "b": "backup", "k": "keep"}[c]
                    break
                print("  Please enter d, b, or k.")
        else:
            debug_log_action = "keep"

    # Confirm plan
    print()
    divider()
    print(f"{BOLD}Plan{RESET}")
    divider()
    print(f"  Always download      : README, signature")
    print(f"  Snapshot archive     : download or skip if checksum matches")
    print(f"  Verify signature     : " +
          (f"{GREEN}yes{RESET}" if do_verify else f"{BOLD}no{RESET}"))
    if do_setup:
        dl_label = (RED + "delete" + RESET   if debug_log_action == "delete" else
                    CYAN + "backup" + RESET  if debug_log_action == "backup" else
                    "keep")
        print(f"  Set up data dir      : {GREEN}yes{RESET}")
        print(f"    Path               : {CYAN}{data_dir}{RESET}")
        print(f"    debug.log          : {dl_label}")
    else:
        print(f"  Set up data dir      : {BOLD}no{RESET}")
    divider()
    print()

    if not yn("Begin?"):
        print(f"{RED}Cancelled.{RESET}")
        sys.exit(0)

    # ══════════════════════════════════════════════════════════════════════════
    # PHASE 2 — Work (no further prompts)
    # ══════════════════════════════════════════════════════════════════════════

    total_steps = 4 + (1 if do_setup else 0)
    summary = []   # list of (label, value, colour)

    ssl_context = create_ssl_context()
    print()
    if CERTIFI_AVAILABLE:
        print("SSL: using certifi certificate bundle.")
    else:
        print("SSL: using system certificate store.")
        if sys.platform == "darwin":
            print(f"     (If SSL errors occur, run: {BOLD}pip3 install certifi{RESET})")

    # ── Step 1: README (always fresh) ────────────────────────────────────────
    step_header(1, total_steps, "Downloading README")
    readme_local, readme_name = download_google_drive_file(
        README_FILE_ID, "SNAPSHOT-README.txt", ssl_context, label="README"
    )
    if not readme_local:
        print(f"{RED}README download failed.  Cannot determine checksums.  Aborting.{RESET}")
        sys.exit(1)

    with open(readme_local, "r", errors="replace") as f:
        readme_text = f.read()
    meta = parse_readme(readme_text)

    archive_name_in_readme = meta.get("name")
    md5_in_readme          = meta.get("md5")
    sha256_in_readme       = meta.get("sha256")

    print(f"  Archive name : {CYAN}{archive_name_in_readme or 'not found'}{RESET}")
    print(f"  SHA-256      : {sha256_in_readme or 'not found'}")
    print(f"  MD5          : {md5_in_readme    or 'not found'}")
    summary.append(("README", readme_name or readme_local, GREEN))

    # ── Step 2: Signature (always fresh) ─────────────────────────────────────
    step_header(2, total_steps, "Downloading signature")
    sig_local, sig_name = download_google_drive_file(
        SIGNATURE_FILE_ID, "xst-snapshot.7z.sig", ssl_context, label="signature"
    )
    summary.append(("Signature", sig_name or sig_local, GREEN)
                   if sig_local else ("Signature", "FAILED", RED))

    # ── Step 3: Snapshot archive (skip if checksum matches) ───────────────────
    step_header(3, total_steps, "Snapshot archive")

    candidate = archive_name_in_readme or "xst-snapshot.7z"
    snapshot_local   = None
    skipped_download = False

    if os.path.isfile(candidate) and (sha256_in_readme or md5_in_readme):
        print(f"  Local file found: {CYAN}{candidate}{RESET}")
        print(f"  Checking against README checksum(s) ...")
        if verify_checksum(candidate, md5_in_readme, sha256_in_readme):
            print(f"  {GREEN}Checksum match \u2014 skipping download.{RESET}")
            snapshot_local   = candidate
            skipped_download = True
            summary.append(("Snapshot", f"{candidate}  {YELLOW}(already present, checksum OK){RESET}", GREEN))
        else:
            print(f"  {YELLOW}Checksum mismatch \u2014 re-downloading.{RESET}")

    if not snapshot_local:
        snapshot_local, snapshot_name = download_google_drive_file(
            SNAPSHOT_FILE_ID, candidate, ssl_context, label="snapshot archive"
        )
        if not snapshot_local:
            print(f"{RED}Snapshot download failed.  Aborting.{RESET}")
            sys.exit(1)
        # Verify freshly downloaded file
        if sha256_in_readme or md5_in_readme:
            print(f"  Verifying downloaded archive ...")
            cksum_ok = verify_checksum(snapshot_local, md5_in_readme, sha256_in_readme)
            if not cksum_ok:
                # The archive may be newer than the README we fetched earlier.
                # Re-download the README (and signature) and try once more before
                # treating this as a genuine corruption.
                print()
                print(f"  {YELLOW}Checksum mismatch on the freshly downloaded archive.{RESET}")
                print(f"  The README may not yet reflect the newest upload.")
                print(f"  Re-fetching README and signature to check ...")
                print()

                new_readme_local, new_readme_name = download_google_drive_file(
                    README_FILE_ID, "SNAPSHOT-README.txt", ssl_context, label="README"
                )
                if new_readme_local:
                    with open(new_readme_local, "r", errors="replace") as f:
                        new_readme_text = f.read()
                    new_meta       = parse_readme(new_readme_text)
                    new_md5        = new_meta.get("md5")
                    new_sha256     = new_meta.get("sha256")
                    readme_local   = new_readme_local
                    readme_name    = new_readme_name
                    # Update the summary entry that was already appended for the README
                    summary[:] = [(l, v, c) for l, v, c in summary if l != "README"]
                    summary.insert(0, ("README", new_readme_name or new_readme_local, GREEN))

                    print(f"  Re-verifying archive against refreshed README ...")
                    cksum_ok = verify_checksum(snapshot_local, new_md5, new_sha256)

                new_sig_local, new_sig_name = download_google_drive_file(
                    SIGNATURE_FILE_ID, "xst-snapshot.7z.sig", ssl_context,
                    label="signature"
                )
                if new_sig_local:
                    sig_local = new_sig_local
                    sig_name  = new_sig_name
                    summary[:] = [(l, v, c) for l, v, c in summary if l != "Signature"]
                    summary.append(("Signature",
                                    new_sig_name or new_sig_local, GREEN))

            summary.append(("Snapshot checksum", "PASSED" if cksum_ok else "FAILED",
                             GREEN if cksum_ok else RED))
        summary.append(("Snapshot", snapshot_name or snapshot_local, GREEN))

    # ── Step 4: Signature verification ───────────────────────────────────────
    step_header(4, total_steps, "Signature verification")
    if do_verify and sig_local:
        key_ok = import_signing_key(SIGNING_KEY_FINGERPRINT, KEYSERVER)
        if key_ok:
            passed = verify_signature(snapshot_local, sig_local)
            summary.append(("Sig verification", "PASSED" if passed else "FAILED",
                             GREEN if passed else RED))
        else:
            summary.append(("Sig verification", "key import failed", RED))
    elif not do_verify:
        print("  Skipped (user choice).")
        summary.append(("Sig verification", "skipped", BOLD))
    else:
        print("  Skipped (signature not downloaded).")
        summary.append(("Sig verification", "skipped \u2014 sig unavailable", BOLD))

    # ── Step 5: Data-directory setup ──────────────────────────────────────────
    if do_setup:
        step_header(5, total_steps, f"Setting up {data_dir}")

        print("  Cleaning blockchain data ...")
        backup_path = clean_data_dir(data_dir, debug_log_action)
        print(f"  {GREEN}Old blockchain data removed.{RESET}")

        if debug_log_action == "delete":
            summary.append(("debug.log", "deleted", BOLD))
        elif debug_log_action == "backup" and backup_path:
            summary.append(("debug.log backup", backup_path, CYAN))
        else:
            summary.append(("debug.log", "kept", BOLD))

        extracted = extract_snapshot(snapshot_local, data_dir)
        summary.append((
            "Extracted to",
            data_dir if extracted else "FAILED",
            GREEN if extracted else RED
        ))

    # ══════════════════════════════════════════════════════════════════════════
    # PHASE 3 — Summary
    # ══════════════════════════════════════════════════════════════════════════

    print()
    print()
    divider(u"\u2550")
    print(f"{BOLD}Summary{RESET}")
    divider(u"\u2550")

    w = max(len(lbl) for lbl, _, _ in summary) + 2
    for label, value, colour in summary:
        print(f"  {BOLD}{label:<{w}}{RESET}{colour}{value}{RESET}")

    print()
    print(f"  {BOLD}{'README file':<{w}}{RESET}{CYAN}{readme_name or readme_local}{RESET}")
    if sig_local:
        print(f"  {BOLD}{'Signature file':<{w}}{RESET}{CYAN}{sig_name or sig_local}{RESET}")
    print()
    print(f"  For full instructions:")
    print(f"  {BLUE}{WIKI_URL}{RESET}")
    divider(u"\u2550")
    print()


if __name__ == "__main__":
    main()
