import os
import asyncio
import logging

from dotenv import load_dotenv

# Load .env EARLY (before importing app modules that read env vars at import time).
if not load_dotenv():
    print("Could not load .env file or it is empty.")
    exit(1)

from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import RedirectResponse
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from starlette.exceptions import HTTPException as StarletteHTTPException

from app.middleware import InputSanitizationMiddleware
from app.v1.dependencies import auth  # AuthMiddleware
from app.utils.responses import SanitizedJSONResponse
from app.v1.routers.saas import (
    accounts, subscriptions, support, supportcustomer, users, roles,
    login, dashboard, audit, rbac, appflow, partners, common, invoices, zerodha
)
from app.v1.routers.platform import alerts, teGPT
from app.v1.routers.platform import teGPT_hooks
from app.v1.routers.platform import market_intelligence
from app.v1.routers import crud
from app.v1.routers.cx import crm
from app.v1.background.movers_refresh import movers_refresh_loop
from app.v1.background.global_intraday import global_intraday_loop
from app.v1.background.paper_eod import paper_eod_loop
from app.v1.background.paper_intraday_exit import paper_intraday_exit_loop
from app.v1.background.market_intelligence import market_intelligence_loop
from app.v1.background.stocks_master_refresh import stocks_master_refresh_loop
from app.v1.background.stock_history_refresh import stock_history_refresh_loop
from app.v1.background.early_movers_refresh import early_movers_refresh_loop
from app.db.database import get_mongo_db
from app.v1.background.global_intraday import _get_global_zerodha_client
from app.v1.services.stocks_master import refresh_stocks_master

app = FastAPI(default_response_class=SanitizedJSONResponse)


origins = [
    "https://localhost:3004",
    "https://cabf1e352845.ngrok-free.app/",
    "https://dev.movex.ai",
    
]
app.add_middleware(auth.AuthMiddleware)
app.add_middleware(
    CORSMiddleware,
    #allow_origins=["*"], 
    allow_origins=origins, #    
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)
app.add_middleware(InputSanitizationMiddleware)

# Create a separate router for endpoints with CORS set to "*"
open_cors_router = FastAPI(default_response_class=SanitizedJSONResponse)

