"""LS FastAPI"""
from contextlib import asynccontextmanager
from urllib.parse import urlparse
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse, Response
from fastapi.exceptions import RequestValidationError
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.types import ASGIApp
import logging

logger = logging.getLogger("uvicorn.error")

from app.config import settings
from app.api.routers import auth, dashboard, products, orders, customers, categories, brands, member_levels, i18n
from app.api.routers.product_price_rules import router as product_price_rules_router
from app.api.routers.admin.settings import router as admin_settings_router
from app.api.routers.admin.ai_product import router as ai_product_router
from app.api.routers.admin.ai_analysis import router as ai_analysis_router
from app.api.routers.admin.email import router as admin_email_router
from app.api.routers.admin.print_templates import router as print_templates_router
from app.api.routers.admin.theme import admin_router as theme_admin_router, store_router as theme_store_router
from app.api.routers.images import router as images_router
from app.api.routers.upload import router as upload_router
from app.api.routers.logistics_admin import router as logistics_admin_router
from app.api.routers.store.logistics import router as store_logistics_router
from app.api.routers.coupons_admin import router as coupons_admin_router
from app.api.routers.reviews_admin import router as reviews_admin_router
from app.api.routers.banners_admin import router as banners_admin_router
from app.api.routers.store import products as store_products
from app.api.routers.store import auth as store_auth
from app.api.routers.store import newsletter as store_newsletter
from app.api.routers.store import coupons as store_coupons
from app.api.routers.store import promotions as store_promotions
from app.api.routers.store import orders as store_orders
from app.api.routers.store import reviews as store_reviews
from app.api.routers.store import refunds as store_refunds
from app.api.routers.store.shipping import router as store_shipping_router
from app.api.routers.admin import refunds_admin
from app.api.routers.store import addresses as store_addresses
# orders_extended 已合并至 orders.py
from app.api.routers.pages import router as pages_router, store_router as store_pages_router
from app.api.routers.seo import router as seo_router
from app.api.routers.admin.shipping import router as shipping_admin_router
from app.api.routers.admin.packing_rules import router as packing_rules_router
from app.api.routers.admin.payment_gateways import router as payment_gateways_router
from app.api.routers.admin.currencies import router as currencies_admin_router
from app.api.routers.admin.storage import router as storage_admin_router
from app.api.routers.store.wallet import router as store_wallet_router
from app.api.routers.store.currencies import router as store_currencies_router
from app.api.routers.store.payment_latipay import router as latipay_router
from app.api.routers.store.payment_windcave import router as windcave_router
from app.api.routers.store.ai_chat import router as store_ai_chat_router
from app.api.routers.admin.plugins import router as plugins_admin_router, store_router as plugins_store_router
from app.api.routers.admin.permissions import router as permissions_admin_router
from app.api.routers.superadmin.tenants import router as superadmin_tenants_router
from app.api.routers.superadmin.product_templates import router as superadmin_templates_router
from app.api.routers.superadmin.storage_profiles import router as superadmin_storage_profiles_router
from app.api.routers.superadmin.ai_profiles import router as superadmin_ai_profiles_router
from app.api.routers.superadmin.pos_lanes import router as superadmin_pos_lanes_router
from app.api.routers.superadmin.plugin_approvals import router as superadmin_plugin_approvals_router
from app.api.routers.admin.product_templates import router as admin_templates_router
from app.core.services.plugin_loader import load_plugins

# 静态 CORS 白名单（本地开发 + 管理端固定域名）
_cors_origins_cfg = [o.strip() for o in settings.ADMIN_CORS_ORIGINS.split(",") if o.strip()]
_static_origins: set[str] = {
    *_cors_origins_cfg,
    "http://localhost:3000", "http://localhost:3001",
    "http://127.0.0.1:3000", "http://127.0.0.1:3001",
}


