# from _socket import _RetAddress
from collections.abc import Callable
from socketserver import BaseRequestHandler
import time
from server import WebRequestHandler
from http.server import HTTPServer
import os, responses
from urllib.parse import unquote
from datatypes import headers, htmldoc, htmlelement, httpReturn
import importlib
from websockets import serve
import asyncio, selectors
import threading, hashlib, base64

if hasattr(selectors, 'PollSelector'):
    _ServerSelector = selectors.PollSelector
else:
    _ServerSelector = selectors.SelectSelector

def normalizePaths(path: str) -> str:
  return path.removesuffix("/").removeprefix("/")

class MyServer(HTTPServer):
  def __init__(self, server_address: tuple[str | bytes | bytearray, int], RequestHandlerClass, data, bind_and_activate: bool = True) -> None:
    self.data = data
    super().__init__(server_address, RequestHandlerClass, bind_and_activate)
    
  def finish_request(self, request, client_address) -> None:
    self.RequestHandlerClass(request, client_address, self, self.data)
    
  def serve_forever(self, poll_interval=0.5):
        """Handle one request at a time until shutdown.

        Polls for shutdown every poll_interval seconds. Ignores
        self.timeout. If you need to do periodic tasks, do them in
        another thread.
        """
        self._BaseServer__is_shut_down.clear()
        try:
            # XXX: Consider using another file descriptor or connecting to the
            # socket to wake this up instead of polling. Polling reduces our
            # responsiveness to a shutdown request and wastes cpu at all other
            # times.
            with _ServerSelector() as selector:
                selector.register(self, selectors.EVENT_READ)

                while not self._BaseServer__shutdown_request:
                    ready = selector.select(poll_interval)
                    # bpo-35017: shutdown() called during select(), exit immediately.
                    if self._BaseServer__shutdown_request:
                        break
                    if ready:
                        threading.Thread(target=self._handle_request_noblock).run()

                    self.service_actions()
        finally:
            self._BaseServer__shutdown_request = False
            self._BaseServer__is_shut_down.set()
            
def getWebsocketAcceptValue(key):
  GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
  return base64.b64encode(hashlib.sha1((key + GUID).encode()).digest()).decode()

class server:
  def __init__(self, paths: dict = {}, unauthorized_files: list = [], root="./", module = None, disallow_var_name = "disallow", path_var_name = "paths"):
    self.paths = paths
    self.unauthorized_files = unauthorized_files
    self.root = root
    self.pathname = path_var_name
    self.disallow = disallow_var_name
    self.module = module
  def updatePathing(self):
    try:
      newmod = importlib.reload(self.module)
      self.paths = getattr(newmod, self.pathname)
      self.unauthorized_files = getattr(newmod, self.disallow)
    except:
      pass
  def runserver(self, port: int):
    server = MyServer(("0.0.0.0", port), WebRequestHandler, self)
    server.serve_forever()
  def respond(self, handler: WebRequestHandler) -> httpReturn:
    self.updatePathing()
    if handler.headers["Upgrade"] == "websocket":
      handshake = handler.headers["Sec-WebSocket-Key"]
      handshake = getWebsocketAcceptValue(handshake)
      return httpReturn("", [("Upgrade", "websocket"), ("Sec-WebSocket-Accept", handshake), ("Connection", "Upgrade")], 101, close=False)
    paths = self.paths
    path: list = normalizePaths(handler.url.path).split("/")
    for segment in path: 
      if segment in paths:
        paths = paths[segment]
        if isinstance(paths, dict):
          if segment == path[len(path)-1] and "root" in paths:
            paths = paths["root"]
    if callable(paths):
      return paths(handler)
    path = normalizePaths(unquote(handler.url.path))
    if os.path.exists(self.root+path) and not (".." in path):
      if not path in self.unauthorized_files:
        try:
          return responses.get_file(handler, self.root)
        except:
          return responses.lsdir(handler, self.root, self.unauthorized_files)
      else:
        return responses.error(handler, 403)
    else:
      if ".." in path:
        return responses.error(handler, 404, "lmao you thought you could scale the filesystem")
      return responses.error(handler, 404, f"no resource at {path}")
    
if __name__ == "__main__":
  # server({}).runserver(4356)
  print(getWebsocketAcceptValue("dGhlIHNhbXBsZSBub25jZQ=="))
  print(getWebsocketAcceptValue("08kp54j1E3z4IfuM1m75tQ=="))