#!/usr/bin/env python3
from pathlib import Path
from datetime import datetime

p = Path('/srv/apps/sotlab-users/sotlab_users_app.py')
s = p.read_text()
bak = p.with_name(p.name + '.bak.music-gate.' + datetime.now().strftime('%Y%m%d%H%M%S'))
bak.write_text(s)

if 'import http.client' not in s:
    s = s.replace('import hmac\n', 'import hmac\nimport http.client\n')

start = s.index('HOST_SERVICE = {')
end = s.index('\n}\n\nSERVICES = [', start) + len('\n}\n')
host_block = """HOST_SERVICE = {
    'sotlab.co.uk': ('main_portal', 'Main portal', '/srv/www/webpi'),
    'www.sotlab.co.uk': ('main_portal', 'Main portal', '/srv/www/webpi'),
    'webpi.sotlab.co.uk': ('webpi', 'webpi', '/srv/www/webpi'),
    'status.sotlab.co.uk': ('status', 'Status', '/srv/www/status'),
    'music.sotlab.co.uk': ('music_navidrome', 'Sot Lab Music', 'http://127.0.0.1:9001'),
}
"""
s = s[:start] + host_block + s[end:]
s = s.replace("('music_navidrome', 'Music / Navidrome'),", "('music_navidrome', 'Music'),")

proxy_func = r'''

HOP_BY_HOP_HEADERS = {
    'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization',
    'te', 'trailers', 'transfer-encoding', 'upgrade'
}


def rewrite_upstream_header(name, value, public_host):
    lname = name.lower()
    if lname == 'location':
        value = value.replace('http://127.0.0.1:9001', f'https://{public_host}')
        value = value.replace('http://localhost:9001', f'https://{public_host}')
    if lname == 'set-cookie':
        value = re.sub(r';\s*domain=(127\.0\.0\.1|localhost)', '', value, flags=re.I)
    return value
'''
if 'def rewrite_upstream_header' not in s:
    marker = '\n\nclass Handler(BaseHTTPRequestHandler):\n'
    s = s.replace(marker, proxy_func + marker)

old_clear = """    def clear_session(self):
        self.send_response(303)
        self.send_header('Location', '/')
        self.send_header('Set-Cookie', f'{SESSION_COOKIE}=; Path=/; Max-Age=0; HttpOnly; Secure; SameSite=Lax')
        self.end_headers()

    def maybe_serve_protected(self):
"""
new_clear = """    def clear_session(self):
        self.send_response(303)
        self.send_header('Location', '/')
        self.send_header('Set-Cookie', f'{SESSION_COOKIE}=; Path=/; Max-Age=0; HttpOnly; Secure; SameSite=Lax')
        self.end_headers()

    def proxy_to_local(self, upstream):
        parsed = urlparse(upstream)
        target_path = self.path or '/'
        body = None
        if self.command in ('POST', 'PUT', 'PATCH'):
            n = int(self.headers.get('Content-Length', '0') or '0')
            if n > 25 * 1024 * 1024:
                self.send_html(page('Too large', '<section class="panel"><h1>Request too large</h1></section>'), 413)
                return True
            body = self.rfile.read(n)
        headers = {}
        public_host = self.host()
        for k, v in self.headers.items():
            if k.lower() in HOP_BY_HOP_HEADERS or k.lower() == 'content-length':
                continue
            headers[k] = v
        headers['Host'] = public_host
        headers['X-Forwarded-Host'] = public_host
        headers['X-Forwarded-Proto'] = 'https'
        headers['X-Forwarded-For'] = self.client_address[0]
        if body is not None:
            headers['Content-Length'] = str(len(body))
        conn = http.client.HTTPConnection(parsed.hostname, parsed.port or 80, timeout=30)
        try:
            conn.request(self.command, target_path, body=body, headers=headers)
            resp = conn.getresponse()
            data = resp.read()
            self.send_response(resp.status, resp.reason)
            for k, v in resp.getheaders():
                if k.lower() in HOP_BY_HOP_HEADERS or k.lower() == 'content-length':
                    continue
                self.send_header(k, rewrite_upstream_header(k, v, public_host))
            self.send_header('Content-Length', str(len(data)))
            self.end_headers()
            self.wfile.write(data)
        finally:
            conn.close()
        return True

    def maybe_serve_protected(self):
"""
if old_clear not in s:
    raise SystemExit('clear_session block not found')
s = s.replace(old_clear, new_clear)

old_static = """        full, ctype = safe_static(root, path)
        if not full:
            self.send_html(page('Not found', '<section class="panel"><h1>Not found</h1></section>'), 404); return True
        data = full.read_bytes()
        self.send_response(200)
        self.send_header('Content-Type', ctype)
        self.send_header('Content-Length', str(len(data)))
        self.end_headers()
        self.wfile.write(data)
        return True
"""
new_static = """        if isinstance(root, str) and root.startswith('http://'):
            return self.proxy_to_local(root)
        full, ctype = safe_static(root, path)
        if not full:
            self.send_html(page('Not found', '<section class="panel"><h1>Not found</h1></section>'), 404); return True
        data = full.read_bytes()
        self.send_response(200)
        self.send_header('Content-Type', ctype)
        self.send_header('Content-Length', str(len(data)))
        self.end_headers()
        self.wfile.write(data)
        return True
"""
if old_static not in s:
    raise SystemExit('static serving block not found')
s = s.replace(old_static, new_static)

p.write_text(s)
print(f'backup={bak}')
