"""
BIM Server module.
Provides HTTP server classes for BIM Viewer and ACC Viewer backends.
Can be used standalone or integrated into another web application.
"""

import json
from http.server import HTTPServer, SimpleHTTPRequestHandler
from urllib.parse import urlparse, parse_qs

from . import auth, viewer, upload, acc


class BIMViewerHandler(SimpleHTTPRequestHandler):
    """
    HTTP handler for BIM Viewer server (2-legged auth, port 9090).
    Serves static files + proxy API endpoints to APS.
    """

    def do_GET(self):
        parsed = urlparse(self.path)
        path = parsed.path
        params = parse_qs(parsed.query)

        routes = {
            "/api/token": self._handle_token,
            "/api/metadata": self._handle_metadata,
            "/api/properties": self._handle_properties,
            "/api/tree": self._handle_tree,
            "/api/thumbnail": self._handle_thumbnail,
            "/api/manifest": self._handle_manifest,
            "/api/phases": self._handle_phases,
        }

        handler = routes.get(path)
        if handler:
            handler(params)
        else:
            super().do_GET()

    def _send_json(self, data, status=200):
        self.send_response(status)
        self.send_header("Content-Type", "application/json")
        self.send_header("Access-Control-Allow-Origin", "*")
        self.end_headers()
        self.wfile.write(json.dumps(data, ensure_ascii=False).encode())

    def _send_error(self, msg, status=500):
        self._send_json({"error": msg}, status)

    def _handle_token(self, params):
        try:
            token = auth.get_2legged_token()
            self._send_json({"access_token": token, "expires_in": 3600})
        except Exception as e:
            self._send_error(str(e))

    def _handle_metadata(self, params):
        urn = params.get("urn", [None])[0]
        if not urn:
            return self._send_error("Missing urn parameter", 400)
        try:
            self._send_json(viewer.get_metadata(urn))
        except Exception as e:
            self._send_error(str(e))

    def _handle_properties(self, params):
        urn = params.get("urn", [None])[0]
        guid = params.get("guid", [None])[0]
        if not urn or not guid:
            return self._send_error("Missing urn or guid", 400)
        try:
            self._send_json(viewer.get_properties(urn, guid))
        except Exception as e:
            self._send_error(str(e))

    def _handle_tree(self, params):
        urn = params.get("urn", [None])[0]
        guid = params.get("guid", [None])[0]
        if not urn or not guid:
            return self._send_error("Missing urn or guid", 400)
        try:
            self._send_json(viewer.get_tree(urn, guid))
        except Exception as e:
            self._send_error(str(e))

    def _handle_thumbnail(self, params):
        urn = params.get("urn", [None])[0]
        if not urn:
            return self._send_error("Missing urn", 400)
        try:
            img = viewer.get_thumbnail(urn)
            self.send_response(200)
            self.send_header("Content-Type", "image/png")
            self.send_header("Access-Control-Allow-Origin", "*")
            self.end_headers()
            self.wfile.write(img)
        except Exception as e:
            self._send_error(str(e))

    def _handle_manifest(self, params):
        urn = params.get("urn", [None])[0]
        if not urn:
            return self._send_error("Missing urn", 400)
        try:
            self._send_json(viewer.get_manifest(urn))
        except Exception as e:
            self._send_error(str(e))

    def _handle_phases(self, params):
        urn = params.get("urn", [None])[0]
        guid = params.get("guid", [None])[0]
        if not urn or not guid:
            return self._send_error("Missing urn or guid", 400)
        try:
            self._send_json(viewer.get_phases(urn, guid))
        except Exception as e:
            self._send_error(str(e))