class DynamicCORSMiddleware(BaseHTTPMiddleware):
    """动态 CORS：静态白名单 OR 租户域名表中存在的域名均放行。"""

    def __init__(self, app: ASGIApp, static_origins: set[str], debug: bool = False):
        super().__init__(app)
        self.static_origins = static_origins
        self.debug = debug

    async def _allowed(self, origin: str) -> bool:
        if self.debug or origin in self.static_origins:
            return True
        host = urlparse(origin).hostname or ""
        if not host:
            return False
        from app.core.cache import cache_get, cache_set
        cached = await cache_get(f"domain:{host}")
        if cached is not None:
            return cached != -1
        from sqlalchemy import select
        from app.core.models.tenant import Tenant
        from app.database import AsyncSessionLocal
        async with AsyncSessionLocal() as db:
            row = (await db.execute(
                select(Tenant.id, Tenant.status).where(Tenant.domain == host)
            )).first()
        if not row or row.status == "suspended":
            await cache_set(f"domain:{host}", -1, ttl=60)
            return False
        await cache_set(f"domain:{host}", row.id, ttl=300)
        return True

    async def dispatch(self, request: Request, call_next):
        origin = request.headers.get("origin", "")
        if not origin:
            return await call_next(request)

        allowed = await self._allowed(origin)

        if request.method == "OPTIONS":
            if not allowed:
                return Response(status_code=403)
            return Response(
                status_code=204,
                headers={
                    "Access-Control-Allow-Origin": origin,
                    "Access-Control-Allow-Credentials": "true",
                    "Access-Control-Allow-Methods": "*",
                    "Access-Control-Allow-Headers": "*",
                    "Access-Control-Max-Age": "600",
                },
            )

        try:
            response = await call_next(request)
        except Exception:
            # 记录堆栈，否则被下面的通用响应吞掉后 500 无从排查
            logger.exception("unhandled error: %s %s", request.method, request.url.path)
            # 即使路由抛出未捕获异常，也要补上 CORS 头，
            # 否则浏览器只会看到 CORS 错误而看不到真实错误信息
            if allowed:
                return Response(
                    status_code=500,
                    content=b'{"detail":"Internal server error"}',
                    media_type="application/json",
                    headers={
                        "Access-Control-Allow-Origin": origin,
                        "Access-Control-Allow-Credentials": "true",
                    },
                )
            raise
        if allowed:
            response.headers["Access-Control-Allow-Origin"] = origin
            response.headers["Access-Control-Allow-Credentials"] = "true"
        return response


@asynccontextmanager
async def lifespan(app: FastAPI):
    import subprocess, sys, os
    print("LS API starting")
    await load_plugins(app)

    # 启动 Celery worker（与 uvicorn 同生命周期）
    _celery_proc = None
    try:
        _venv_celery = os.path.join(os.path.dirname(sys.executable), "celery")
        _backend_dir = os.path.dirname(os.path.abspath(__file__))
        _celery_log = open(os.path.join(_backend_dir, "celery.log"), "a")
        _celery_proc = subprocess.Popen(
            [_venv_celery, "-A", "app.celery_app", "worker",
             "--loglevel=info", "--concurrency=1", "-n", "email_worker@%h"],
            cwd=_backend_dir,
            stdout=_celery_log,
            stderr=subprocess.STDOUT,
        )
        print(f"[Celery] worker started (pid={_celery_proc.pid})")
    except Exception as e:
        print(f"[Celery] failed to start worker: {e}")

    yield

    # 停止 Celery worker
    if _celery_proc and _celery_proc.poll() is None:
        _celery_proc.terminate()
        try:
            _celery_proc.wait(timeout=10)
        except subprocess.TimeoutExpired:
            _celery_proc.kill()
        print("[Celery] worker stopped")
    print("LS API stopped")


app = FastAPI(
    title="LS API",
    description="SaaS e-commerce platform",
    version="1.0.0",
    docs_url="/docs",
    redoc_url="/redoc",
    lifespan=lifespan,
)

app.add_middleware(
    DynamicCORSMiddleware,
    static_origins=_static_origins,
    debug=settings.DEBUG,
)


@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    body = await request.body()
    logger.error(f"[422] {request.method} {request.url.path} | errors={exc.errors()} | body={body[:2000]}")
    return JSONResponse(status_code=422, content={"detail": exc.errors()})


@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
    logger.exception("unhandled error: %s %s", request.method, request.url.path)
    return JSONResponse(
        status_code=500,
        content={"detail": f"Server error: {str(exc)}"},
    )

app.include_router(auth.router,             prefix="/api")
app.include_router(dashboard.router,        prefix="/api")
app.include_router(products.router,         prefix="/api")
app.include_router(product_price_rules_router, prefix="/api")
app.include_router(brands.router,           prefix="/api")
app.include_router(images_router,           prefix="/api")
app.include_router(upload_router,           prefix="/api")
app.include_router(categories.router,       prefix="/api")
app.include_router(i18n.router,             prefix="/api")
app.include_router(coupons_admin_router,    prefix="/api")
app.include_router(reviews_admin_router,    prefix="/api")
app.include_router(banners_admin_router,    prefix="/api")
app.include_router(orders.router,           prefix="/api")
app.include_router(customers.router,        prefix="/api")
app.include_router(member_levels.router,    prefix="/api")
app.include_router(member_levels.points_router, prefix="/api")
app.include_router(logistics_admin_router,  prefix="/api")
app.include_router(shipping_admin_router,   prefix="/api")
app.include_router(packing_rules_router,   prefix="/api")
app.include_router(admin_settings_router,   prefix="/api")
app.include_router(payment_gateways_router, prefix="/api")
app.include_router(currencies_admin_router, prefix="/api")
app.include_router(storage_admin_router,   prefix="/api")
app.include_router(store_currencies_router,  prefix="/api")
app.include_router(ai_product_router,       prefix="/api")
app.include_router(ai_analysis_router,      prefix="/api")
app.include_router(admin_email_router,      prefix="/api")
app.include_router(print_templates_router,  prefix="/api")
app.include_router(theme_admin_router,      prefix="/api")
app.include_router(theme_store_router,      prefix="/api")

