"""Synthetic HTTP migration witness. No live website is requested or changed."""
import csv
import hashlib
from html.parser import HTMLParser
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import io
import json
from pathlib import Path
import sys
import threading
import urllib.error
import urllib.request

MAP = """old_path,decision,new_path,status,canonical
/services/site-care,keep,/services/site-care,200,/services/site-care
/services/old-audit,redirect,/services/technical-audit,301,/services/technical-audit
/offers/retired-package,remove,,410,
"""
ORIGIN = 'https://new.example'


def page(path):
    return '<link rel="canonical" href="' + ORIGIN + path + '"><h1>Fixture</h1>'


ROUTES = {
    '/services/site-care': (200, None, page('/services/site-care')),
    '/services/old-audit': (301, '/services/technical-audit', ''),
    '/services/technical-audit': (200, None, page('/services/technical-audit')),
    '/offers/retired-package': (410, None, 'gone'),
    '/intermediate': (301, '/services/technical-audit', ''),
    '/': (200, None, page('/')),
}


class Canonicals(HTMLParser):
    def __init__(self):
        super().__init__()
        self.values = []

    def handle_starttag(self, tag, attrs):
        attrs = dict(attrs)
        if tag == 'link' and 'canonical' in attrs.get('rel', '').lower().split():
            self.values.append(attrs.get('href'))


class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        status, location, body = ROUTES.get(self.path, (404, None, 'not found'))
        data = body.encode()
        self.send_response(status)
        self.send_header('Content-Type', 'text/html; charset=utf-8')
        self.send_header('Content-Length', str(len(data)))
        if location is not None:
            self.send_header('Location', location)
        self.end_headers()
        self.wfile.write(data)

    def log_message(self, *args):
        pass


class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, *args):
        return None


passed, trace = [], []


def check(condition, marker):
    if not condition:
        raise AssertionError(marker)
    passed.append(marker)


server = ThreadingHTTPServer(('127.0.0.1', 0), Handler)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
base = 'http://127.0.0.1:' + str(server.server_port)
opener = urllib.request.build_opener(NoRedirect())


def get(path):
    try:
        response = opener.open(base + path, timeout=3)
    except urllib.error.HTTPError as error:
        response = error
    with response:
        value = dict(path=path, status=response.status,
                     location=response.headers.get('Location'), body=response.read().decode())
        trace.append(value)
        return value


try:
    for row in csv.DictReader(io.StringIO(MAP)):
        label = row['decision']
        first = get(row['old_path'])
        check(first['status'] == int(row['status']), label + ':first-status')
        if label == 'redirect':
            check(first['location'] == row['new_path'], 'redirect:direct-location')
            final = get(row['new_path'])
            check(final['status'] == 200, 'redirect:final-status')
            check(final['location'] is None, 'redirect:final-no-location')
        else:
            check(first['location'] is None, label + ':no-location')
            final = first
        if row['canonical']:
            parser = Canonicals()
            parser.feed(final['body'])
            check(parser.values == [ORIGIN + row['canonical']], label + ':canonical')
    unknown = get('/unknown')
    check(unknown['status'] == 404, 'unknown:status')
    check(unknown['location'] is None, 'unknown:no-location')
    result = dict(status='pass', passed=passed, trace=trace)
except AssertionError as error:
    result = dict(status='fail', assertion=str(error), passed=passed, trace=trace)
finally:
    server.shutdown()
    server.server_close()
    thread.join(timeout=2)
result.update(python=sys.version.split()[0],
              sourceSha256=hashlib.sha256(Path(__file__).read_bytes()).hexdigest())
print(json.dumps(result, sort_keys=True))
raise SystemExit(0 if result['status'] == 'pass' else 1)
