from __future__ import annotations

import asyncio
import os
from pathlib import Path
from typing import Callable, Awaitable, Sequence

from quart import Quart, request, g, send_from_directory

from config import init_config, config, pretty
from models import KV  # ensure models imported

from db.session import register_db
from suma_browser.app.middleware import register as register_middleware
from suma_browser.app.redis_cacher import register as register_redis
from suma_browser.app.csrf import protect
from suma_browser.app.errors import errors

from .jinja_setup import setup_jinja
from .user_loader import load_current_user


# Async init of config (runs once at import)
asyncio.run(init_config())

BASE_DIR = Path(__file__).resolve().parent.parent
STATIC_DIR = str(BASE_DIR / "static")
TEMPLATE_DIR = str(BASE_DIR / "suma_browser" / "templates")


def create_base_app(
    name: str,
    *,
    context_fn: Callable[[], Awaitable[dict]] | None = None,
    before_request_fns: Sequence[Callable[[], Awaitable[None]]] | None = None,
) -> Quart:
    """
    Create a Quart app with shared infrastructure.

    Parameters
    ----------
    name:
        Application name (also used as CACHE_APP_PREFIX).
    context_fn:
        Async function returning a dict for template context.
        Each app provides its own — the cart app queries locally,
        while coop/market apps fetch via internal API.
        If not provided, a minimal default context is used.
    before_request_fns:
        Extra before-request hooks (e.g. cart_loader for the cart app).
    """
    app = Quart(
        name,
        static_folder=STATIC_DIR,
        static_url_path="/static",
        template_folder=TEMPLATE_DIR,
    )

    app.secret_key = os.getenv("SECRET_KEY", "dev-secret-key-change-me-777")

    # Session cookie shared across subdomains
    cookie_domain = os.getenv("SESSION_COOKIE_DOMAIN")  # e.g. ".rose-ash.com"
    if cookie_domain:
        app.config["SESSION_COOKIE_DOMAIN"] = cookie_domain
    app.config["SESSION_COOKIE_NAME"] = "coop_session"

    # Ghost / Redis config
    app.config["GHOST_API_URL"] = os.getenv("GHOST_API_URL")
    app.config["GHOST_PUBLIC_URL"] = os.getenv("GHOST_PUBLIC_URL")
    app.config["GHOST_CONTENT_KEY"] = os.getenv("GHOST_CONTENT_API_KEY")
    app.config["REDIS_URL"] = os.getenv("REDIS_URL")

    # Cache app prefix for key namespacing
    app.config["CACHE_APP_PREFIX"] = name

    # --- infrastructure ---
    register_middleware(app)
    register_db(app)
    register_redis(app)
    setup_jinja(app)
    errors(app)

    # --- before-request hooks ---
    @app.before_request
    async def _route_log():
        g.root = request.headers.get("x-forwarded-prefix", "/")
        g.scheme = request.scheme
        g.host = request.host

    @app.before_request
    async def _load_user():
        await load_current_user()

    # Register any app-specific before-request hooks (e.g. cart loader)
    if before_request_fns:
        for fn in before_request_fns:
            app.before_request(fn)

    @app.before_request
    async def _csrf_protect():
        await protect()

    # --- after-request hooks ---
    @app.after_request
    async def _add_hx_preserve_search_header(response):
        value = request.headers.get("X-Search")
        if value is not None:
            response.headers["HX-Preserve-Search"] = value
        return response

    # --- context processor ---
    if context_fn is not None:
        @app.context_processor
        async def _inject_base():
            return await context_fn()
    else:
        # Minimal fallback (no cart, no menu_items)
        from .context import base_context

        @app.context_processor
        async def _inject_base():
            return await base_context()

    # --- cleanup internal API client on shutdown ---
    @app.after_serving
    async def _close_internal_client():
        from .internal_api import close_client
        await close_client()

    # --- startup ---
    @app.before_serving
    async def _startup():
        await init_config()
        print(pretty())

    # --- favicon ---
    @app.get("/favicon.ico")
    async def favicon():
        return await send_from_directory("static", "favicon.ico")

    return app
