#!/usr/bin/env python3

import sys
import os
import json
from functools import reduce
import requests
import time
import dateutil.parser
from datetime import timedelta, datetime, timezone
import argparse

HERE = os.path.dirname(__file__)
READIES = os.path.abspath(os.path.join(HERE, ".."))
sys.path.insert(0, READIES)
import paella  # noqa: F401

#----------------------------------------------------------------------------------------------

def dockerhub_query(query, token, key):
    results = {}
    url = f"https://hub.docker.com/v2/{query}/?page_size=100"
    count = 0
    page = 0
    while url is not None:
        page += 1
        if page > MAX_PAGES:
            break
        t0 = time.monotonic()
        if DEBUG:
            print(f"# {url}")
        if token is not None:
            res = requests.get(url, headers={'Authorization': f'JWT {token}'})
        else:
            res = requests.get(url)
        if res.status_code > 204 or not res.ok:
            raise RuntimeError(res)
        jres = json.loads(res.content)
        if count == 0:
            try:
                count = jres['count']
                if count > 100:
                    if DEBUG:
                        print(f"# {url} = {count}")
            except:
                pass
        x = reduce(lambda d, x: d.update({x[key]: x}) or d, jres['results'], {})
        results.update(x)
        t = time.monotonic()
        if DEBUG:
            print(f"# {url} -> {t - t0}")
        url = jres['next']
    return results

#----------------------------------------------------------------------------------------------

def scan():
    token = None
    if USER != "":
        try:
            d = f'{{"username": "{USER}", "password": "{PASSWD}"}}'
            res = requests.post("https://hub.docker.com/v2/users/login/", headers={'Content-Type': 'application/json'}, data=d)
            jres = json.loads(res.content)
            token = jres['token']
        except:
            pass

    now = datetime.now(timezone.utc)
    max_dt = timedelta(days=MAX_DAYS)

    try:
        images = dockerhub_query(f"repositories/{ORG}", token, 'name')
    except Exception as x:
        print(f"### error: {x}")
        exit(1)

    for img, image in images.items():
        try:
            tags = dockerhub_query(f"repositories/{ORG}/{img}/tags", token, 'name')
        except:
            print(f"### error in {img}")
            continue

        for tag_name, tag in tags.items():
            t = tag['last_updated']
            tt = dateutil.parser.parse(t)
            if now - tt > max_dt:
                print(f"# {img}/{tag_name} is too old ({now - tt}), skipping")
                break
            who = tag['last_updater_username']
            print(f"{img}/{tag_name}: {t} {who}")

#----------------------------------------------------------------------------------------------

parser = argparse.ArgumentParser(description='List Dockerhub images/tags')
# parser.add_argument('-u', '--user', type=str, default=os.getenv('USER', ''), help="Dockerhub username (also env var USER)")
# parser.add_argument('-p', '--password', type=str, default=os.getenv('PASSWD', ''), help="Dockerhub password (also env var PASSWD)")
parser.add_argument('-u', '--user', type=str, default='', help="Dockerhub username (also env var USER)")
parser.add_argument('-p', '--password', type=str, default='', help="Dockerhub password (also env var PASSWD)")
parser.add_argument('--debug', action="store_true", default=False, help='Print debug info')
parser.add_argument('-o', '--org', type=str, help="Dockerhub organization to scan")
parser.add_argument('--pages', type=int, default=10, help='Maximum info pages to scan')
parser.add_argument('--days', type=int, default=180, help='Maximum info pages to scan')
args = parser.parse_args()

DEBUG = args.debug
USER = args.user
PASSWD = args.password
ORG = args.org
MAX_PAGES = args.pages
MAX_DAYS = args.days
scan()
