import type { Express } from "express";
import { createServer, type Server } from "http";
import { storage } from "./storage.js";
import { insertLabelSchema, insertUserSchema, insertTaskSchema, insertNoteSchema, insertGoalSchema, insertRewardSchema, insertUserRewardSchema, User, Task } from "../shared/schema.js";
import { z } from "zod";
import { EmailService } from "./email.js";
import { mcpServer } from "./mcp";
import { AiService, DEFAULT_SYSTEM_PROMPT } from "./ai.js";
import { RecurrenceService } from "./services/recurrence.js";
import { GamificationService } from "./gamification.js";
import { getVapidPublicKey, savePushSubscription, removePushSubscription, removeUserSubscriptions, sendPushNotification } from "./push.js";

const emailService = new EmailService(storage);
const aiService = new AiService(storage);
const recurrenceService = new RecurrenceService(storage);
const gamificationService = new GamificationService(storage);

import { setupAuth, hashPassword, comparePassword } from "./auth.js";
import { apiKeyAuth } from "./api-key-auth.js";

function isAdmin(req: any, res: any, next: any) {
  if (req.isAuthenticated() && req.user.role === 'admin') {
    return next();
  }
  res.status(403).json({ error: "Unauthorized: Admin access required" });
}

export async function registerRoutes(app: Express): Promise<Server> {
  setupAuth(app);

  // API Key auth middleware - must be after session setup
  // Allows external tools to authenticate via X-API-Key header
  app.use("/api", apiKeyAuth());

  // Update user schedule
  app.patch("/api/user/schedule", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    try {
      const { start, end, days, availability } = req.body;

      const updates: Partial<User> = {};

      // Backward compatibility / Simple Mode
      if (start && end && days) {
        updates.workHours = { start, end, days };
        // Sync to availability.work if availability not explicitly provided?
        if (!availability) {
          updates.availability = {
            work: { start, end, days },
            personal: (req.user as User).availability?.personal || { start: "18:00", end: "22:00", days: [1, 2, 3, 4, 5, 0, 6] }
          };
        }
      }

      // New Mode
      if (availability) {
        updates.availability = availability;
        // Sync workHours to availability.work for legacy support
        if (availability.work) {
          updates.workHours = availability.work;
        }
      }

      if (Object.keys(updates).length === 0) {
        return res.status(400).json({ error: "No schedule data provided" });
      }

      const updated = await storage.updateUser(userId, updates);

      await storage.createAuditLog({
        userId,
        action: "UPDATE",
        entityType: "USER",
        entityId: userId,
        details: { action: "UPDATE_SCHEDULE", updates },
        source: "USER"
      });

      res.json(updated);
    } catch (e) {
      res.status(500).json({ error: "Failed to update schedule" });
    }
  });

  // --- Setup Routes ---
  app.get("/api/setup/status", async (req, res) => {
    const hasAdmin = await storage.hasAdminUser();
    res.json({ isSetup: hasAdmin });
  });

  // Debug endpoints removed (SEC-1) - were accessible without auth
  // Use admin settings panel or /api/admin/settings instead

  app.get("/api/settings/public", async (req, res) => {
    const regEnabled = await storage.getSystemSettings("registration_enabled");
    // Default to true if not set, or specifically check for "false"
    res.json({ registration_enabled: regEnabled !== "false" });
  });

  app.post("/api/setup", async (req, res) => {
    const hasAdmin = await storage.hasAdminUser();
    if (hasAdmin) {
      return res.status(403).json({ error: "Setup already completed" });
    }

    // Create Super Admin
    try {
      const hashedPassword = await hashPassword(req.body.password);
      const adminUser = await storage.createUser({
        username: req.body.username,
        email: req.body.email,
        password: hashedPassword,
        role: 'admin',
        isActive: true
      });

      // Auto-enable registration by default on setup
      await storage.setSystemSettings("registration_enabled", "true");

      req.login(adminUser, (err) => {
        if (err) return res.status(500).json({ error: "Login failed after setup" });
        req.session.save(() => {
          return res.json(adminUser);
        });
      });
    } catch (e) {
      res.status(500).json({ error: "Failed to create admin user" });
    }
  });

  // Check if AI is configured (for UI empty states)
  app.get("/api/ai/status", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const key = await storage.getSystemSettings("ai_api_key");
    const provider = await storage.getSystemSettings("ai_provider");
    // Consider configured if we have a key OR if provider is 'ollama'
    const isConfigured = (provider === 'ollama') || (!!key && key.length > 0);
    res.json({ configured: isConfigured, provider });
  });

  // --- Password Reset Routes ---
  app.post("/api/auth/forgot-password", async (req, res) => {
    try {
      const { email } = req.body;
      if (!email) return res.status(400).json({ error: "Email required" });

      const user = await storage.getUserByEmail(email);
      if (!user) {
        // Check security best practices: delay response or return success to avoid enumeration?
        // For now, let's behave nicely.
        return res.json({ message: "If an account exists, a reset email has been sent." });
      }

      const tokenString = crypto.randomUUID();
      // Expires in 1 hour
      const expiresAt = new Date(Date.now() + 60 * 60 * 1000);

      await storage.createPasswordResetToken({
        userId: user.id,
        token: tokenString,
        expiresAt,
        isUsed: false
      });

      await emailService.sendPasswordResetEmail(user, tokenString);
      res.json({ message: "If an account exists, a reset email has been sent." });
    } catch (e) {
      console.error("Forgot Password Error:", e);
      res.status(500).json({ error: "Server error" });
    }
  });

  app.post("/api/auth/reset-password", async (req, res) => {
    try {
      const { token, newPassword } = req.body;
      if (!token || !newPassword) return res.status(400).json({ error: "Token and password required" });

      const resetToken = await storage.getPasswordResetToken(token);
      if (!resetToken) {
        return res.status(400).json({ error: "Invalid or expired token" });
      }

      if (resetToken.isUsed) {
        return res.status(400).json({ error: "Token already used" });
      }

      if (new Date() > new Date(resetToken.expiresAt)) {
        return res.status(400).json({ error: "Token expired" });
      }

      // Update User Password
      const hashedPassword = await hashPassword(newPassword);
      await storage.updateUser(resetToken.userId, { password: hashedPassword });

      // Mark token used
      await storage.markPasswordResetTokenUsed(resetToken.id);

      res.json({ message: "Password reset successfully. You can now login." });
    } catch (e) {
      console.error("Reset Password Error:", e);
      res.status(500).json({ error: "Server error" });
    }
  });

  // --- Admin Routes ---
  app.get("/api/admin/users", isAdmin, async (req, res) => {
    try {
      const users = await storage.getAllUsers();
      res.json(users);
    } catch (e) {
      res.status(500).json({ error: "Failed to fetch users" });
    }
  });

  app.post("/api/admin/users", isAdmin, async (req, res) => {
    try {
      const hashedPassword = await hashPassword(req.body.password);
      const newUser = await storage.createUser({
        username: req.body.username,
        email: req.body.email,
        password: hashedPassword,
        role: req.body.role || 'user',
        isActive: true
      });

      await storage.createAuditLog({
        userId: (req.user as User).id,
        action: "CREATE",
        entityType: "USER",
        entityId: newUser.id,
        details: { username: newUser.username, role: newUser.role },
        source: "USER"
      });

      res.json(newUser);
    } catch (e) {
      res.status(500).json({ error: "Failed to create user" });
    }
  });

  app.post("/api/admin/users/:id/toggle-active", isAdmin, async (req, res) => {
    try {
      const user = await storage.getUser(req.params.id);
      if (!user) return res.status(404).json({ error: "User not found" });

      if (user.role === 'admin' && user.id === (req.user as User).id) {
        return res.status(400).json({ error: "Cannot deactivate yourself" });
      }

      const updated = await storage.updateUser(user.id, { isActive: !user.isActive });

      await storage.createAuditLog({
        userId: (req.user as User).id,
        action: "UPDATE",
        entityType: "USER",
        entityId: user.id,
        details: { isActive: !user.isActive },
        source: "USER"
      });

      res.json(updated);
    } catch (e) {
      res.status(500).json({ error: "Failed to toggle user status" });
    }
  });

  app.delete("/api/admin/users/:id", isAdmin, async (req, res) => {
    try {
      const user = await storage.getUser(req.params.id);
      if (!user) return res.status(404).json({ error: "User not found" });

      if (user.role === 'admin' && user.id === (req.user as User).id) {
        return res.status(400).json({ error: "Cannot delete yourself" });
      }

      await storage.deleteUser(user.id);

      await storage.createAuditLog({
        userId: (req.user as User).id,
        action: "DELETE",
        entityType: "USER",
        entityId: user.id,
        details: { username: user.username },
        source: "USER"
      });

      res.sendStatus(204);
    } catch (e) {
      res.status(500).json({ error: "Failed to delete user" });
    }
  });

  app.post("/api/admin/users/:id/reset-xp", isAdmin, async (req, res) => {
    try {
      const user = await storage.getUser(req.params.id);
      if (!user) return res.status(404).json({ error: "User not found" });

      const updated = await storage.updateUser(user.id, { xp: 0, level: 1 });

      await storage.createAuditLog({
        userId: (req.user as User).id,
        action: "UPDATE",
        entityType: "USER",
        entityId: user.id,
        details: { action: "RESET_XP", previousXp: user.xp, previousLevel: user.level },
        source: "ADMIN"
      });

      res.json(updated);
    } catch (e) {
      res.status(500).json({ error: "Failed to reset user XP" });
    }
  });

  // --- MCP Routes ---
  app.get("/api/mcp/sse", async (req, res) => {
    const enabled = await storage.getSystemSettings("mcp_enabled");
    if (enabled !== "true") return res.status(503).send("MCP Server Disabled");
    await mcpServer.handleSse(req, res);
  });

  app.post("/api/mcp/messages", async (req, res) => {
    const enabled = await storage.getSystemSettings("mcp_enabled");
    if (enabled !== "true") return res.status(503).json({ error: "MCP Server Disabled" });
    await mcpServer.handleMessage(req, res);
  });

  app.get("/api/admin/audit-logs", isAdmin, async (req, res) => {
    try {
      const logs = await storage.getAuditLogs();
      res.json(logs);
    } catch (e) {
      res.status(500).json({ error: "Failed to fetch audit logs" });
    }
  });

  app.get("/api/admin/settings", isAdmin, async (req, res) => {
    const keys = [
      "registration_enabled",
      "smtp_host", "smtp_port", "smtp_user", "smtp_pass", "smtp_from", "smtp_secure",
      "ai_provider", "ai_api_key", "ai_model", "ai_base_url",
      "mcp_enabled", "mcp_port"
    ];
    const settings: any = {};
    for (const key of keys) {
      const val = await storage.getSystemSettings(key);
      if (key === "registration_enabled" || key === "smtp_secure" || key === "mcp_enabled") {
        settings[key] = val === "true";
      } else {
        settings[key] = val || ""; // Return empty string if undefined for inputs
      }
    }

    // Inject System Prompt with Default Fallback
    const storedPrompt = await storage.getSystemSettings("ai_system_prompt");
    settings["ai_system_prompt"] = storedPrompt || DEFAULT_SYSTEM_PROMPT;

    res.json(settings);
  });

  app.post("/api/admin/settings", isAdmin, async (req, res) => {
    const updates = req.body;
    const currentPrompt = await storage.getSystemSettings("ai_system_prompt");

    // Check for System Prompt changes & Log
    if (updates.ai_system_prompt && updates.ai_system_prompt !== currentPrompt) {
      await storage.createAuditLog({
        userId: (req.user as User).id,
        action: "UPDATE",
        entityType: "SYSTEM_SETTINGS",
        entityId: "ai_system_prompt",
        details: { message: "AI System Prompt updated" },
        source: "ADMIN"
      });
    }

    const keys = [
      "registration_enabled",
      "smtp_host", "smtp_port", "smtp_user", "smtp_pass", "smtp_from", "smtp_secure",
      "ai_provider", "ai_api_key", "ai_model", "ai_base_url", "ai_system_prompt",
      "mcp_enabled", "mcp_port"
    ];

    for (const key of keys) {
      if (req.body[key] !== undefined) {
        await storage.setSystemSettings(key, String(req.body[key]));
      }
    }

    await storage.createAuditLog({
      userId: (req.user as User).id,
      action: "UPDATE",
      entityType: "SYSTEM_SETTINGS",
      entityId: null,
      details: req.body,
      source: "USER"
    });

    res.json({ success: true });
  });


  app.post("/api/admin/ollama/tags", isAdmin, async (req, res) => {
    const { baseUrl } = req.body;
    // Default to localhost:11434 if not provided, or stored setting?
    // Client should send the value from the input field.
    const url = (baseUrl || "http://localhost:11434").replace(/\/$/, "") + "/api/tags";
    try {
      const resp = await fetch(url);
      if (!resp.ok) throw new Error(`Ollama Error: ${resp.statusText}`);
      const data = await resp.json();
      res.json(data);
    } catch (e: any) {
      console.error("Ollama Tags Error:", e);
      res.status(500).json({ error: "Failed to fetch Ollama tags: " + e.message });
    }
  });

  app.post("/api/admin/ollama/pull", isAdmin, async (req, res) => {
    const { baseUrl, model } = req.body;
    if (!model) return res.status(400).json({ error: "Model name required" });

    const url = (baseUrl || "http://localhost:11434").replace(/\/$/, "") + "/api/pull";
    console.log(`Pulling Ollama model ${model} from ${url}...`);

    try {
      // connecting to ollama
      const resp = await fetch(url, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ name: model, stream: false }),
      });

      if (!resp.ok) {
        const errText = await resp.text();
        throw new Error(`Ollama Pull Error: ${errText}`);
      }

      const data = await resp.json();
      res.json(data);
    } catch (e: any) {
      console.error("Ollama Pull Error:", e);
      res.status(500).json({ error: "Failed to pull model: " + e.message });
    }
  });

  // --- AI Chat History Routes ---

  // Get all conversations for user
  app.get("/api/ai/conversations", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const conversations = await storage.getConversations((req.user as User).id);
      res.json(conversations);
    } catch (e) {
      res.status(500).json({ error: "Failed to fetch conversations" });
    }
  });

  // Create new conversation
  app.post("/api/ai/conversations", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const user = req.user as User;
    try {
      const { title } = req.body;
      const conversation = await storage.createConversation(user.id, title);

      await storage.createAuditLog({
        userId: user.id,
        action: "CREATE",
        entityType: "CONVERSATION",
        entityId: conversation.id,
        details: { title },
        source: "USER"
      });

      res.json(conversation);
    } catch (e) {
      res.status(500).json({ error: "Failed to create conversation" });
    }
  });

  // Rename conversation
  app.patch("/api/ai/conversations/:id", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const user = req.user as User;
    try {
      const { title } = req.body;
      if (!title) return res.status(400).json({ error: "Title is required" });

      const conv = await storage.getConversation(req.params.id);
      if (!conv) return res.status(404).json({ error: "Conversation not found" });
      if (conv.userId !== user.id) return res.sendStatus(403);

      const updated = await storage.updateConversation(req.params.id, title);

      await storage.createAuditLog({
        userId: user.id,
        action: "UPDATE",
        entityType: "CONVERSATION",
        entityId: req.params.id,
        details: { title },
        source: "USER"
      });

      res.json(updated);
    } catch (e) {
      res.status(500).json({ error: "Failed to update conversation" });
    }
  });

  // Purchase Reward
  app.post("/api/rewards/:rewardId/purchase", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    const rewardId = req.params.rewardId;

    try {
      const reward = await storage.getReward(rewardId);
      if (!reward) return res.status(404).json({ error: "Reward not found" });

      const purchase = await storage.purchaseReward(userId, reward.id, reward.cost);

      await storage.createAuditLog({
        userId: userId,
        action: "PURCHASE",
        entityType: "REWARD",
        entityId: reward.id,
        details: { name: reward.title, cost: reward.cost },
        source: "USER"
      });

      res.json(purchase);
    } catch (e) {
      res.status(500).json({ error: "Purchase failed" });
    }
  });

  // Get single conversation
  app.get("/api/ai/conversations/:id", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const conv = await storage.getConversation(req.params.id);
      if (!conv) return res.status(404).json({ error: "Conversation not found" });
      if (conv.userId !== (req.user as User).id) return res.sendStatus(403);
      res.json(conv);
    } catch (e) {
      res.status(500).json({ error: "Failed to fetch conversation" });
    }
  });

  // Delete conversation
  app.delete("/api/ai/conversations/:id", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const conv = await storage.getConversation(req.params.id);
      if (!conv) return res.status(404).json({ error: "Conversation not found" });
      if (conv.userId !== (req.user as User).id) return res.sendStatus(403);

      await storage.deleteConversation(req.params.id);

      await storage.createAuditLog({
        userId: (req.user as User).id,
        action: "DELETE",
        entityType: "CONVERSATION",
        entityId: req.params.id,
        details: null,
        source: "USER"
      });

      res.sendStatus(204);
    } catch (e) {
      res.status(500).json({ error: "Failed to delete conversation" });
    }
  });

  // Get messages for conversation
  app.get("/api/ai/conversations/:id/messages", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const conv = await storage.getConversation(req.params.id);
      if (!conv) return res.status(404).json({ error: "Conversation not found" });
      if (conv.userId !== (req.user as User).id) return res.sendStatus(403);

      const messages = await storage.getMessages(req.params.id);
      res.json(messages);
    } catch (e) {
      res.status(500).json({ error: "Failed to fetch messages" });
    }
  });

  // Generate conversation title
  app.post("/api/ai/generate-title", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const user = req.user as User;
    if (!user.aiEnabled) return res.status(403).json({ error: "AI Assistant is disabled for this user" });

    try {
      const { messages } = req.body;
      if (!messages || !Array.isArray(messages)) return res.status(400).json({ error: "Messages array is required" });

      const title = await aiService.generateTitle(messages);
      res.json({ title });
    } catch (e: any) {
      console.error("Generate Title Error:", e);
      res.status(500).json({ error: e.message });
    }
  });

  // Send message (Chat)
  app.post("/api/ai/chat", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const user = req.user as User;
    if (!user.aiEnabled) return res.status(403).json({ error: "AI Assistant is disabled for this user" });

    try {
      const { conversationId, content, clientTime } = req.body;
      if (!conversationId || !content) return res.status(400).json({ error: "Missing conversationId or content" });

      // Verify ownership
      const conv = await storage.getConversation(conversationId);
      if (!conv) return res.status(404).json({ error: "Conversation not found" });
      if (conv.userId !== user.id) return res.sendStatus(403);

      // Store User Message
      const userMessage = await storage.addMessage({
        conversationId,
        role: 'user',
        content
      });

      // Send immediate response to client (Session Aware: request completes)
      res.json(userMessage);

      // Background Processing (Fire & Forget)
      (async () => {
        try {
          // Fetch history for context
          const dbMessages = await storage.getMessages(conversationId);
          // Convert to format expected by AiService (role, content)
          const history = dbMessages.map(m => ({ role: m.role as 'user' | 'assistant' | 'system', content: m.content }));

          // Build User Context (Tasks etc)
          const tasks = await storage.getTasksForUser(user.id);
          const activeTasks = tasks.filter(t => t.status !== 'done');
          const completedTasks = tasks.filter(t => t.status === 'done');

          const currentClientTime = clientTime || new Date().toLocaleString();

          const context = `
User Context:
- User ID: ${user.id}
- Username: ${user.username}
- XP: ${user.xp} (Level ${user.level})
- Current Date/Time: ${currentClientTime}

Task Summary:
- Total Active Tasks: ${activeTasks.length}
- Total Completed Tasks: ${completedTasks.length}

High Priority Active Tasks:
${activeTasks.filter(t => t.priority === 'high').map(t => `- ${t.title} (Due: ${t.dueDate})`).join('\n') || 'None'}

Recent Active Tasks:
${activeTasks.slice(0, 5).map(t => `- [${t.priority}] ${t.title}`).join('\n')}
`;

          // Call AI Service
          const responseContent = await aiService.chat(history, user, context);

          // Store AI Response
          await storage.addMessage({
            conversationId,
            role: 'assistant',
            content: responseContent
          });
        } catch (bgError) {
          console.error("Background AI Processing Error:", bgError);
          // Optional: Add a system message saying it failed?
        }
      })();

    } catch (e: any) {
      console.error("AI Route Error:", e);
      res.status(500).json({ error: e.message || "Failed to generate AI response" });
    }
  });

  // Analyze completed task for follow-up
  app.post("/api/ai/analyze-completion", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const user = req.user as User;
    if (!user.aiEnabled) return res.json({ needed: false }); // Silently fail/return false

    try {
      const { taskId } = req.body;
      const task = await storage.getTask(taskId);
      if (!task) return res.status(404).json({ error: "Task not found" });

      // Build context
      const context = `
User Context:
- User ID: ${user.id}
- Username: ${user.username}
- Current Date/Time: ${new Date().toLocaleString()}
`;

      const analysis = await aiService.analyzeTaskCompletion(task, context);
      res.json(analysis);
    } catch (e: any) {
      console.error("Analysis Error:", e);
      res.status(500).json({ error: "Failed to analyze task" });
    }
  });

  // Schedule a task using AI
  app.post("/api/ai/schedule", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const user = req.user as User;
    if (!user.aiEnabled) return res.status(403).json({ error: "AI Assistant is disabled for this user" });

    try {
      const { taskId } = req.body;
      if (!taskId) return res.status(400).json({ error: "Task ID is required" });

      const result = await aiService.scheduleTask(taskId, user.id);

      if (result.success && result.scheduledDate) {
        // Log it
        await storage.createAuditLog({
          userId: user.id,
          action: "UPDATE",
          entityType: "TASK",
          entityId: taskId,
          details: { action: "AUTO_SCHEDULE", date: result.scheduledDate },
          source: "AI"
        });
      }

      res.json(result);
    } catch (e: any) {
      console.error("Scheduling Error:", e);
      res.status(500).json({ error: e.message || "Failed to schedule task" });
    }
  });

  // Edit message and regenerate (Regenerate Response)
  app.put("/api/ai/chat/:messageId", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const user = req.user as User;
    if (!user.aiEnabled) return res.status(403).json({ error: "AI Assistant is disabled for this user" });

    try {
      const { messageId } = req.params;
      const { content, clientTime } = req.body;
      if (!content) return res.status(400).json({ error: "Content is required" });

      // Verify message and ownership
      const message = await storage.getMessage(messageId);
      if (!message) return res.status(404).json({ error: "Message not found" });

      const conv = await storage.getConversation(message.conversationId);
      if (!conv || conv.userId !== user.id) return res.sendStatus(403);
      if (message.role !== 'user') return res.status(400).json({ error: "Can only edit user messages" });

      // 1. Update the message content
      const updatedMessage = await storage.updateMessage(messageId, content);

      // 2. Delete all subsequent messages (history truncation)
      await storage.deleteMessagesAfter(message.conversationId, message.createdAt as Date, message.id);

      // 3. Prepare context for regeneration
      const dbMessages = await storage.getMessages(message.conversationId);
      const history = dbMessages.map(m => ({ role: m.role as 'user' | 'assistant' | 'system', content: m.content }));

      const tasks = await storage.searchTasks("", user.id); // Get all tasks
      const activeTasks = tasks.filter(t => t.status !== 'done');
      const completedTasks = tasks.filter(t => t.status === 'done');

      // Fetch all labels to map IDs to names
      const labels = await storage.getAllLabels();
      const labelMap = new Map(labels.map(l => [l.id, l.name]));

      const activeTasksWithLabels = activeTasks.map(t => ({
        ...t,
        label: t.labelId ? labelMap.get(t.labelId) || "No Label" : "No Label"
      }));

      const context = `
User Context:
- User ID: ${user.id}
- Username: ${user.username}
- XP: ${user.xp} (Level ${user.level})
- Current Date/Time: ${clientTime || new Date().toLocaleString()}

Task Summary:
- Total Active Tasks: ${activeTasks.length}
- Total Completed Tasks: ${completedTasks.length}

High Priority Active Tasks:
${activeTasksWithLabels.filter(t => t.priority === 'high').map(t => `- [${t.label}] ${t.title} (Due: ${t.dueDate})`).join('\n') || 'None'}

Recent Active Tasks:
${activeTasksWithLabels.slice(0, 5).map(t => `- [${t.priority}] [${t.label}] ${t.title}`).join('\n')}
`;

      // 4. Call AI Service (Regenerate)
      const responseContent = await aiService.chat(history, user, context);

      // 5. Store AI Response
      const botMessage = await storage.addMessage({
        conversationId: message.conversationId,
        role: 'assistant',
        content: responseContent
      });

      res.json(botMessage);
    } catch (e: any) {
      console.error("AI Edit Error:", e);
      res.status(500).json({ error: e.message || "Failed to regenerate AI response" });
    }
  });

  // --- API Key Management Routes ---
  
  // Get current API key (masked)
  app.get("/api/user/api-key", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const user = req.user as User;
    if (user.apiKey) {
      // Show first 8 and last 4 chars
      const masked = user.apiKey.substring(0, 8) + "..." + user.apiKey.substring(user.apiKey.length - 4);
      res.json({ hasKey: true, maskedKey: masked });
    } else {
      res.json({ hasKey: false, maskedKey: null });
    }
  });

  // Generate new API key
  app.post("/api/user/api-key", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const user = req.user as User;
    const crypto = await import("crypto");
    const newKey = "tf_" + crypto.randomBytes(32).toString("hex");
    
    await storage.updateUserApiKey(user.id, newKey);

    await storage.createAuditLog({
      userId: user.id,
      action: "CREATE",
      entityType: "API_KEY",
      entityId: user.id,
      details: { message: "API key generated" },
      source: "USER"
    });

    // Return full key ONCE (user must save it)
    res.json({ apiKey: newKey, message: "Save this key - it won't be shown again in full." });
  });

  // Revoke API key
  app.delete("/api/user/api-key", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const user = req.user as User;
    
    await storage.updateUserApiKey(user.id, null);

    await storage.createAuditLog({
      userId: user.id,
      action: "DELETE",
      entityType: "API_KEY",
      entityId: user.id,
      details: { message: "API key revoked" },
      source: "USER"
    });

    res.json({ message: "API key revoked" });
  });

  // Health check endpoint
  app.get("/api/health", (req, res) => {
    res.status(200).json({ status: "ok", timestamp: new Date().toISOString() });
  });

  // Labels API routes
  app.get("/api/labels", async (req, res) => {
    try {
      if (!req.isAuthenticated()) return res.sendStatus(401);
      const userId = (req.user as User).id;
      const allLabels = await storage.getAllLabels();
      const sharedLabels = await storage.getSharedLabels(userId);
      const sharedLabelIds = new Set(sharedLabels.map(sl => sl.labelId));

      // Filter: Created by me OR Shared with me
      // If creatorId is null (legacy/default), everyone sees it? Or system public?
      // Assumption: labels with creatorId=null are "System Defaults" visible to all.
      // Or we should update getAllLabels to filter in DB.

      const visibleLabels = allLabels.filter(l =>
        l.creatorId === userId ||
        sharedLabelIds.has(l.id) ||
        l.creatorId === null
      );

      res.json(visibleLabels);
    } catch (error) {
      res.status(500).json({ error: "Failed to fetch labels" });
    }
  });

  app.get("/api/labels/:id", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const label = await storage.getLabel(req.params.id);
      if (!label) {
        return res.status(404).json({ error: "Label not found" });
      }
      res.json(label);
    } catch (error) {
      res.status(500).json({ error: "Failed to fetch label" });
    }
  });

  app.delete("/api/tasks/:id", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const { hasAccess, task } = await checkTaskAccess(req.user as User, req.params.id, 'write');
      if (!task) return res.status(404).json({ error: "Task not found" });
      // DELETE usually requires ownership or explicit 'write' permission.
      // Shared read-only users should NOT be able to delete.
      // checkTaskAccess('write') should cover this if we implement strict permissions in sharedTasks later.
      // For now, let's assume if they have 'write', they can delete (or we restrict delete to Owner).
      // Let's restrict DELETE to Owner for safety unless specifically allowed.

      if (task.userId !== (req.user as User).id) {
        return res.status(403).json({ error: "Only the owner can delete a task" });
      }

      await storage.deleteTask(req.params.id);
      res.sendStatus(204);
    } catch (error) {
      res.status(500).json({ error: "Failed to delete task" });
    }
  });

  app.post("/api/labels", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const result = insertLabelSchema.safeParse(req.body);
      if (!result.success) {
        return res.status(400).json({ error: "Invalid label data", details: result.error });
      }

      const label = await storage.createLabel({
        ...result.data,
        creatorId: (req.user as User).id // Assign creator
      });

      await storage.createAuditLog({
        userId: (req.user as User).id,
        action: "CREATE",
        entityType: "LABEL",
        entityId: label.id,
        details: { name: label.name },
        source: "USER"
      });

      res.status(201).json(label);
    } catch (error) {
      console.error("Create Label Error:", error);
      res.status(500).json({ error: "Failed to create label" });
    }
  });



  app.patch("/api/labels/:id", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const updates = insertLabelSchema.partial().safeParse(req.body);
      if (!updates.success) {
        return res.status(400).json({ error: "Invalid label data", details: updates.error });
      }

      const label = await storage.updateLabel(req.params.id, updates.data);
      if (!label) {
        return res.status(404).json({ error: "Label not found" });
      }

      await storage.createAuditLog({
        userId: (req.user as User).id,
        action: "UPDATE",
        entityType: "LABEL",
        entityId: label.id,
        details: updates.data,
        source: "USER"
      });

      res.json(label);
    } catch (error) {
      res.status(500).json({ error: "Failed to update label" });
    }
  });

  app.delete("/api/labels/:id", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const success = await storage.deleteLabel(req.params.id);
      if (!success) {
        return res.status(404).json({ error: "Label not found" });
      }
      // BUG FIX: removed duplicate deleteLabel() call (DUP-2)

      await storage.createAuditLog({
        userId: (req.user as User).id,
        action: "DELETE",
        entityType: "LABEL",
        entityId: req.params.id,
        details: null,
        source: "USER"
      });

      res.status(204).send();
    } catch (error) {
      res.status(500).json({ error: "Failed to delete label" });
    }
  });

  // Shared Label Routes
  app.get("/api/labels/:id/share", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const label = await storage.getLabel(req.params.id);
      if (!label) return res.status(404).json({ error: "Label not found" });

      // Only creator or admin or write permission can view shares?
      // Actually creator, or anyone with 'write'/'admin' permission on the label?
      // For simplicity: Only creator can manage shares.
      if (label.creatorId && label.creatorId !== (req.user as User).id) {
        return res.status(403).json({ error: "Only the label owner can manage shares" });
      }

      // const shares = await storage.getLabelShares(req.params.id); // Not needed as we fetch below

      // We need user details for the frontend
      const users = await storage.getLabelSharedUsers(req.params.id);
      const sharesWithDetails = await Promise.all(users.map(async u => {
        const shareInfos = await storage.getLabelShares(req.params.id);
        const specificShare = shareInfos.find(s => s.sharedWithUserId === u.id);
        return {
          userId: u.id,
          username: u.username,
          permission: specificShare?.permission || 'read'
        };
      }));

      res.json(sharesWithDetails);
    } catch (e) {
      res.status(500).json({ error: "Failed to fetch label shares" });
    }
  });

  app.post("/api/labels/:id/share", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const { username, permission } = req.body;
      if (!username) return res.status(400).json({ error: "Username required" });

      const label = await storage.getLabel(req.params.id);
      if (!label) return res.status(404).json({ error: "Label not found" });

      if (label.creatorId && label.creatorId !== (req.user as User).id) {
        return res.status(403).json({ error: "Only owner can share label" });
      }

      const targetUser = await storage.getUserByUsername(username);
      if (!targetUser) return res.status(404).json({ error: "User not found" });
      if (targetUser.id === (req.user as User).id) return res.status(400).json({ error: "Cannot share with yourself" });

      const share = await storage.shareLabel(label.id, targetUser.id, (req.user as User).id, permission || 'read');
      res.json(share);
    } catch (e) {
      res.status(500).json({ error: "Failed to share label" });
    }
  });

  app.delete("/api/labels/:id/share/:userId", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const label = await storage.getLabel(req.params.id);
      if (!label) return res.status(404).json({ error: "Label not found" });

      if (label.creatorId && label.creatorId !== (req.user as User).id) {
        // Also allow user to unshare themselves?
        if (req.params.userId !== (req.user as User).id) {
          return res.status(403).json({ error: "Only owner can remove other collaborators" });
        }
      }

      await storage.unshareLabel(label.id, req.params.userId);
      res.sendStatus(204);
    } catch (e) {
      res.status(500).json({ error: "Failed to remove share" });
    }
  });

  // Tasks API routes (updated GET)
  app.get("/api/tasks", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const tasks = await storage.getTasksForUser((req.user as User).id);
      res.json(tasks);
    } catch (error) {
      res.status(500).json({ error: "Failed to fetch tasks" });
    }
  });

  // Search tasks - MUST be before /api/tasks/:id to avoid route conflict
  app.get("/api/tasks/search", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const query = (req.query.q as string) || "";
      const tasks = await storage.searchTasks(query, (req.user as User).id);
      res.json(tasks);
    } catch (error) {
      res.status(500).json({ error: "Failed to search tasks" });
    }
  });

  // Helper for RBAC
  const checkTaskAccess = async (user: User, taskId: string, requiredPermission: 'read' | 'write' = 'read'): Promise<{ hasAccess: boolean, task?: Task }> => {
    const task = await storage.getTask(taskId);
    if (!task) return { hasAccess: false };

    // 1. Ownership
    if (task.userId === user.id) return { hasAccess: true, task };

    // 2. Shared Task (Direct)
    // We need a storage method for this efficiently, but for now we might need to query
    // Since storage interface is generic, let's assume valid access if we can find a record
    // Optimization: Add storage.hasTaskAccess(userId, taskId)? 
    // For now, let's fallback to checking if the task is in the user's "visible" list or simple logic
    // Implementation Plan Step: "Shared Access (query sharedTasks table)"

    // We'll trust the current `storage.getTask` usually returns raw task. 
    // But we need to verify IF the user is allowed.

    // Check Shared Tasks
    const shared = await storage.getSharedTask(taskId, user.id);
    if (shared) {
      // Shared tasks currently imply 'read'. If we need 'write', we might need more fields.
      // For now, let's assume shared = read/write or just read.
      // The schema `sharedTasks` doesn't have permissions, so full access? 
      // Start with READ access for shared. WRITE might need schema update.
      // Let's assume shared tasks are R/W for now for simplicity unless specified.
      return { hasAccess: true, task };
    }

    // 3. Global Access (UserTaskAccess)
    // Check if user has access to the owner's tasks
    if (!task.userId) return { hasAccess: false, task }; // Should not happen for user tasks
    const hasGlobalAccess = await storage.checkUserTaskAccess(task.userId, user.id);
    if (hasGlobalAccess) return { hasAccess: true, task }; // "Share All"

    return { hasAccess: false, task }; // Task exists but no access
  };

  app.get("/api/tasks/:id", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const { hasAccess, task } = await checkTaskAccess(req.user as User, req.params.id, 'read');
      if (!task) return res.status(404).json({ error: "Task not found" });
      if (!hasAccess) return res.status(403).json({ error: "Access denied" });

      res.json(task);
    } catch (error) {
      res.status(500).json({ error: "Failed to fetch task" });
    }
  });

  app.post("/api/tasks/:id/schedule", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const taskId = req.params.id;
      const task = await storage.getTask(taskId);

      if (!task) return res.status(404).json({ error: "Task not found" });
      if (task.userId !== (req.user as User).id) return res.sendStatus(403);

      const result = await aiService.scheduleTask(taskId, (req.user as User).id);

      if (result.success) {
        res.json(result);
        // Award XP for using AI scheduling
        if (req.user) {
          await gamificationService.awardXP((req.user as User).id, 'ai_action');
        }

        await storage.createAuditLog({
          userId: (req.user as User).id,
          action: "UPDATE",
          entityType: "TASK",
          entityId: taskId,
          details: { action: "AI_SCHEDULE" },
          source: "AI"
        });
      } else {
        res.status(400).json(result);
      }
    } catch (e: any) {
      console.error("Schedule Task Error:", e);
      res.status(500).json({ error: e.message || "Failed to schedule task" });
    }
  });

  app.post("/api/tasks", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const result = insertTaskSchema.safeParse(req.body);
      if (!result.success) {
        return res.status(400).json({ error: "Invalid task data", details: result.error });
      }

      const task = await storage.createTask({
        ...result.data,
        userId: (req.user as User).id
      });
      // Award XP for creating a task
      if (req.user) {
        const source = req.body.parentTaskId ? 'create_subtask' : 'create_task';
        await gamificationService.awardXP((req.user as User).id, source, undefined, { taskId: task.id, taskTitle: task.title });
      }

      await storage.createAuditLog({
        userId: (req.user as User).id,
        action: "CREATE",
        entityType: "TASK",
        entityId: task.id,
        details: { title: task.title },
        source: "USER"
      });

      res.status(201).json(task);
    } catch (error) {
      res.status(500).json({ error: "Failed to create task" });
    }
  });

  app.patch("/api/tasks/:id", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      // 1. Check Access (Security Fix + Fetch)
      // We use checkTaskAccess to ensure the user has write permissions (ownership or shared write access)
      const { hasAccess, task: previousTask } = await checkTaskAccess(req.user as User, req.params.id, 'write');

      if (!previousTask) return res.status(404).json({ error: "Task not found" });
      if (!hasAccess) return res.status(403).json({ error: "Access denied" });

      const updates = insertTaskSchema.partial().safeParse(req.body);

      if (!updates.success) {
        return res.status(400).json({ error: "Invalid task data", details: updates.error });
      }

      // Award XP using GamificationService
      if (req.user) {
        if (updates.data.status === 'done' && previousTask.status !== 'done') {
          const isLate = previousTask.dueDate && new Date(previousTask.dueDate) < new Date();
          const source = isLate ? 'complete_task_late' : 'complete_task';
          await gamificationService.awardXP((req.user as User).id, source, undefined, { taskId: previousTask.id, taskTitle: previousTask.title });
        } else if (Object.keys(updates.data).length > 0) {
          // Small points for any other update (title, description, etc)
          await gamificationService.awardXP((req.user as User).id, 'update_task', undefined, { taskId: previousTask.id, taskTitle: previousTask.title });
        }
      }

      const updatedTask = await storage.updateTask(req.params.id, updates.data);
      if (!updatedTask) {
        return res.status(404).json({ error: "Task not found" });
      }

      // 2. Time Tracking Logic
      // If timeTracked has increased, log the delta
      if (updates.data.timeTracked && updatedTask.timeTracked > previousTask.timeTracked) {
        const delta = updatedTask.timeTracked - previousTask.timeTracked;
        await storage.logTaskTime({
          taskId: updatedTask.id,
          userId: (req.user as User).id,
          timeSpent: delta
        });
      }

      await storage.createAuditLog({
        userId: (req.user as User).id,
        action: "UPDATE",
        entityType: "TASK",
        entityId: updatedTask.id,
        details: updates.data,
        source: "USER"
      });

      res.json(updatedTask);
    } catch (error: any) {
      console.error("PATCH Task Error:", error);
      res.status(500).json({ error: "Failed to update task", details: String(error) });
    }
  });

  // Time Analytics Endpoint
  app.get("/api/analytics/time-distribution", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);

    const period = req.query.period as 'day' | 'week' | 'month' | 'year';
    if (!['day', 'week', 'month', 'year'].includes(period)) {
      return res.status(400).json({ error: "Invalid period. Must be day, week, month, or year." });
    }

    try {
      const distribution = await storage.getAnalyticsTimeDistribution((req.user as User).id, period);
      res.json(distribution);
    } catch (error) {
      console.error("Analytics Error:", error);
      res.status(500).json({ error: "Failed to fetch time distribution" });
    }
  });

  // BUG FIX: Removed duplicate DELETE /api/tasks/:id route (DUP-1)
  // The correct one with auth + ownership check is at line ~925

  // Notes API routes
  app.get("/api/notes", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      res.json([]); // Placeholder until storage implementation
    } catch (error) {
      res.status(500).json({ error: "Failed to fetch notes" });
    }
  });

  app.post("/api/notes", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const result = insertNoteSchema.safeParse(req.body);
      if (!result.success) {
        return res.status(400).json({ error: "Invalid note data", details: result.error });
      }
      res.status(201).json({ ...result.data, id: "placeholder" });
    } catch (error) {
      res.status(500).json({ error: "Failed to create note" });
    }
  });

  // Goals API
  app.get("/api/goals", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const goals = await storage.getGoals();
      res.json(goals);
    } catch (error) {
      res.status(500).json({ error: "Failed to fetch goals" });
    }
  });

  app.post("/api/goals", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const result = insertGoalSchema.safeParse(req.body);
      if (!result.success) {
        return res.status(400).json(result.error);
      }
      const goal = await storage.createGoal(result.data);
      console.log("Goal created:", goal);

      await storage.createAuditLog({
        userId: (req.user as User).id || null,
        action: "CREATE",
        entityType: "GOAL",
        entityId: goal.id,
        details: { title: goal.title },
        source: "USER"
      });

      res.json(goal);
    } catch (error) {
      console.error("Error in POST /api/goals:", error);
      res.status(500).json({ error: "Failed to create goal" });
    }
  });

  // Analytics API
  app.get("/api/analytics/weekly", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const data = await gamificationService.getWeeklyAnalytics((req.user as User).id);
      res.json(data);
    } catch (e) {
      res.status(500).json({ error: "Failed to fetch analytics" });
    }
  });

  app.get("/api/analytics/yearly", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const data = await gamificationService.getYearlyAnalytics((req.user as User).id);
      res.json(data);
    } catch (e) {
      res.status(500).json({ error: "Failed to fetch analytics" });
    }
  });

  app.get("/api/analytics/monthly", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const data = await gamificationService.getMonthlyAnalytics((req.user as User).id);
      res.json(data);
    } catch (e) {
      res.status(500).json({ error: "Failed to fetch analytics" });
    }
  });

  // Dead code removed (DEAD-1): calculateXP was unused, GamificationService handles XP

  // Rewards API
  app.get("/api/rewards", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const allRewards = await storage.getAllRewards();
      const userId = req.query.userId as string;

      // Filter rewards: System rewards OR User's own rewards
      const visibleRewards = allRewards.filter(r => r.isSystem || (userId && r.userId === userId));

      // If userId is provided, check ownership of visible rewards
      if (userId) {
        const userRewards = await storage.getUserRewards(userId);
        const ownedIds = new Set(userRewards.map(ur => ur.rewardId));
        return res.json(visibleRewards.map(r => ({ ...r, owned: ownedIds.has(r.id) })));
      }
      res.json(visibleRewards);
    } catch (error) {
      res.status(500).json({ error: "Failed to fetch rewards" });
    }
  });

  app.post("/api/rewards/purchase", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      // 1. Get User & Reward
      const userId = (req.user as User).id;
      const { rewardId } = req.body;

      if (!rewardId) return res.status(400).json({ error: "Missing rewardId" });
      const user = await storage.getUser(userId); // Fetch user here
      const allRewards = await storage.getAllRewards();
      const reward = allRewards.find(r => r.id === rewardId);

      if (!user || !reward) return res.status(404).json({ error: "User or Reward not found" });

      // 2. Check Ownership (if one-time)
      // For now, allow multiple purchases unless type is 'feature_unlock'
      if (reward.type === 'feature_unlock') {
        const userRewards = await storage.getUserRewards(userId);
        if (userRewards.some(ur => ur.rewardId === rewardId)) {
          return res.status(400).json({ error: "Reward already owned" });
        }
      }

      // 3. Check Funds
      if (user.xp < reward.cost) {
        return res.status(400).json({ error: "Insufficient XP" });
      }

      // 4. Transaction
      const updatedUser = await storage.updateUserXP(userId, -reward.cost);
      await storage.createUserReward({ userId, rewardId });

      await storage.createAuditLog({
        userId,
        action: "PURCHASE",
        entityType: "REWARD",
        entityId: rewardId.toString(),
        source: "USER",
        details: { cost: reward.cost, rewardName: reward.title }
      });

      res.json({ success: true, user: updatedUser });
    } catch (e) {
      res.status(500).json({ error: "Purchase failed" });
    }
  });

  app.post("/api/rewards", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const rewardData = {
        ...req.body,
        userId: (req.user as User).id,
        isSystem: (req.user as User).role === 'admin' && req.body.isSystem !== false,
      };

      const reward = await storage.createReward(rewardData);

      await storage.createAuditLog({
        userId: (req.user as User).id,
        action: "CREATE",
        entityType: "REWARD",
        entityId: reward.id.toString(),
        source: "ADMIN",
        details: { title: reward.title, cost: reward.cost }
      });

      res.json(reward);
    } catch (err) {
      res.status(500).json({ error: "Failed to create reward" });
    }
  });

  // User Gamification Endpoints
  app.get("/api/user/history", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const history = await storage.getXpEvents((req.user as User).id);
      res.json(history);
    } catch (e) {
      res.status(500).json({ error: "Failed to fetch history" });
    }
  });

  app.get("/api/user/inventory", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const userRewards = await storage.getUserRewards((req.user as User).id);
      // Join with rewards details
      const allRewards = await storage.getAllRewards();
      const inventory = userRewards.map(ur => {
        const reward = allRewards.find(r => r.id === ur.rewardId);
        return {
          ...ur,
          reward // Nested details
        };
      }).filter(item => item.reward); // Filter out any broken links
      res.json(inventory);
    } catch (e) {
      res.status(500).json({ error: "Failed to fetch inventory" });
    }
  });

  // --- Social & Leaderboard Routes ---

  app.get("/api/leaderboard", async (req, res) => {
    try {
      const users = await storage.getLeaderboard();
      // Return public info only
      const leaderboard = users.map(u => ({
        username: u.username,
        xp: u.xp,
        level: u.level,
        id: u.id // Needed? Maybe for linking profile
      }));
      res.json(leaderboard);
    } catch (e) {
      res.status(500).json({ error: "Failed to fetch leaderboard" });
    }
  });

  app.patch("/api/user/privacy", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const { showOnLeaderboard, isSearchable, aiEnabled, is2faEnabled, language } = req.body;
      const updates: any = {};
      if (showOnLeaderboard !== undefined) updates.showOnLeaderboard = showOnLeaderboard;
      if (isSearchable !== undefined) updates.isSearchable = isSearchable;
      if (aiEnabled !== undefined) updates.aiEnabled = aiEnabled;
      if (is2faEnabled !== undefined) updates.is2faEnabled = is2faEnabled;
      if (language !== undefined) updates.language = language;

      const updated = await storage.updateUser((req.user as User).id, updates);

      await storage.createAuditLog({
        userId: (req.user as User).id,
        action: "UPDATE",
        entityType: "USER_PRIVACY",
        entityId: (req.user as User).id.toString(),
        source: "USER",
        details: updates
      });

      res.json(updated);
    } catch (e) {
      res.status(500).json({ error: "Failed to update privacy settings" });
    }
  });

  app.patch("/api/user/profile", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const { email } = req.body;
      if (!email || !email.includes('@')) return res.status(400).json({ error: "Invalid email" });

      const existing = await storage.getUserByEmail(email);
      if (existing && existing.id !== (req.user as User).id) {
        return res.status(400).json({ error: "Email already taken" });
      }

      const updated = await storage.updateUser((req.user as User).id, { email });

      await storage.createAuditLog({
        userId: (req.user as User).id,
        action: "UPDATE",
        entityType: "USER_PROFILE",
        entityId: (req.user as User).id.toString(),
        source: "USER",
        details: { change: "email" }
      });

      res.json(updated);
    } catch (e) {
      res.status(500).json({ error: "Failed to update profile" });
    }
  });



  app.patch("/api/user/password", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const { currentPassword, newPassword } = req.body;
      if (!currentPassword || !newPassword) return res.status(400).json({ error: "Missing fields" });

      const user = await storage.getUser((req.user as User).id);
      if (!user) return res.status(404).json({ error: "User not found" });

      const isValid = await comparePassword(currentPassword, user.password);
      if (!isValid) return res.status(400).json({ error: "Incorrect current password" });

      const hashedPassword = await hashPassword(newPassword);
      await storage.updateUser(user.id, { password: hashedPassword });

      await storage.createAuditLog({
        userId: user.id,
        action: "UPDATE",
        entityType: "USER_PASSWORD",
        entityId: user.id.toString(),
        source: "USER",
        details: {}
      });

      res.json({ message: "Password updated" });
    } catch (e) {
      res.status(500).json({ error: "Failed to update password" });
    }
  });

  app.get("/api/users/search", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const query = req.query.q as string;
    try {
      const users = await storage.searchUsers(query);
      // Filter out self
      const others = users.filter(u => u.id !== (req.user as User).id);
      res.json(others.map(u => ({ id: u.id, username: u.username })));
    } catch (e) {
      res.status(500).json({ error: "Search failed" });
    }
  });

  app.post("/api/tasks/:id/share", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const { targetUserId } = req.body;
      const taskId = req.params.id;

      // Verify ownership
      const task = await storage.getTask(taskId);
      if (!task) return res.status(404).json({ error: "Task not found" });

      if (task.userId !== (req.user as User).id) {
        return res.status(403).json({ error: "Unauthorized" });
      }

      await storage.shareTask({
        taskId,
        sharedByUserId: (req.user as User).id,
        sharedWithUserId: targetUserId
      });

      await storage.createAuditLog({
        userId: (req.user as User).id,
        action: "SHARE",
        entityType: "TASK",
        entityId: taskId,
        source: "USER",
        details: { sharedWith: targetUserId }
      });
      res.json({ success: true });
    } catch (e) {
      res.status(500).json({ error: "Failed to share task" });
    }
  });

  app.get("/api/tasks/:id/shared-users", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const taskId = req.params.id;
      // Verify ownership or access? Ideally only owner can see who else sees it.
      const task = await storage.getTask(taskId);
      if (!task) return res.status(404).json({ error: "Task not found" });

      if (task.userId !== (req.user as User).id) {
        return res.status(403).json({ error: "Unauthorized" });
      }

      const users = await storage.getTaskSharedUsers(taskId);
      // Return minimal info
      res.json(users.map(u => ({ id: u.id, username: u.username })));
    } catch (e) {
      res.status(500).json({ error: "Failed to fetch shared users" });
    }
  });

  app.delete("/api/tasks/:id/share/:userId", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const taskId = req.params.id;
      const targetUserId = req.params.userId;

      // Verify ownership
      const task = await storage.getTask(taskId);
      if (!task) return res.status(404).json({ error: "Task not found" });

      if (task.userId !== (req.user as User).id) {
        return res.status(403).json({ error: "Unauthorized" });
      }

      const success = await storage.unshareTask(taskId, targetUserId);
      if (success) {
        await storage.createAuditLog({
          userId: (req.user as User).id,
          action: "UNSHARE",
          entityType: "TASK",
          entityId: taskId,
          source: "USER",
          details: { unsharedWith: targetUserId }
        });
        res.json({ success: true });
      } else {
        res.status(404).json({ error: "Share not found" });
      }
    } catch (e) {
      res.status(500).json({ error: "Failed to unshare task" });
    }
  });

  app.post("/api/users/share-all", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const { targetUserId } = req.body;
      await storage.shareAllTasks({
        ownerId: (req.user as User).id,
        viewerId: targetUserId
      });

      await storage.createAuditLog({
        userId: (req.user as User).id,
        action: "SHARE",
        entityType: "ALL_TASKS",
        entityId: "0",
        source: "USER",
        details: { sharedWith: targetUserId }
      });
      res.json({ success: true });
    } catch (e) {
      res.status(500).json({ error: "Failed to share all tasks" });
    }
  });


  const httpServer = createServer(app);


  // Storage needs to support Goal Update
  // Storage needs to support Goal Update
  app.patch("/api/goals/:id", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      // BUG FIX: was calling updateTask() instead of updateGoal() (DEAD-4)
      const updated = await storage.updateGoal(req.params.id, req.body);

      if (updated) {
        await storage.createAuditLog({
          userId: (req.user as User).id,
          action: "UPDATE",
          entityType: "GOAL",
          entityId: updated.id.toString(),
          source: "USER",
          details: req.body
        });
      }

    } catch (e) {
      res.status(500).json({ error: "Failed to update goal" });
    }
  });

  // Routine Completion Endpoint
  app.post("/api/user/routine/:type/complete", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const user = req.user as User;
    const type = req.params.type;

    try {
      if (type === 'morning') {
        await storage.updateUser(user.id, { lastMorningRoutine: new Date() });
        await gamificationService.awardXP(user.id, 'morning_routine', 20); // Bonus
      } else if (type === 'evening') {
        await storage.updateUser(user.id, { lastEveningRoutine: new Date() });
        await gamificationService.awardXP(user.id, 'evening_routine', 20); // Bonus
      } else {
        return res.status(400).json({ error: "Invalid routine type" });
      }

      await storage.createAuditLog({
        userId: user.id,
        action: "COMPLETE",
        entityType: "ROUTINE",
        entityId: type,
        source: "USER",
        details: { type }
      });

      res.json({ success: true });
    } catch (e) {
      res.status(500).json({ error: "Failed to complete routine" });
    }
  });

  // Export User Data
  app.post("/api/user/data-export", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const user = req.user as User;
      const { includeTasks, includeLabels, includeSettings } = req.body;

      const exportData: any = {
        exportedAt: new Date(),
        user: { username: user.username, email: user.email }
      };

      if (includeTasks) {
        exportData.tasks = await storage.getTasksForUser(user.id);
      }

      if (includeLabels) {
        exportData.labels = await storage.getLabels(user.id);
      }

      if (includeSettings && user.role === 'admin') {
        // Only admins can request system settings dump, though arguably they might mean "User Settings"
        // The prompt said "system settings". I will include system settings if they are admin.
        // But maybe they meant user preferences? The prompt specifically said "system settings".
        const allSettings: Record<string, string> = {};
        const keys = [
          "registration_enabled", "smtp_host", "smtp_port", "smtp_user", "smtp_from",
          "ai_provider", "ai_model", "ai_base_url", "ai_system_prompt", "mcp_enabled"
        ];
        for (const key of keys) {
          const val = await storage.getSystemSettings(key);
          if (val) allSettings[key] = val;
        }
        exportData.systemSettings = allSettings;
      }

      await storage.createAuditLog({
        userId: user.id,
        action: "EXPORT_DATA",
        entityType: "USER_DATA",
        entityId: user.id.toString(),
        source: "USER",
        details: { includeTasks, includeLabels, includeSettings }
      });

      res.setHeader('Content-Type', 'application/json');
      res.setHeader('Content-Disposition', `attachment; filename="taskflow_export_${user.username}.json"`);
      res.json(exportData);
    } catch (e) {
      console.error("Export Failed:", e);
      res.status(500).json({ error: "Failed to export data" });
    }
  });

  // ============================================
  // ADHD MODE ROUTES
  // ============================================

  // Update ADHD settings
  app.patch("/api/user/adhd-settings", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    try {
      const { adhdMode, adhdSettings } = req.body;
      const updated = await storage.updateUser(userId, {
        adhdMode,
        adhdSettings,
      });

      await storage.createAuditLog({
        userId,
        action: "UPDATE",
        entityType: "USER",
        entityId: userId,
        details: { action: "UPDATE_ADHD_SETTINGS", adhdMode },
        source: "USER"
      });

      res.json(updated);
    } catch (e) {
      console.error("Update ADHD settings failed:", e);
      res.status(500).json({ error: "Failed to update ADHD settings" });
    }
  });

  // Log a break
  app.post("/api/user/log-break", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    try {
      const { breakType, durationMinutes } = req.body;

      // Log the break
      await storage.createBreakLog({
        userId,
        breakType,
        durationMinutes: durationMinutes || 5,
      });

      // Update user's lastBreakAt
      await storage.updateUser(userId, {
        lastBreakAt: new Date(),
      });

      // Award XP for taking a break (micro-XP)
      // BUG FIX: Parameters were swapped (TYPE-2) - source and amount
      await gamificationService.awardXP(userId, 'break_taken', 10, { breakType });

      res.json({ success: true });
    } catch (e) {
      console.error("Log break failed:", e);
      res.status(500).json({ error: "Failed to log break" });
    }
  });

  // Get break stats
  app.get("/api/user/break-stats", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    try {
      const stats = await storage.getBreakStats(userId);
      res.json(stats);
    } catch (e) {
      res.status(500).json({ error: "Failed to get break stats" });
    }
  });

  // Log energy level
  app.post("/api/energy/log", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    try {
      const { energyLevel, notes } = req.body;

      await storage.createEnergyLog({
        userId,
        energyLevel,
        notes,
      });

      // Update user's current energy level
      await storage.updateUser(userId, {
        currentEnergyLevel: energyLevel,
        todayEnergyCheckedIn: true,
      });

      // Award XP for checking in
      // BUG FIX: Parameters were swapped (TYPE-2)
      await gamificationService.awardXP(userId, 'energy_checkin', 5, { energyLevel });

      res.json({ success: true });
    } catch (e) {
      console.error("Log energy failed:", e);
      res.status(500).json({ error: "Failed to log energy" });
    }
  });

  // Get energy history
  app.get("/api/energy/history", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    try {
      const history = await storage.getEnergyHistory(userId);
      res.json(history);
    } catch (e) {
      res.status(500).json({ error: "Failed to get energy history" });
    }
  });

  // Get energy-based task suggestions
  app.get("/api/tasks/energy-based", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    try {
      const user = req.user as User;
      const tasks = await storage.getTasksForUser(userId);
      const energyLevel = user.currentEnergyLevel || 'medium';

      // Filter tasks based on energy level
      let recommended = tasks.filter(t => t.status !== 'done');

      if (energyLevel === 'low') {
        // Prefer low-energy tasks for low energy
        recommended = recommended.filter(t =>
          t.energyLevel === 'low' ||
          (t.estimatedDuration && t.estimatedDuration <= 15) ||
          t.priority === 'low'
        );
      } else if (energyLevel === 'high') {
        // Prefer high-energy/challenging tasks
        recommended = recommended.filter(t =>
          t.energyLevel === 'high' ||
          t.priority === 'high' ||
          (t.estimatedDuration && t.estimatedDuration > 30)
        );
      }

      res.json(recommended.slice(0, 10));
    } catch (e) {
      res.status(500).json({ error: "Failed to get energy-based tasks" });
    }
  });

  // Get quick wins
  app.get("/api/tasks/quick-wins", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    try {
      const user = req.user as User;
      const tasks = await storage.getTasksForUser(userId);
      const threshold = (user.adhdSettings as any)?.quickWinThreshold || 10;

      const quickWins = tasks
        .filter(t => {
          if (t.status === 'done') return false;
          if (t.estimatedDuration && t.estimatedDuration > threshold * 1.5) return false;
          return true;
        })
        .slice(0, 10);

      res.json(quickWins);
    } catch (e) {
      res.status(500).json({ error: "Failed to get quick wins" });
    }
  });

  // AI Task Breakdown
  app.post("/api/ai/breakdown-task", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    try {
      const { taskId, title, description, estimatedDuration } = req.body;

      const breakdown = await aiService.breakdownTask({
        title,
        description,
        estimatedDuration,
      });

      await storage.createAuditLog({
        userId,
        action: "AI_BREAKDOWN",
        entityType: "TASK",
        entityId: taskId,
        details: { title, subtaskCount: breakdown.subtasks?.length },
        source: "AI"
      });

      res.json(breakdown);
    } catch (e) {
      console.error("AI breakdown failed:", e);
      res.status(500).json({ error: "Failed to break down task" });
    }
  });

  // ============================================
  // BODY DOUBLING SESSION ROUTES
  // ============================================

  // Get all sessions
  app.get("/api/sessions", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    try {
      const sessions = await storage.getBodyDoublingSessions(userId);
      res.json(sessions);
    } catch (e) {
      res.status(500).json({ error: "Failed to get sessions" });
    }
  });

  // Create session
  app.post("/api/sessions", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    try {
      const session = await storage.createBodyDoublingSession({
        hostId: userId,
        ...req.body,
      });

      // Auto-join as participant
      await storage.joinSession(session.id, userId);

      res.json(session);
    } catch (e) {
      console.error("Create session failed:", e);
      res.status(500).json({ error: "Failed to create session" });
    }
  });

  // Join session
  app.post("/api/sessions/:id/join", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    const sessionId = req.params.id;
    try {
      await storage.joinSession(sessionId, userId);
      res.json({ success: true });
    } catch (e) {
      console.error("Join session failed:", e);
      res.status(500).json({ error: "Failed to join session" });
    }
  });

  // Leave session
  app.post("/api/sessions/:id/leave", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    const sessionId = req.params.id;
    try {
      await storage.leaveSession(sessionId, userId);
      res.json({ success: true });
    } catch (e) {
      console.error("Leave session failed:", e);
      res.status(500).json({ error: "Failed to leave session" });
    }
  });

  // ============================================
  // FOCUS SESSION ROUTES
  // ============================================

  // Start focus session
  app.post("/api/focus-sessions", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    try {
      const session = await storage.createFocusSession({
        userId,
        ...req.body,
      });
      res.json(session);
    } catch (e) {
      res.status(500).json({ error: "Failed to start focus session" });
    }
  });

  // Complete focus session
  app.patch("/api/focus-sessions/:id/complete", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    try {
      const { actualMinutes, wasCompleted } = req.body;
      const session = await storage.completeFocusSession(req.params.id, actualMinutes, wasCompleted);

      // Award XP based on completion
      const xpAmount = wasCompleted ? 15 : 5;
      // BUG FIX: Parameters were swapped (TYPE-2)
      await gamificationService.awardXP(userId, 'focus_session', xpAmount, {
        sessionType: session?.sessionType,
        actualMinutes,
      });

      res.json(session);
    } catch (e) {
      res.status(500).json({ error: "Failed to complete focus session" });
    }
  });

  // ============================================
  // DAILY CHALLENGES ROUTES
  // ============================================

  // Get today's challenges
  app.get("/api/challenges/today", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    try {
      let challenges = await storage.getTodaysChallenges(userId);

      // Generate new challenges if none exist for today
      if (challenges.length === 0) {
        challenges = await storage.generateDailyChallenges(userId);
      }

      res.json(challenges);
    } catch (e) {
      res.status(500).json({ error: "Failed to get challenges" });
    }
  });

  // Update challenge progress
  app.post("/api/challenges/:id/progress", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    try {
      const { increment } = req.body;
      const challenge = await storage.updateChallengeProgress(req.params.id, increment || 1);

      // Award XP if completed
      if (challenge?.completedAt && challenge.xpReward) {
        await gamificationService.awardXP(userId, 'daily_challenge', challenge.xpReward, {
          challengeType: challenge.challengeType,
        });
      }

      res.json(challenge);
    } catch (e) {
      res.status(500).json({ error: "Failed to update challenge progress" });
    }
  });

  // ============================================
  // PUSH NOTIFICATION ROUTES
  // ============================================

  // Get VAPID public key (needed by client to subscribe)
  app.get("/api/push/vapid-public-key", async (req, res) => {
    try {
      const publicKey = await getVapidPublicKey();
      if (!publicKey) {
        return res.status(503).json({ error: "Push notifications not configured" });
      }
      res.json({ publicKey });
    } catch (e) {
      console.error("Get VAPID key failed:", e);
      res.status(500).json({ error: "Failed to get VAPID key" });
    }
  });

  // Subscribe to push notifications
  app.post("/api/push/subscribe", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    try {
      const { subscription } = req.body;
      if (!subscription || !subscription.endpoint || !subscription.keys) {
        return res.status(400).json({ error: "Invalid subscription data" });
      }

      const userAgent = req.headers['user-agent'];
      const success = await savePushSubscription(userId, subscription, userAgent);

      if (success) {
        // Send a welcome notification to confirm it works
        await sendPushNotification(userId, {
          title: "Notifications Enabled",
          body: "You'll now receive task reminders even when the app is closed!",
          tag: "welcome"
        });
        res.json({ success: true });
      } else {
        res.status(500).json({ error: "Failed to save subscription" });
      }
    } catch (e) {
      console.error("Subscribe failed:", e);
      res.status(500).json({ error: "Failed to subscribe" });
    }
  });

  // Unsubscribe from push notifications
  app.post("/api/push/unsubscribe", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    try {
      const { endpoint } = req.body;
      if (!endpoint) {
        return res.status(400).json({ error: "Endpoint required" });
      }

      const success = await removePushSubscription(endpoint);
      res.json({ success });
    } catch (e) {
      console.error("Unsubscribe failed:", e);
      res.status(500).json({ error: "Failed to unsubscribe" });
    }
  });

  // Unsubscribe all devices for current user
  app.delete("/api/push/subscriptions", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    try {
      const success = await removeUserSubscriptions(userId);
      res.json({ success });
    } catch (e) {
      console.error("Remove subscriptions failed:", e);
      res.status(500).json({ error: "Failed to remove subscriptions" });
    }
  });

  // Handle subscription changes (from service worker)
  app.post("/api/push/resubscribe", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    try {
      const { oldEndpoint, newSubscription } = req.body;

      // Remove old subscription
      if (oldEndpoint) {
        await removePushSubscription(oldEndpoint);
      }

      // Save new subscription
      if (newSubscription) {
        const userAgent = req.headers['user-agent'];
        await savePushSubscription(userId, newSubscription, userAgent);
      }

      res.json({ success: true });
    } catch (e) {
      console.error("Resubscribe failed:", e);
      res.status(500).json({ error: "Failed to resubscribe" });
    }
  });

  // Test notification (useful for debugging)
  app.post("/api/push/test", async (req, res) => {
    if (!req.isAuthenticated()) return res.sendStatus(401);
    const userId = (req.user as User).id;
    try {
      const result = await sendPushNotification(userId, {
        title: "Test Notification",
        body: "Push notifications are working!",
        tag: "test"
      });
      res.json(result);
    } catch (e) {
      console.error("Test notification failed:", e);
      res.status(500).json({ error: "Failed to send test notification" });
    }
  });

  // End of routes
  return httpServer;
}
