#!/usr/bin/env python3

import asyncio
import dummy_killer
import tornado.ioloop
import tornado.web
import tornado.httpserver
import ssl
import argparse
import os
import sys
import traceback

class MainHandler(tornado.web.RequestHandler):
    @tornado.gen.coroutine
    def get(self, path):
        if path == '/empty':
            # Return an empty reply
            self.set_header("Content-Type", "text/plain")
            self.write("")
        elif path == '/error_403':
            # Return a 403 HTTP error
            raise tornado.web.HTTPError(403)
        elif path == '/timeout':
            # Wait for 4 seconds before returning an empty reply
            yield tornado.gen.sleep(4)
            self.set_header("Content-Type", "text/plain")
            self.write("")
        elif path == '/request':
            # Return a string 'hello world'
            self.set_header("Content-Type", "text/plain")
            self.write("hello world")
        elif path == '/map-simple':
            # Return a string 'hello map'
            self.set_header("Content-Type", "text/plain")
            self.write("hello map")
        elif path == '/map-query':
            # Parse the 'key' argument from the HTTP request
            key = self.get_query_argument("key", default=None)
            if key == 'au':
                # Return a string 'hit' if 'key' is equal to 'au'
                self.set_header("Content-Type", "text/plain")
                self.write("1.0")
            else:
                # Return a 404 HTTP error if 'key' is not equal to 'au'
                raise tornado.web.HTTPError(404)
        elif path == '/settings':
            self.set_header("Content-Type", "application/json")
            self.write("{\"actions\": { \"reject\": 1.0}, \"symbols\": { \"EXTERNAL_SETTINGS\": 1.0 }}")
        else:
            raise tornado.web.HTTPError(404)

    @tornado.gen.coroutine
    def post(self, path):
        if path == '/empty':
            # Return an empty reply
            self.set_header("Content-Type", "text/plain")
            self.write("")
        elif path == '/error_403':
            # Return a 403 HTTP error
            raise tornado.web.HTTPError(403)
        elif path == '/request':
            # Return a string 'hello post'
            self.set_header("Content-Type", "text/plain")
            self.write("hello post")
        elif path == '/timeout':
            # Wait for 4 seconds before returning an empty reply
            yield tornado.gen.sleep(4)
            self.set_header("Content-Type", "text/plain")
            self.write("")
        elif path == '/map-simple':
            # Return a string 'hello map'
            self.set_header("Content-Type", "text/plain")
            self.write("hello map")
        elif path == '/map-query':
            # Parse the 'key' argument from the HTTP request
            key = self.get_query_argument("key", default="")
            if key == 'au':
                # Return a string 'hit' if 'key' is equal to 'au'
                self.set_header("Content-Type", "text/plain")
                self.write("hit")
            else:
                # Return a 404 HTTP error if 'key' is not equal to 'au'
                raise tornado.web.HTTPError(404)
        elif path == '/settings':
            self.set_header("Content-Type", "application/json")
            self.write("{\"actions\": { \"reject\": 1.0}, \"symbols\": { \"EXTERNAL_SETTINGS\": 1.0 }}")
        else:
            raise tornado.web.HTTPError(404)

    def head(self, path):
        # Log the request headers in the exact order they arrived on the
        # wire so functional tests can assert on header presence and order
        # (used by the url_redirector stealth fingerprint test).
        hdrs = ", ".join(f"{k}={v}" for k, v in self.request.headers.get_all())
        print(f"dummy_http.py: HEAD {path} headers: {hdrs}", file=sys.stderr)
        self.set_header("Content-Type", "text/plain")
        if path == "/redirect1":
            # Send an HTTP redirect to the bind address of the server
            self.redirect(f"{self.request.protocol}://{self.request.host}/hello")
        elif path == "/redirect2":
            # Send an HTTP redirect to the bind address of the server
            self.redirect(f"{self.request.protocol}://{self.request.host}/redirect1")
        elif path == "/redirect3":
            # Send an HTTP redirect to the bind address of the server
            self.redirect(f"{self.request.protocol}://{self.request.host}/redirect4")
        elif path == "/redirect4":
            # Send an HTTP redirect to the bind address of the server
            self.redirect(f"{self.request.protocol}://{self.request.host}/redirect3")
        elif path == "/tel_redirect":
            # Send an HTTP redirect to a tel: URL (non-HTTP scheme)
            self.redirect("tel:88006007775")
        elif path == "/mailto_redirect":
            # Send an HTTP redirect to a mailto: URL (non-HTTP scheme)
            self.redirect("mailto:user@example.net")
        elif path == "/redir_to_example":
            # Redirect to a domain matching the displayed text, so the phishing
            # module sees a 1:1 redirect target and must not fire (no FP).
            self.redirect("http://www.example.com/")
        elif path == "/chain_intermediate_1":
            # Intermediate hop to chain_to_tel2
            self.redirect(f"{self.request.protocol}://{self.request.host}/chain_intermediate_2")
        elif path == "/chain_intermediate_2":
            # Final hop to a tel: URL
            self.redirect("tel:88006007776")
        elif path == "/chain1":
            # Intermediate hop to chain2
            self.redirect(f"{self.request.protocol}://{self.request.host}/chain2")
        elif path == "/chain2":
            # Intermediate hop to chain3
            self.redirect(f"{self.request.protocol}://{self.request.host}/chain3")
        elif path == "/chain3":
            # Final hop
            self.redirect(f"{self.request.protocol}://{self.request.host}/hello")
        elif path == "/slow":
            # Slow redirect
            self.redirect(f"{self.request.protocol}://{self.request.host}/hello")
        elif path == "/combo_entry":
            # Redirect to a PATH-LESS wrapper that carries a percent-encoded
            # target (with its own &-separated params) plus an extra distinct
            # URL. Resolving the full target needs: keeping the query on a
            # path-less request (http path-less fix) and sending it
            # percent-encoded (redirector raw-request fix); and the extra URL
            # must NOT surface, because the followed wrapper's query is not
            # re-extracted (REDIRECTED-guard fix).
            target = "http%3A%2F%2Fdest.com%2F%3Fa%3D1%26b%3D2"
            other = "http%3A%2F%2Fother.com%2F"
            self.redirect(f"{self.request.protocol}://{self.request.host}?u={target}&other={other}")
        elif path == "/" and self.get_query_argument("u", default=None):
            # Path-less wrapper: redirect to the URL carried (percent-encoded)
            # in the u query parameter.
            self.redirect(self.get_query_argument("u"))
        else:
            self.send_response(200)
        self.set_header("Content-Type", "text/plain")

