#!/usr/bin/env python
# Copyright 2021-2022 python-tuf contributors
# SPDX-License-Identifier: MIT OR Apache-2.0

"""Simple repository example application

The application stores metadata and targets in memory, and serves them via http.
Nothing is persisted on disk or loaded from disk. The application simulates a
live repository by adding new target files periodically.
"""

import argparse
import logging
import sys
from datetime import datetime
from http.server import BaseHTTPRequestHandler, HTTPServer
from time import time
from typing import Dict, List

from _simplerepo import SimpleRepository

logger = logging.getLogger(__name__)


class ReqHandler(BaseHTTPRequestHandler):
    """HTTP handler to serve metadata and targets from a SimpleRepository"""

    def do_GET(self):
        if self.path.startswith("/metadata/") and self.path.endswith(".json"):
            self.get_metadata(self.path[len("/metadata/") : -len(".json")])
        elif self.path.startswith("/targets/"):
            self.get_target(self.path[len("/targets/") :])
        else:
            self.send_error(404, "Only serving /metadata/*.json")

    def get_metadata(self, ver_and_role: str):
        repo = self.server.repo

        ver_str, sep, role = ver_and_role.rpartition(".")
        if sep == "":
            # 0 will lead to list lookup with -1, meaning latest version
            ver = 0
        else:
            ver = int(ver_str)

        if role not in repo.role_cache or ver > len(repo.role_cache[role]):
            self.send_error(404, f"Role {role} version {ver} not found")
            return

        # send the metadata json
        data = repo.role_cache[role][ver - 1].to_bytes()
        self.send_response(200)
        self.send_header("Content-length", len(data))
        self.end_headers()
        self.wfile.write(data)

    def get_target(self, targetpath: str):
        repo: SimpleRepository = self.server.repo
        _hash, _, target = targetpath.partition(".")

        if target not in repo.target_cache:
            self.send_error(404, f"target {targetpath} not found")
            return

        # TODO: check that hash actually matches -- or use hash.targetpath as target_cache keys?

        # send the target content
        data = repo.target_cache[target]
        self.send_response(200)
        self.send_header("Content-length", len(data))
        self.end_headers()
        self.wfile.write(data)


class RepositoryServer(HTTPServer):
    def __init__(self, port: int):
        super().__init__(("127.0.0.1", port), ReqHandler)
        self.timeout = 1
        self.repo = SimpleRepository()


def main(argv: List[str]) -> None:
    """Example repository server"""

    parser = argparse.ArgumentParser()
    parser.add_argument("-v", "--verbose", action="count")
    parser.add_argument("-p", "--port", type=int, default=8001)
    args, _ = parser.parse_known_args(argv)

    level = logging.DEBUG if args.verbose else logging.INFO
    logging.basicConfig(level=level)

    server = RepositoryServer(args.port)
    last_change = 0
    counter = 0

    logger.info(
        f"Now serving. Root v1 at http://127.0.0.1:{server.server_port}/metadata/1.root.json"
    )

    while True:
        # Simulate a live repository: Add a new target file every few seconds
        if time() - last_change > 10:
            last_change = int(time())
            counter += 1
            content = str(datetime.fromtimestamp(last_change))
            server.repo.add_target(f"file{str(counter)}.txt", content)

        server.handle_request()


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