class ACCViewerHandler(SimpleHTTPRequestHandler):
    """
    HTTP handler for ACC Viewer server (3-legged auth, port 9091).
    Serves static files + OAuth2 + Data Management + Model Derivative endpoints.
    """

    def do_GET(self):
        parsed = urlparse(self.path)
        path = parsed.path
        params = parse_qs(parsed.query)

        routes = {
            "/auth/login": self._handle_login,
            "/auth/callback": self._handle_callback,
            "/auth/status": self._handle_auth_status,
            "/auth/logout": self._handle_logout,
            "/api/token": self._handle_token,
            "/api/hubs": self._handle_hubs,
            "/api/projects": self._handle_projects,
            "/api/topfolders": self._handle_top_folders,
            "/api/folder": self._handle_folder,
            "/api/versions": self._handle_versions,
            "/api/item": self._handle_item,
            "/api/metadata": self._handle_metadata,
            "/api/tree": self._handle_tree,
            "/api/properties": self._handle_properties,
            "/api/phases": self._handle_phases,
        }

        handler = routes.get(path)
        if handler:
            handler(params)
        else:
            super().do_GET()

    def _send_json(self, data, status=200):
        self.send_response(status)
        self.send_header("Content-Type", "application/json")
        self.send_header("Access-Control-Allow-Origin", "*")
        self.end_headers()
        self.wfile.write(json.dumps(data, ensure_ascii=False).encode())

    def _send_error(self, msg, status=500):
        self._send_json({"error": msg}, status)

    def _redirect(self, url):
        self.send_response(302)
        self.send_header("Location", url)
        self.end_headers()

    # Auth endpoints
    def _handle_login(self, params):
        url, state = auth.get_auth_url()
        self._redirect(url)

    def _handle_callback(self, params):
        code = params.get("code", [None])[0]
        error = params.get("error", [None])[0]
        if error:
            self.send_response(200)
            self.send_header("Content-Type", "text/html")
            self.end_headers()
            self.wfile.write(f"<html><body><h2>Login Failed</h2><p>{error}</p></body></html>".encode())
            return
        if not code:
            return self._send_error("Missing code", 400)
        try:
            import time
            data = auth.exchange_code(code)
            auth.set_active_token({
                "access_token": data["access_token"],
                "refresh_token": data.get("refresh_token"),
                "expires_at": time.time() + data["expires_in"] - 60,
            })
            self._redirect("/acc_viewer.html#logged-in")
        except Exception as e:
            self._send_error(f"Token exchange failed: {e}", 500)

    def _handle_auth_status(self, params):
        profile = acc.get_user_profile()
        if profile:
            self._send_json({"loggedIn": True, **profile})
        else:
            self._send_json({"loggedIn": False})

    def _handle_logout(self, params):
        auth.logout()
        self._send_json({"loggedIn": False})

    def _handle_token(self, params):
        token = auth.get_user_token()
        if not token:
            return self._send_error("Not logged in", 401)
        self._send_json({"access_token": token, "expires_in": 3600})

    # Data Management endpoints
    def _handle_hubs(self, params):
        try:
            self._send_json({"hubs": acc.get_hubs()})
        except Exception as e:
            self._send_error(str(e))

    def _handle_projects(self, params):
        hub_id = params.get("hub_id", [None])[0]
        if not hub_id:
            return self._send_error("Missing hub_id", 400)
        try:
            self._send_json({"projects": acc.get_projects(hub_id)})
        except Exception as e:
            self._send_error(str(e))

    def _handle_top_folders(self, params):
        hub_id = params.get("hub_id", [None])[0]
        project_id = params.get("project_id", [None])[0]
        if not hub_id or not project_id:
            return self._send_error("Missing hub_id or project_id", 400)
        try:
            self._send_json({"items": acc.get_top_folders(hub_id, project_id)})
        except Exception as e:
            self._send_error(str(e))

    def _handle_folder(self, params):
        project_id = params.get("project_id", [None])[0]
        folder_id = params.get("folder_id", [None])[0]
        if not project_id or not folder_id:
            return self._send_error("Missing project_id or folder_id", 400)
        try:
            self._send_json({"items": acc.get_folder_contents(project_id, folder_id)})
        except Exception as e:
            self._send_error(str(e))

    def _handle_versions(self, params):
        project_id = params.get("project_id", [None])[0]
        item_id = params.get("item_id", [None])[0]
        if not project_id or not item_id:
            return self._send_error("Missing project_id or item_id", 400)
        try:
            self._send_json({"versions": acc.get_versions(project_id, item_id)})
        except Exception as e:
            self._send_error(str(e))

    def _handle_item(self, params):
        project_id = params.get("project_id", [None])[0]
        item_id = params.get("item_id", [None])[0]
        if not project_id or not item_id:
            return self._send_error("Missing project_id or item_id", 400)
        try:
            self._send_json(acc.get_item_tip(project_id, item_id))
        except Exception as e:
            self._send_error(str(e))

    # Model Derivative endpoints (proxied via user token)
    def _handle_metadata(self, params):
        urn = params.get("urn", [None])[0]
        if not urn:
            return self._send_error("Missing urn", 400)
        try:
            self._send_json(viewer.get_metadata(urn, use_user_token=True))
        except Exception as e:
            self._send_error(str(e))

    def _handle_tree(self, params):
        urn = params.get("urn", [None])[0]
        guid = params.get("guid", [None])[0]
        if not urn or not guid:
            return self._send_error("Missing urn or guid", 400)
        try:
            self._send_json(viewer.get_tree(urn, guid, use_user_token=True))
        except Exception as e:
            self._send_error(str(e))

    def _handle_properties(self, params):
        urn = params.get("urn", [None])[0]
        guid = params.get("guid", [None])[0]
        if not urn or not guid:
            return self._send_error("Missing urn or guid", 400)
        try:
            self._send_json(viewer.get_properties(urn, guid, use_user_token=True))
        except Exception as e:
            self._send_error(str(e))

    def _handle_phases(self, params):
        urn = params.get("urn", [None])[0]
        guid = params.get("guid", [None])[0]
        if not urn or not guid:
            return self._send_error("Missing urn or guid", 400)
        try:
            self._send_json(viewer.get_phases(urn, guid, use_user_token=True))
        except Exception as e:
            self._send_error(str(e))


def run_bim_server(port=9090, bind="0.0.0.0"):
    """Start the BIM Viewer server (2-legged auth)."""
    server = HTTPServer((bind, port), BIMViewerHandler)
    print(f"BIM Viewer server running at http://localhost:{port}")
    server.serve_forever()


def run_acc_server(port=9091, bind="0.0.0.0"):
    """Start the ACC Viewer server (3-legged auth)."""
    server = HTTPServer((bind, port), ACCViewerHandler)
    print(f"ACC Server running at http://localhost:{port}")
    server.serve_forever()
