import asyncio
import logging
from pathlib import Path
from urllib.parse import quote, urlparse

from fastapi import FastAPI, Request
from fastapi.exception_handlers import http_exception_handler
from fastapi.exceptions import HTTPException as FastAPIHTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles

from app.core.services import build_services
from app.startup import crear_usuario_admin
from app.routes import users, dashboard, admin, profile, pages, documents, history, soat, batches

app = FastAPI(title="EpicrisisIA - MVP")
BASE_DIR = Path(__file__).resolve().parent.parent
services = build_services(BASE_DIR)
app.state.services = services

static_dir = BASE_DIR / "static"

# Reduce noisy passlib bcrypt version warnings on bcrypt>=4
logging.getLogger("passlib.handlers.bcrypt").setLevel(logging.ERROR)

# Middleware CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Routers
app.include_router(users.router)
app.include_router(dashboard.router)
app.include_router(admin.router)
app.include_router(profile.router)
app.include_router(pages.router)
app.include_router(documents.router)
app.include_router(history.router)
app.include_router(soat.router)
app.include_router(batches.router)

app.mount("/static", StaticFiles(directory=static_dir), name="static")


def _is_fetch_or_api_request(request: Request) -> bool:
    headers = request.headers
    accept = headers.get("accept", "").lower()
    x_requested_with = headers.get("x-requested-with", "").lower()
    sec_fetch_mode = headers.get("sec-fetch-mode", "").lower()
    path = request.url.path

    if x_requested_with in {"xmlhttprequest", "fetch"}:
        return True
    if path.startswith("/api/"):
        return True
    if "application/json" in accept:
        return True
    if sec_fetch_mode and sec_fetch_mode != "navigate" and "text/html" not in accept:
        return True
    return False


def _resolve_next_path(request: Request, *, prefer_referer: bool = False) -> str:
    if prefer_referer:
        referer = request.headers.get("referer", "").strip()
        if referer:
            parsed_referer = urlparse(referer)
            same_origin = (
                (not parsed_referer.scheme or parsed_referer.scheme == request.url.scheme)
                and (not parsed_referer.netloc or parsed_referer.netloc == request.url.netloc)
            )
            if same_origin and parsed_referer.path and not parsed_referer.path.startswith("/login"):
                referer_path = parsed_referer.path
                if parsed_referer.query:
                    referer_path = f"{referer_path}?{parsed_referer.query}"
                return referer_path

    next_path = request.url.path
    if request.url.query:
        next_path = f"{next_path}?{request.url.query}"
    return next_path


def _build_login_url(request: Request, *, prefer_referer: bool = False) -> str:
    next_path = _resolve_next_path(request, prefer_referer=prefer_referer)
    return f"/login?next={quote(next_path, safe='')}"


@app.exception_handler(FastAPIHTTPException)
async def http_exception_with_auth_redirect(request: Request, exc: FastAPIHTTPException):
    is_auth_expired = (
        exc.status_code == 401
        and str(exc.detail) == "Not authenticated"
        and (exc.headers or {}).get("X-Auth-Error") == "auth_expired"
    )
    if not is_auth_expired:
        return await http_exception_handler(request, exc)

    is_fetch_or_api = _is_fetch_or_api_request(request)
    login_url = _build_login_url(request, prefer_referer=is_fetch_or_api)
    if is_fetch_or_api:
        response = JSONResponse(
            status_code=401,
            content={
                "detail": "Not authenticated",
                "code": "auth_expired",
                "login_url": login_url,
            },
        )
    else:
        response = RedirectResponse(url=login_url, status_code=303)
    response.delete_cookie("access_token")
    return response


@app.on_event("startup")
async def startup_event():
    await asyncio.create_task(crear_usuario_admin())
