from contextlib import asynccontextmanager
from pathlib import Path

from apscheduler.schedulers.background import BackgroundScheduler
from fastapi import FastAPI, HTTPException, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, HTMLResponse
from fastapi.staticfiles import StaticFiles

from app.api import applications, auth, cvs, jobs, profile, plans, billing, admin, marketing, intelligence, tier_b, tier_c, tier_d, brand, agent_ops, launch
from app.core.config import get_settings
from app.core.database import Base, SessionLocal, engine
from app.core.migrate import migrate_sqlite
from app.services.scrape_service import run_all_enabled
from app.services.seed import seed_database
from app.services import brand as brand_svc
from app.services import proposals as proposals_svc
from app.services import campaigns as campaigns_svc

scheduler = BackgroundScheduler()
ROOT = Path(__file__).resolve().parents[2]  # JobsWorld/
FRONTEND_DIST = ROOT / "frontend" / "dist"


def scheduled_scrape() -> None:
    db = SessionLocal()
    try:
        run_all_enabled(db)
    finally:
        db.close()


@asynccontextmanager
async def lifespan(_: FastAPI):
    settings = get_settings()
    settings.data_dir.mkdir(parents=True, exist_ok=True)
    Base.metadata.create_all(bind=engine)
    migrate_sqlite(engine)
    db = SessionLocal()
    try:
        seed_database(db)
    finally:
        db.close()

    # Brand + propuestas + campañas (idempotente)
    brand_svc.list_presets()
    brand_svc.get_active()
    proposals_svc.ensure_seed_if_empty()
    campaigns_svc.seed_default_campaigns()

    if not scheduler.running:
        scheduler.add_job(
            scheduled_scrape,
            "interval",
            minutes=max(15, settings.scrape_interval_minutes),
            id="scrape_all",
            replace_existing=True,
        )

        def scheduled_agent_proposals() -> None:
            try:
                proposals_svc.generate_from_lab(use_llm=True, count=3)
            except Exception:  # noqa: BLE001
                pass

        scheduler.add_job(
            scheduled_agent_proposals,
            "interval",
            hours=12,
            id="lab_proposals",
            replace_existing=True,
        )

        def scheduled_marketing_tick() -> None:
            try:
                from app.services import marketing_engine as eng

                eng.auto_tick()
            except Exception:  # noqa: BLE001
                pass

        scheduler.add_job(
            scheduled_marketing_tick,
            "interval",
            hours=6,
            id="marketing_auto",
            replace_existing=True,
        )
        scheduler.start()
    yield
    if scheduler.running:
        scheduler.shutdown(wait=False)


app = FastAPI(title="JobsWorld", version="2.7.0", lifespan=lifespan)
settings = get_settings()
app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.cors_origin_list + ["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

app.include_router(auth.router, prefix="/api")
app.include_router(profile.router, prefix="/api")
app.include_router(jobs.router, prefix="/api")
app.include_router(cvs.router, prefix="/api")
app.include_router(applications.router, prefix="/api")
app.include_router(plans.router, prefix="/api")
app.include_router(billing.router, prefix="/api")
app.include_router(admin.router, prefix="/api")
app.include_router(marketing.router, prefix="/api")
app.include_router(intelligence.router, prefix="/api")
app.include_router(tier_b.router, prefix="/api")
app.include_router(tier_c.router, prefix="/api")
app.include_router(tier_d.router, prefix="/api")
app.include_router(brand.router, prefix="/api")
app.include_router(agent_ops.router, prefix="/api")
app.include_router(launch.router, prefix="/api")


@app.get("/api/health")
def health():
    from app.services import ai_cv

    return {"status": "ok", "app": "JobsWorld", "ai": ai_cv.llm_status()}


# Assets + SPA fallback (para /login, /jobs/1, etc.)
if FRONTEND_DIST.is_dir():
    assets_dir = FRONTEND_DIST / "assets"
    if assets_dir.is_dir():
        app.mount("/assets", StaticFiles(directory=str(assets_dir)), name="assets")

    @app.get("/")
    def spa_root():
        return FileResponse(FRONTEND_DIST / "index.html")

    @app.get("/{full_path:path}")
    def spa_fallback(full_path: str, request: Request):
        if full_path.startswith("api/") or full_path.startswith("api"):
            raise HTTPException(status_code=404, detail="Not Found")
        if full_path.startswith("l/"):
            slug = full_path.split("/", 1)[1].split("?")[0]
            from app.services.launch import is_crawler, og_html

            if is_crawler(request.headers.get("user-agent") or ""):
                from app.core.config import get_settings
                from app.services.launch import get_social

                social = get_social()
                origin = (
                    (social.get("public_base_url") or "").rstrip("/")
                    or (get_settings().public_base_url or "").rstrip("/")
                    or str(request.base_url).rstrip("/")
                )
                html = og_html(slug, origin)
                if html:
                    return HTMLResponse(html)
        candidate = FRONTEND_DIST / full_path
        if candidate.is_file():
            return FileResponse(candidate)
        return FileResponse(FRONTEND_DIST / "index.html")