app.include_router(store_products.router,   prefix="/api")
app.include_router(brands.store_router,     prefix="/api")
app.include_router(member_levels.store_router, prefix="/api")
app.include_router(store_auth.router,       prefix="/api")
app.include_router(store_newsletter.router, prefix="/api")
app.include_router(store_coupons.router,    prefix="/api")
app.include_router(store_promotions.router, prefix="/api")
app.include_router(store_orders.router,     prefix="/api")
app.include_router(store_addresses.router,  prefix="/api")
app.include_router(store_reviews.router,    prefix="/api")
app.include_router(store_refunds.router,      prefix="/api")
app.include_router(refunds_admin.router,       prefix="/api")
app.include_router(pages_router,            prefix="/api")
app.include_router(store_logistics_router,  prefix="/api")
app.include_router(store_shipping_router,   prefix="/api")
app.include_router(store_wallet_router,     prefix="/api")
app.include_router(latipay_router,          prefix="/api")
app.include_router(windcave_router,         prefix="/api")
app.include_router(store_pages_router,      prefix="/api")
app.include_router(store_ai_chat_router,    prefix="/api")
app.include_router(plugins_admin_router,    prefix="/api")
app.include_router(plugins_store_router,    prefix="/api")
app.include_router(permissions_admin_router, prefix="/api")
app.include_router(superadmin_tenants_router, prefix="/api")
app.include_router(superadmin_templates_router, prefix="/api")
app.include_router(superadmin_storage_profiles_router, prefix="/api")
app.include_router(superadmin_ai_profiles_router, prefix="/api")
app.include_router(superadmin_pos_lanes_router, prefix="/api")
app.include_router(superadmin_plugin_approvals_router, prefix="/api")
app.include_router(admin_templates_router, prefix="/api")

# SEO 插件路由（sitemap / robots.txt / 商品 SEO 数据）
app.include_router(seo_router, prefix="/api")


@app.get("/health", tags=["system"])
async def health():
    return {"status": "ok", "env": settings.APP_ENV}


@app.get("/", tags=["system"])
async def root():
    return {"name": "LS API", "version": "1.0.0", "docs": "/docs"}
from app.api.routers.admin.stock_statuses import router as stock_statuses_admin_router
from app.api.routers.admin.inventory_alerts import router as inventory_alerts_router
from app.api.routers.admin.audit_logs import router as audit_logs_router
from app.api.routers.admin.navigation_menus import router as nav_menus_admin_router
from app.api.routers.store.navigation_menus import router as nav_menus_store_router
from app.api.routers.admin.articles import router as articles_admin_router
from app.api.routers.store.articles import router as articles_store_router
from app.api.routers.store.mp_bootstrap import router as mp_bootstrap_router
from app.api.routers.store.mp_auth import router as mp_auth_router
from app.api.routers.store.mp_cart import router as mp_cart_router
from app.api.routers.store.wishlist import router as store_wishlist_router
from app.api.routers.store.payment_wxpay import router as store_wxpay_router
from app.api.routers.superadmin.tenant_channels import router as superadmin_channels_router

app.include_router(stock_statuses_admin_router, prefix="/api")
app.include_router(inventory_alerts_router, prefix="/api")
app.include_router(audit_logs_router,         prefix="/api")
app.include_router(nav_menus_admin_router,    prefix="/api")
app.include_router(nav_menus_store_router,    prefix="/api")
app.include_router(articles_admin_router,     prefix="/api")
app.include_router(articles_store_router,     prefix="/api")
app.include_router(mp_bootstrap_router,       prefix="/api")
app.include_router(mp_auth_router,            prefix="/api")
app.include_router(mp_cart_router,            prefix="/api")
app.include_router(store_wishlist_router,     prefix="/api")
app.include_router(store_wxpay_router,       prefix="/api")
app.include_router(superadmin_channels_router, prefix="/api")