def make_app():
    return tornado.web.Application([
        (r"(/.*)", MainHandler),
    ])

async def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--bind", "-b", default="127.0.0.1", help="bind address")
    parser.add_argument("--port", "-p", type=int, default=18080, help="bind port")
    parser.add_argument("--keyfile", "-k", help="server private key file")
    parser.add_argument("--certfile", "-c", help="server certificate file")
    parser.add_argument("--pidfile", "-pf", help="path to the PID file")
    args = parser.parse_args()

    print(f"dummy_http.py: Starting server on {args.bind}:{args.port}", file=sys.stderr)

    # Create the Tornado application
    app = make_app()

    # If keyfile and certfile are provided, create an HTTPS server.
    # Otherwise, create an HTTP server.
    if args.keyfile and args.certfile:
        print(f"dummy_http.py: Using SSL with cert={args.certfile}, key={args.keyfile}", file=sys.stderr)
        ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
        ssl_ctx.load_cert_chain(args.certfile, args.keyfile)
        server = tornado.httpserver.HTTPServer(app, ssl_options=ssl_ctx)
    else:
        server = tornado.httpserver.HTTPServer(app)

    # Start the server
    server.bind(args.port, args.bind)
    server.start(1)

    # Write the PID to the specified PID file, if provided
    # Do this AFTER successful bind/start so we know the server is actually running
    if args.pidfile:
        dummy_killer.write_pid(args.pidfile)
        print(f"dummy_http.py: PID file written to {args.pidfile}", file=sys.stderr)

    print(f"dummy_http.py: Server started successfully, listening on {args.bind}:{args.port}", file=sys.stderr)

    await asyncio.Event().wait()

if __name__ == "__main__":
    try:
        if hasattr(asyncio, 'run') and callable(getattr(asyncio, 'run')):
            asyncio.run(main())
        else:
            loop = asyncio.get_event_loop()
            loop.run_until_complete(main())
    except Exception as e:
        print(f"dummy_http.py: FATAL ERROR: {type(e).__name__}: {e}", file=sys.stderr)
        traceback.print_exc(file=sys.stderr)
        sys.exit(1)
