"""
Main HTTP server that aggregates all GCP service emulators.

Creates a Flask application with all service blueprints registered,
a health endpoint, and CORS/auth middleware. Automatically discovers
and loads both hand-written and auto-generated service modules.
"""

import importlib
import json
import logging
import os
from typing import Optional

from flask import Flask, jsonify, request

from gcplocal.constants import DEFAULT_HOST, DEFAULT_PORT, SUPPORTED_SERVICES
from gcplocal.state import StateStore, state as global_state
from gcplocal.auth import authenticate_request

logger = logging.getLogger("gcplocal")


class GCPLocalServer:
    """Orchestrates all emulated GCP services behind a single HTTP server."""

    def __init__(self, host: str = DEFAULT_HOST, port: int = DEFAULT_PORT, state: Optional[StateStore] = None):
        self.host = host
        self.port = port
        self.state = state or global_state
        self.app = create_app(self.state)

    def start(self, debug: bool = False):
        logger.info(f"gcplocal starting on http://{self.host}:{self.port}")
        self.app.run(host=self.host, port=self.port, debug=debug, threaded=True)


def create_app(state: Optional[StateStore] = None) -> Flask:
    """Build the Flask app with all service emulators registered."""
    state = state or global_state
    app = Flask("gcplocal")
    app.config["JSONIFY_PRETTYPRINT_REGULAR"] = False

    _register_middleware(app)
    _register_health(app, state)
    _register_services(app, state)
    _register_oauth(app)
    _register_fallback(app)

    return app


def _register_middleware(app: Flask):
    @app.before_request
    def _auth_middleware():
        if request.path in ("/_ah/health", "/health", "/"):
            return None
        authenticate_request(request)
        return None

    @app.after_request
    def _cors(response):
        response.headers["Access-Control-Allow-Origin"] = "*"
        response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, PATCH, DELETE, OPTIONS"
        response.headers["Access-Control-Allow-Headers"] = "Authorization, Content-Type"
        response.headers["Server"] = "gcplocal"
        return response


def _register_health(app: Flask, state: StateStore):
    @app.route("/")
    @app.route("/health")
    @app.route("/_ah/health")
    def health():
        resource_count = len(state._resources)
        return jsonify({
            "status": "healthy",
            "service": "gcplocal",
            "services": SUPPORTED_SERVICES,
            "resources_in_memory": resource_count,
        })

    @app.route("/reset", methods=["POST"])
    def reset():
        state.clear()
        return jsonify({"status": "reset", "message": "All state cleared"})


def _register_services(app: Flask, state: StateStore):
    from gcplocal.services.compute import ComputeService
    from gcplocal.services.storage import StorageService
    from gcplocal.services.iam import IAMService
    from gcplocal.services.pubsub import PubSubService
    from gcplocal.services.resourcemanager import ResourceManagerService
    from gcplocal.services.sql import CloudSQLService

    core_services = [
        ComputeService(state),
        StorageService(state),
        IAMService(state),
        PubSubService(state),
        ResourceManagerService(state),
        CloudSQLService(state),
    ]

    registered_names = set()
    for svc in core_services:
        bp = svc.create_blueprint()
        app.register_blueprint(bp)
        registered_names.add(svc.service_name)
        logger.info(f"  Registered service: {svc.service_name} ({svc.api_prefix})")

    _register_auto_generated_services(app, state, registered_names)


def _register_auto_generated_services(app: Flask, state: StateStore, skip: set):
    """Discover and load auto-generated services from the service registry."""
    registry_path = os.path.join(os.path.dirname(__file__), "service_registry.json")
    if not os.path.exists(registry_path):
        return

    try:
        with open(registry_path) as f:
            registry = json.load(f)
    except (json.JSONDecodeError, IOError) as e:
        logger.warning(f"Failed to load service registry: {e}")
        return

    for svc_name, entry in registry.items():
        if svc_name in skip:
            continue

        module_path = entry.get("module")
        class_name = entry.get("class_name")
        if not module_path or not class_name:
            continue

        try:
            mod = importlib.import_module(module_path)
            svc_class = getattr(mod, class_name)
            svc = svc_class(state)
            bp = svc.create_blueprint()
            app.register_blueprint(bp)
            logger.info(f"  Registered auto-generated service: {svc.service_name} ({svc.api_prefix})")
        except Exception as e:
            logger.warning(f"  Failed to load {svc_name}: {e}")


def _register_oauth(app: Flask):
    """Fake OAuth2 token endpoint so Terraform's auth flow works."""
    @app.route("/token", methods=["POST"])
    @app.route("/oauth2/v4/token", methods=["POST"])
    @app.route("/oauth2/v3/token", methods=["POST"])
    def fake_token():
        return jsonify({
            "access_token": "ya29.gcplocal-mock-token",
            "expires_in": 3600,
            "token_type": "Bearer",
        })

    @app.route("/computeMetadata/v1/instance/service-accounts/default/token", methods=["GET"])
    def fake_metadata_token():
        return jsonify({
            "access_token": "ya29.gcplocal-mock-token",
            "expires_in": 3600,
            "token_type": "Bearer",
        })

    @app.route("/computeMetadata/v1/project/project-id", methods=["GET"])
    def fake_project_id():
        return "gcplocal-project"


def _register_fallback(app: Flask):
    @app.errorhandler(404)
    def not_found(e):
        logger.warning(f"Unhandled route: {request.method} {request.path}")
        return jsonify({
            "error": {
                "code": 404,
                "message": f"gcplocal: no handler for {request.method} {request.path}. "
                           f"This API endpoint may not be implemented yet.",
                "status": "NOT_FOUND",
            }
        }), 404


def start_server(host: str = DEFAULT_HOST, port: int = DEFAULT_PORT, debug: bool = False):
    """Convenience function to create and start the server."""
    server = GCPLocalServer(host=host, port=port)
    server.start(debug=debug)
