from fastapi import FastAPI, UploadFile, File, HTTPException, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware
from app.routes import auth, asim, position_type, mass_template, schedule, unavailability, special, portal, shift_swap, daily, report, area, position_slot, area_unavailability, attendance, seat_layout
from app.routes.settings import router as settings_router, public_router as settings_public_router
from app.database import get_db, SessionLocal
from app.models.asim import Asim
from app.models.system_setting import SystemSetting
from app.core.security import get_current_user, require_pengurus, decode_token
from app.core.limiter import limiter
from slowapi import _rate_limit_exceeded_handler
from slowapi.errors import RateLimitExceeded
from sqlalchemy.orm import Session
from fastapi import Depends
import shutil
import uuid
import os

ALLOWED_CONTENT_TYPES = {"image/jpeg", "image/png", "image/jpg"}
EXT_MAP = {"image/jpeg": "jpg", "image/jpg": "jpg", "image/png": "png"}

app = FastAPI(title="ASIM Scheduling API")

# Rate limiting (anti brute-force)
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)


# ── Maintenance mode middleware ───────────────────────────────
MAINTENANCE_BYPASS_PATHS = {
    "/api/auth/login",
    "/api/settings/maintenance-status",
    "/api/health",
    "/",
}


class MaintenanceMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        path = request.url.path

        # Always allow bypassed paths
        if path in MAINTENANCE_BYPASS_PATHS:
            return await call_next(request)

        # Check maintenance mode
        db = SessionLocal()
        try:
            row = db.query(SystemSetting).filter(
                SystemSetting.key == "maintenance_mode"
            ).first()
            is_maintenance = row and row.value == "true"
        finally:
            db.close()

        if not is_maintenance:
            return await call_next(request)

        # In maintenance: allow super_admin through
        auth_header = request.headers.get("authorization", "")
        if auth_header.startswith("Bearer "):
            try:
                payload = decode_token(auth_header[7:])
                if payload.get("role") == "super_admin":
                    return await call_next(request)
            except Exception:
                pass

        # Block everyone else
        return JSONResponse(
            status_code=503,
            content={"detail": "Sistem sedang dalam maintenance"},
        )


app.add_middleware(MaintenanceMiddleware)

app.add_middleware(
    CORSMiddleware,
    allow_origins=[
        "http://localhost:3000",
        "http://localhost:8000",
        "http://localhost:8001",
        "http://localhost:8002",
        "http://localhost:8003",
        "http://localhost:8004",
        "http://localhost:8005",
        "http://asim.goteku.com",
    ],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Static files untuk serve foto
app.mount("/uploads", StaticFiles(directory="uploads"), name="uploads")

# ── Public routes (login, dll) ─────────────────────────────
app.include_router(auth.router)
app.include_router(settings_public_router)  # /api/settings/maintenance-status (no auth)

# ── Any logged-in user ─────────────────────────────────────
# (write endpoints di dalam file masing-masing punya require_pengurus individual)
app.include_router(portal.router,         dependencies=[Depends(get_current_user)])
app.include_router(shift_swap.router,     dependencies=[Depends(get_current_user)])
app.include_router(unavailability.router,      dependencies=[Depends(get_current_user)])
app.include_router(area_unavailability.router, dependencies=[Depends(get_current_user)])
app.include_router(schedule.router,       dependencies=[Depends(get_current_user)])
app.include_router(special.router,        dependencies=[Depends(get_current_user)])
app.include_router(daily.router,          dependencies=[Depends(get_current_user)])
app.include_router(attendance.router,     dependencies=[Depends(get_current_user)])
app.include_router(seat_layout.router,   dependencies=[Depends(get_current_user)])

# ── Pengurus / super_admin only ────────────────────────────
app.include_router(asim.router,           dependencies=[Depends(require_pengurus)])
app.include_router(position_type.router,  dependencies=[Depends(get_current_user)])
app.include_router(mass_template.router,  dependencies=[Depends(require_pengurus)])
app.include_router(report.router,         dependencies=[Depends(require_pengurus)])
app.include_router(area.router,           dependencies=[Depends(get_current_user)])
app.include_router(position_slot.router,  dependencies=[Depends(require_pengurus)])
app.include_router(settings_router)       # /api/settings (super_admin guards inside)


@app.get("/")
def root():
    return {"message": "ASIM Scheduling API is running!"}

@app.get("/api/health")
def health():
    return {"status": "ok", "service": "ASIM Scheduling"}

@app.post("/api/asim/{asim_id}/photo")
async def upload_photo(
    asim_id: int,
    file: UploadFile = File(...),
    db: Session = Depends(get_db),
    _: object = Depends(require_pengurus),
):
    asim = db.query(Asim).filter(Asim.id == asim_id).first()
    if not asim:
        raise HTTPException(status_code=404, detail="ASIM tidak ditemukan")

    # Validasi MIME type
    if file.content_type not in ALLOWED_CONTENT_TYPES:
        raise HTTPException(status_code=400, detail="File harus berupa JPG atau PNG")

    # Validasi magic bytes (bukan cuma MIME dari header)
    header = await file.read(12)
    await file.seek(0)
    is_jpg = header[:3] == b'\xff\xd8\xff'
    is_png = header[:8] == b'\x89PNG\r\n\x1a\n'
    if not (is_jpg or is_png):
        raise HTTPException(status_code=400, detail="Konten file tidak valid (bukan gambar)")

    # Hapus foto lama jika ada
    if asim.photo:
        old_path = asim.photo.lstrip('/')
        old_path = old_path.replace("uploads/", "uploads/", 1)
        # handle legacy http://localhost:8000/ prefix
        if old_path.startswith("http"):
            old_path = old_path.split("/uploads/", 1)[-1]
            old_path = f"uploads/{old_path}"
        if os.path.exists(old_path):
            os.remove(old_path)

    # Ekstensi dari MIME type (bukan filename — cegah path traversal)
    ext = EXT_MAP.get(file.content_type, "jpg")
    filename = f"uploads/photos/asim_{asim_id}_{uuid.uuid4().hex[:8]}.{ext}"
    with open(filename, "wb") as buffer:
        shutil.copyfileobj(file.file, buffer)

    # Update database — simpan sebagai relative path agar bekerja di semua environment
    asim.photo = f"/{filename}"
    db.commit()

    return {"photo_url": asim.photo}