// Task Team — API Server — 2026-03-29
require("dotenv").config();
const Fastify = require("fastify");
const cors = require("@fastify/cors");
const jwt = require("@fastify/jwt");
const rateLimit = require("@fastify/rate-limit");
const { Pool } = require("pg");
const Redis = require("ioredis");
const websocket = require("@fastify/websocket");
const swagger = require("@fastify/swagger");
const swaggerUi = require("@fastify/swagger-ui");

// Database pool
const pool = new Pool({
    connectionString: process.env.DATABASE_URL || "postgresql://taskteam:TaskTeam2026!@10.10.10.10:5432/taskteam"
});

// Redis client
const redis = new Redis(process.env.REDIS_URL || "redis://:Redis2026!@10.10.10.10:6379");

redis.on("connect", () => {
    console.log("Redis connected");
});
redis.on("error", (err) => {
    console.error("Redis error: " + err.message);
});

// Graceful shutdown
const shutdown = async (signal) => {
    console.log(`Received ${signal}, shutting down gracefully...`);
    await redis.quit();
    await pool.end();
    process.exit(0);
};
process.on("SIGINT", () => shutdown("SIGINT"));
process.on("SIGTERM", () => shutdown("SIGTERM"));

// Start
const start = async () => {
    const app = Fastify({ logger: true });

    // Decorate with db and redis
    app.decorate("db", pool);
    app.decorate("redis", redis);

    // Plugins (must await for Fastify 5 compatibility)
    await app.register(cors, { origin: true });
    await app.register(rateLimit, {
        max: 100,
        timeWindow: "1 minute",
        keyGenerator: (req) => {
            // Use user ID if authenticated, otherwise IP
            try {
                const token = req.headers.authorization?.split(" ")[1];
                if (token) {
                    const decoded = app.jwt.decode(token);
                    return `user:${decoded.id}`;
                }
            } catch {}
            return `ip:${req.ip}`;
        },
        errorResponseBuilder: () => ({ error: "Too many requests", statusCode: 429 })
    });
    await app.register(jwt, { secret: process.env.JWT_SECRET || "taskteam-jwt-secret-2026" });

    // Health check (excluded from rate limit)
    app.get("/health", { config: { rateLimit: false } }, async () => ({
        status: "ok",
        timestamp: new Date().toISOString(),
        pid: process.pid,
        redis: redis.status
    }));
    // Swagger/OpenAPI documentation
    await app.register(swagger, {
        openapi: {
            info: { title: "Task Team API", version: "1.0.0", description: "Personal life management system API" },
            servers: [{ url: "https://api.hasdo.info" }],
            tags: [
                { name: "Auth", description: "Authentication & OAuth" },
                { name: "Tasks", description: "Task management" },
                { name: "Groups", description: "Task groups" },
                { name: "Goals", description: "Goals & AI planner" },
                { name: "Projects", description: "Project management" },
                { name: "Collaboration", description: "Team collaboration" },
                { name: "Connectors", description: "External integrations" },
                { name: "Notifications", description: "Push notifications" },
                { name: "System", description: "Health & monitoring" }
            ]
        }
    });

    await app.register(swaggerUi, {
        routePrefix: "/docs",
        uiConfig: { docExpansion: "list", deepLinking: true }
    });

    // WebSocket support
    await app.register(websocket);

    app.get("/ws", { websocket: true }, (socket, req) => {
        socket.on("message", (msg) => {
            socket.send(JSON.stringify({ type: "ack", message: msg.toString() }));
        });
        socket.send(JSON.stringify({ type: "connected", timestamp: new Date().toISOString() }));
    });

    // Register routes
    await app.register(require("./routes/tasks"), { prefix: "/api/v1" });
    await app.register(require("./routes/groups"), { prefix: "/api/v1" });
    await app.register(require("./routes/auth"), { prefix: "/api/v1" });
    await app.register(require("./routes/oauth"), { prefix: "/api/v1" });
    await app.register(require("./routes/connectors"), { prefix: "/api/v1" });
    await app.register(require("./routes/connectors/odoo"), { prefix: "/api/v1" });
    await app.register(require("./routes/connectors/moodle"), { prefix: "/api/v1" });
    await app.register(require("./routes/connectors/pohoda"), { prefix: "/api/v1" });
    await app.register(require("./routes/chat"), { prefix: "/api/v1" });
    await app.register(require("./routes/notifications"), { prefix: "/api/v1" });
    await app.register(require("./routes/goals"), { prefix: "/api/v1" });
    await app.register(require("./routes/projects"), { prefix: "/api/v1" });
    await app.register(require("./routes/deploy"), { prefix: "/api/v1" });
    await app.register(require("./routes/system"), { prefix: "/api/v1" });
    await app.register(require("./routes/collaboration"), { prefix: "/api/v1" });
    await app.register(require("./routes/email"), { prefix: "/api/v1" });
    await app.register(require("./routes/errors"), { prefix: "/api/v1" });
    await app.register(require("./routes/invitations"), { prefix: "/api/v1" });
    await app.register(require("./routes/notification-prefs"), { prefix: "/api/v1" });
    await app.register(require("./routes/odoo-modules"), { prefix: "/api/v1" });
    await app.register(require("./routes/spaced-repetition"), { prefix: "/api/v1" });
    await app.register(require("./routes/admin"), { prefix: "/api/v1" });
    await app.register(require("./routes/activity"), { prefix: "/api/v1" });
    await app.register(require("./routes/families"), { prefix: "/api/v1" });

    // Register features (modular)
    const { registerFeatures } = require("./features/registry");
    await registerFeatures(app);
    try {
        await app.listen({ port: process.env.PORT || 3000, host: "0.0.0.0" });
        console.log("Task Team API listening on port " + (process.env.PORT || 3000) + " (pid: " + process.pid + ")");
    } catch (err) {
        console.error(err);
        process.exit(1);
    }
};
start();