# Apply CORS middleware to this specific router
open_cors_router.add_middleware(
    CORSMiddleware,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

app.exception_handler(StarletteHTTPException)


# ✅ No need to define open_cors_router unless you're mounting it explicitly

# ✅ Mount public folder
app.mount("/public", StaticFiles(directory="public"), name="public")

# ✅ Include your routers
app.include_router(accounts.router, prefix="/v1/accounts", tags=["Accounts"])
app.include_router(login.router, prefix="/v1/auth", tags=["Login"])
app.include_router(users.router, prefix="/v1/users", tags=["Users"])
app.include_router(roles.router, prefix="/v1/roles", tags=["Roles"])
app.include_router(rbac.router, prefix="/v1/rbac", tags=["RBAC"])
app.include_router(common.router, prefix="/v1/common", tags=["RBAC"])
app.include_router(appflow.router, prefix="/v1/appflow", tags=["Apps"])
app.include_router(partners.router, prefix="/v1/partners", tags=["Partners"])
app.include_router(audit.router, prefix="/v1/audit", tags=["Audit"])
app.include_router(dashboard.router, prefix="/v1/dashboard", tags=["Dashboard"])
app.include_router(subscriptions.router, prefix="/v1/subscriptions", tags=["Subscriptions"])
app.include_router(invoices.router, prefix="/v1/saasinvoices", tags=["SaaS Invoices"])
app.include_router(support.router, prefix="/v1/support", tags=["Support"])
app.include_router(supportcustomer.router, prefix="/v1/supportcustomer", tags=["Customer Support"])
app.include_router(crm.router, prefix="/v1/cx/crm", tags=["CRM"])
app.include_router(crud.router, prefix="/v1/crud", tags=["CRUD"])

# Zerodha OAuth + postback hooks (mounted under tradeco)
app.include_router(teGPT_hooks.router, prefix="/v1/tradeco", tags=["Zerodha Trade Engine"])
app.include_router(zerodha.router, prefix="/v1/settings/zerodha", tags=["Zerodha Settings"])
app.include_router(teGPT.router, prefix="/v1/tradeco", tags=["ChatGPT Trading Engine"])
app.include_router(alerts.router, prefix="/v1/tradeco", tags=["Alerts"])
app.include_router(market_intelligence.router, prefix="/v1/tradeco", tags=["Market Intelligence"])


def _configure_quiet_dependency_loggers() -> None:
    """Reduce noisy INFO logs from third-party libraries.

    Controlled via `QUIET_LOGS` (default true).
    """

    raw = (os.getenv("QUIET_LOGS", "true") or "true").strip().lower()
    enabled = raw not in ("0", "false", "no", "off")
    if not enabled:
        return

    for name in ("httpx", "httpcore", "openai", "urllib3"):
        try:
            lib_logger = logging.getLogger(name)
            lib_logger.setLevel(logging.WARNING)
            # Some libraries emit very chatty INFO logs (e.g. per-request).
            # Prevent them from bubbling up into uvicorn/root handlers.
            lib_logger.propagate = False
            if not lib_logger.handlers:
                lib_logger.addHandler(logging.NullHandler())
        except Exception:
            pass


# Configure dependency loggers at import time as well, so it takes effect
# even before startup events run (and across uvicorn reloads).
_configure_quiet_dependency_loggers()


@app.on_event("startup")
async def startup_events() -> None:
    """Start background tasks when the FastAPI app boots.

    Currently this kicks off a periodic ET movers refresh loop, which
    scrapes Economic Times top gainers/losers and keeps the `movers`
    collection up to date during IST market hours.
    """
    raw_flag = (os.getenv("ENABLE_BACKGROUND_JOBS", "true") or "true").strip().lower()
    enable_background_jobs = raw_flag not in ("0", "false", "no", "off")

    if not enable_background_jobs:
        print("[Startup] ENABLE_BACKGROUND_JOBS=false -> background loops are DISABLED")
        return

    # Optional: run only a selected subset of background loops.
    # Example: BACKGROUND_JOBS_ONLY=stocks_master,stock_history
    only_raw = (os.getenv("BACKGROUND_JOBS_ONLY", "") or "").strip()
    only = {s.strip().lower() for s in only_raw.split(",") if s.strip()} if only_raw else set()

    if only:
        print(f"[Startup] ENABLE_BACKGROUND_JOBS=true -> starting ONLY background loops: {sorted(only)}")
    else:
        print("[Startup] ENABLE_BACKGROUND_JOBS=true -> starting background loops")

    _configure_quiet_dependency_loggers()

    def _enabled(name: str) -> bool:
        return (not only) or (name.strip().lower() in only)

    # Stocks master + history (optionally bootstrap stocks master once first)
    if _enabled("stocks_master") and _enabled("stock_history"):
        bootstrap = (os.getenv("BOOTSTRAP_STOCKS_MASTER_BEFORE_HISTORY", "1") or "1").strip().lower() not in (
            "0",
            "false",
            "no",
            "off",
        )
        if bootstrap:
            try:
                # Best-effort: populate stocks immediately so history has a universe to work on.
                for db in get_mongo_db():
                    zerodha = _get_global_zerodha_client(db)
                    if not zerodha:
                        print("[Startup] Stocks bootstrap skipped: global Zerodha client unavailable")
                        break
                    print("[Startup] Bootstrapping stocks master (one-time) before history")
                    res = refresh_stocks_master(db=db, zerodha_client=zerodha, exchanges=["NSE"], allow_cache_write=True)
                    print(
                        f"[Startup] Stocks master bootstrap done | inserted={res.get('inserted')} updated={res.get('updated')} kept={res.get('kept')} instruments={res.get('instruments')}"
                    )
                    break
            except Exception:
                print("[Startup] Stocks master bootstrap failed (continuing with loops)")

    if _enabled("movers_refresh"):
        movers_refresh_interval = int(os.getenv("MOVERS_REFRESH_INTERVAL_SECONDS", "600"))
        asyncio.create_task(movers_refresh_loop(interval_seconds=movers_refresh_interval))

    if _enabled("global_intraday"):
        # Back-compat: prefer env var if provided; fall back to 900s.
        global_intraday_interval = int(os.getenv("GLOBAL_INTRADAY_INTERVAL_SECONDS", "900") or "900")
        asyncio.create_task(global_intraday_loop(interval_seconds=global_intraday_interval))

    # NOTE: legacy per-list intraday loops are intentionally disabled.
    # Intraday analysis runs only in `global_intraday_loop()` over the combined watchlist.

    if _enabled("paper_intraday_exit"):
        asyncio.create_task(paper_intraday_exit_loop(interval_seconds=60))

    if _enabled("paper_eod"):
        asyncio.create_task(paper_eod_loop(interval_seconds=60))

    if _enabled("market_intelligence"):
        asyncio.create_task(market_intelligence_loop(interval_seconds=3600))

    if _enabled("stocks_master"):
        asyncio.create_task(stocks_master_refresh_loop())

    if _enabled("stock_history"):
        asyncio.create_task(stock_history_refresh_loop())

    if _enabled("early_movers"):
        asyncio.create_task(early_movers_refresh_loop())

# ✅ IP-restricted docs access
allowed_ips = ["127.0.0.1"]

@app.middleware("http")
async def restrict_access(request: Request, call_next):
    client_ip = request.client.host
    if request.url.path.startswith("/docs") or request.url.path.startswith("/redoc"):
        if client_ip not in allowed_ips:
            raise HTTPException(status_code=404, detail="Forbidden")
    return await call_next(request)
