import asyncio
import json
import subprocess
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Query, status
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from sqlalchemy.future import select
from database import engine, Base, AsyncSessionLocal
from models import User, Role, Project, KanbanColumn
from auth import get_password_hash
from routers import (
    auth_router, kanban_router, admin_router, settings_router, 
    team_router, ticket_router, notification_router, kb_router, 
    automation_router, superadmin_router, backup_router, dashboard_router, debug_router
)
from backup_manager import backup_scheduler

app = FastAPI(title="Gestión de Proyectos API", version="1.0.0")

import traceback
from fastapi.responses import JSONResponse

@app.exception_handler(Exception)
async def global_exception_handler(request, exc):
    print("GLOBAL EXCEPTION TRIGGERED:")
    traceback.print_exc()
    return JSONResponse(
        status_code=500,
        content={"error": str(exc), "traceback": traceback.format_exc()}
    )


# CORS for frontend
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.middleware("http")
async def add_cache_control_header(request, call_next):
    response = await call_next(request)
    if request.url.path.startswith("/api/"):
        response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
        response.headers["Pragma"] = "no-cache"
        response.headers["Expires"] = "0"
    return response

# Include routers
app.include_router(auth_router.router)

app.include_router(kanban_router.router)
app.include_router(admin_router.router)
app.include_router(settings_router.router)
app.include_router(team_router.router)
app.include_router(ticket_router.router)
app.include_router(notification_router.router)
app.include_router(kb_router.router)
app.include_router(automation_router.router)

# Manejo de WebSockets para notificaciones (usando la conexiÃ³n de Redis)
app.include_router(superadmin_router.router)
app.include_router(backup_router.router)
app.include_router(dashboard_router.router)
app.include_router(debug_router.router)

class ConnectionManager:
    def __init__(self):
        self.active_connections: list[WebSocket] = []

    async def connect(self, websocket: WebSocket):
        await websocket.accept()
        self.active_connections.append(websocket)

    def disconnect(self, websocket: WebSocket):
        self.active_connections.remove(websocket)

    async def broadcast(self, message: str):
        for connection in self.active_connections:
            await connection.send_text(message)

manager = ConnectionManager()

@app.websocket("/ws/kanban")
async def websocket_endpoint(websocket: WebSocket):
    await manager.connect(websocket)
    try:
        while True:
            data = await websocket.receive_text()
            # Parse data and broadcast
            parsed = json.loads(data)
            await manager.broadcast(json.dumps({
                "type": "update",
                "payload": parsed
            }))
    except WebSocketDisconnect:
        manager.disconnect(websocket)

@app.websocket("/ws/admin/logs")
async def admin_logs_ws(websocket: WebSocket, token: str = Query(...)):
    # Aquí iría la verificación de token JWT para asegurar que es Administrador.
    # Para efectos demostrativos y permitirte probarlo sin UI de login, aceptaremos la conexión.
    await websocket.accept()
    
    # Run journalctl in background and stream output
    try:
        process = await asyncio.create_subprocess_exec(
            'journalctl', '-u', 'ganttscrum.service', '-n', '50', '-f', '--no-pager',
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE
        )
        while True:
            line = await process.stdout.readline()
            if not line:
                break
            await websocket.send_text(line.decode('utf-8').strip())
    except WebSocketDisconnect:
        process.terminate()
    except Exception as e:
        await websocket.send_text(f"[Error interno del servidor] {str(e)}")
        if 'process' in locals():
            process.terminate()

@app.on_event("startup")
async def startup_event():
    # Create tables automatically for ease of development
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)
        
    # Seed DB with initial data
    async with AsyncSessionLocal() as db:
        # Check if admin role exists
        stmt = select(Role).where(Role.name == "Administrador")
        result = await db.execute(stmt)
        admin_role = result.scalar_one_or_none()
        if not admin_role:
            admin_role = Role(name="Administrador", description="Acceso total al sistema")
            db.add(admin_role)
            await db.commit()
            await db.refresh(admin_role)
            
        # Check if project exists
        stmt = select(Project).where(Project.id == 1)
        result = await db.execute(stmt)
        project = result.scalar_one_or_none()
        if not project:
            project = Project(name="Proyecto Principal", description="Proyecto creado automáticamente")
            db.add(project)
            await db.commit()
            await db.refresh(project)
            
            # Seed default Kanban Columns for this project
            default_columns = [
                KanbanColumn(name="Por Hacer", order=1, project_id=project.id),
                KanbanColumn(name="En Progreso", order=2, project_id=project.id),
                KanbanColumn(name="Revisión", order=3, project_id=project.id),
                KanbanColumn(name="Hecho", order=4, project_id=project.id)
            ]
            db.add_all(default_columns)
            await db.commit()
            
        # Check if admin user exists
        stmt = select(User).where(User.username == "admin")
        result = await db.execute(stmt)
        admin_user = result.scalar_one_or_none()
        if not admin_user:
            hashed_pwd = get_password_hash("admin")
            admin_user = User(username="admin", email="admin@ganttscrum.local", hashed_password=hashed_pwd)
            admin_user.roles.append(admin_role)
            db.add(admin_user)
            await db.commit()
            
    # Start the backup scheduler background task
    asyncio.create_task(backup_scheduler(AsyncSessionLocal))

# Mount static files to serve the frontend (must be last)
import os
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
FRONTEND_DIR = os.path.join(BASE_DIR, "..", "frontend")

os.makedirs("uploads/logos", exist_ok=True)
app.mount("/uploads", StaticFiles(directory="uploads"), name="uploads")
app.mount("/", StaticFiles(directory=FRONTEND_DIR, html=True), name="frontend")
