import os
import json
# Fix matplotlib/gunicorn permission issues in server environment
_temp_dir = os.path.join(os.getcwd(), 'temp')
os.makedirs(_temp_dir, exist_ok=True)
os.environ['MPLCONFIGDIR'] = _temp_dir
os.environ['HOME'] = _temp_dir

from fastapi import FastAPI, HTTPException, Request, WebSocket, WebSocketDisconnect, Query
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
import traceback
import os
import auth_utils
import session_utils
from services.global_manager import global_manager
from services.chat_manager import manager

import locales
import config
import db
from routers import auth, orders, catalog, portfolio, files, chat, blog, admin, contact, warehouse

app = FastAPI(title="Radionica 3D API")

@app.on_event("startup")
async def startup_event():
    print("Application starting up...")

# Configure CORS
origins = [
    "http://localhost:5173",
    "http://127.0.0.1:5173",
    "http://localhost:5000",
    "http://localhost:8001",
    "https://radionica3d.me",
]
extra_origins = os.getenv("CORS_ORIGINS")
if extra_origins:
    origins.extend(extra_origins.split(","))

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    lang = request.query_params.get("lang", "en")
    errors = []
    for error in exc.errors():
        error_type = error.get("type", "unknown")
        ctx = error.get("ctx", {})
        translated_msg = locales.translate_error(error_type, lang, **ctx)
        loc = ".".join(str(l) for l in error.get("loc", [])[1:])
        errors.append({
            "loc": error.get("loc"),
            "msg": f"{loc}: {translated_msg}" if loc else translated_msg,
            "type": error_type
        })
    return JSONResponse(status_code=422, content={"detail": errors})

@app.exception_handler(Exception)
async def all_exception_handler(request: Request, exc: Exception):
    print(f"ERROR: {exc}")
    traceback.print_exc()
    if config.DEBUG:
        return JSONResponse(
            status_code=500,
            content={"detail": str(exc), "traceback": traceback.format_exc()}
        )
    return JSONResponse(status_code=500, content={"detail": "Internal server error"})

# Add custom exception logging or other middleware here if needed

# Include Routers
app.include_router(auth.router)
app.include_router(orders.router)
app.include_router(catalog.router)
app.include_router(portfolio.router)
app.include_router(files.router)
app.include_router(chat.router)
app.include_router(blog.router)
app.include_router(admin.router)
app.include_router(contact.router)
app.include_router(warehouse.router)

# WebSocket Global Handler (Centralized to handle various proxy prefixes)
@app.websocket("/global")
async def ws_global(websocket: WebSocket, token: str = Query(...)):
    payload = auth_utils.decode_token(token)
    if not payload:
        print(f"DEBUG: WS Global Auth failed for token: {token[:10]}...")
        await websocket.close(code=4001)
        return
    user_id = payload.get("id")
    role = payload.get("role")
    if not user_id:
        print(f"DEBUG: WS Global Auth failed: no user_id in payload")
        await websocket.close(code=4001)
        return
        
    await global_manager.connect(websocket, user_id, role)
    session_utils.track_user_ping(user_id)
    
    # Send initial unread count
    if role != 'admin':
        await global_manager.notify_user(user_id)
    else:
        await global_manager.notify_admins()
    
    try:
        while True:
            data = await websocket.receive_text()
            if data == "ping":
                session_utils.track_user_ping(user_id)
    except WebSocketDisconnect:
        global_manager.disconnect(websocket, user_id)
    except Exception as e:
        global_manager.disconnect(websocket, user_id)

@app.websocket("/chat")
async def ws_chat(websocket: WebSocket, token: str = Query(...), order_id: int = Query(...)):
    payload = auth_utils.decode_token(token)
    if not payload:
        print(f"DEBUG: Chat WS Auth failed for Order #{order_id}")
        await websocket.close(code=4001)
        return
    role = payload.get("role")
    user_id = payload.get("id")
    print(f"DEBUG: Chat WS connection attempt for Order #{order_id} from User #{user_id} (Role: {role})")
    
    if role != 'admin':
        user_info = db.execute_query("SELECT can_chat FROM users WHERE id = %s", (user_id,))
        if not user_info or not user_info[0]['can_chat']:
            print(f"DEBUG: Chat WS rejected: user cannot chat")
            await websocket.close(code=4003)
            return

    order = db.execute_query("SELECT user_id FROM orders WHERE id = %s", (order_id,))
    if not order:
        print(f"DEBUG: Chat WS rejected: order #{order_id} not found")
        await websocket.close(code=4004)
        return
    if role != 'admin' and order[0]['user_id'] != user_id:
        print(f"DEBUG: Chat WS rejected: not authorized for this order")
        await websocket.close(code=4003)
        return
    
    await manager.connect(websocket, order_id, role)
    try:
        while True:
            data = await websocket.receive_text()
            try:
                # Try to parse as JSON for new "true chat" protocol
                msg_data = json.loads(data)
                if msg_data.get("type") == "message":
                    text = msg_data.get("text", "")
                    await manager.handle_message(order_id, user_id, role, text)
                elif msg_data.get("type") == "read":
                    await _handle_read_event(order_id, user_id, role)
            except json.JSONDecodeError:
                # Fallback for legacy plain text messages (like "read")
                if data == "read":
                    await _handle_read_event(order_id, user_id, role)
    except WebSocketDisconnect:
        manager.disconnect(websocket, order_id)
    except Exception as e:
        print(f"ERROR in ws_chat loop: {e}")
        manager.disconnect(websocket, order_id)

async def _handle_read_event(order_id: int, user_id: int, role: str):
    if role == 'admin':
        db.execute_commit("UPDATE order_messages SET is_read = TRUE WHERE order_id = %s AND is_from_admin = FALSE AND is_read = FALSE", (order_id,))
        await global_manager.notify_admins()
        await global_manager.notify_order_read(order_id)
    else:
        db.execute_commit("UPDATE order_messages SET is_read = TRUE WHERE order_id = %s AND is_from_admin = TRUE AND is_read = FALSE", (order_id,))
        await global_manager.notify_user(user_id)

# Mount Static Files
for sub in ["", "previews", "invoices", "reports"]:
    path = os.path.join("uploads", sub)
    if not os.path.exists(path):
        os.makedirs(path, exist_ok=True)
# Mount static files for uploads and previews with caching
app.mount("/uploads", StaticFiles(directory="uploads", html=False), name="uploads")

@app.middleware("http")
async def add_cache_control_header(request, call_next):
    response = await call_next(request)
    if request.url.path.startswith("/uploads"):
        response.headers["Cache-Control"] = "public, max-age=604800, immutable"
    return response

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="127.0.0.1", port=8000)
