// src/controllers/api/v1/chat-session.ts
// Copyright (C) 2026 Robert Colbert <rob.colbert@openplatform.us>
// All Rights Reserved

import { Request, Response } from "express";

import { DtpController } from "../../../lib/controller.js";
import ChatSessionService from "../../../services/chat-session.js";
import { ChatSessionMode } from "@gadget/api";
import { populateChatSessionById } from "@/controllers/lib/populators.js";

class ChatSessionController extends DtpController {
  get name(): string {
    return "ChatSessionController";
  }
  get slug(): string {
    return "ctrl:api:v1:chat-session";
  }
  get route(): string {
    return "/chat-sessions";
  }

  async start(): Promise<void> {
    this.router.use(this.requireUser());

    this.router.param("sessionId", populateChatSessionById(this));

    this.router.post("/", this.createSession.bind(this));

    this.router.get("/:sessionId/turns", this.getSessionTurns.bind(this));
    this.router.get("/:sessionId", this.getSession.bind(this));
    this.router.get("/", this.listSessions.bind(this));

    this.router.put("/:sessionId", this.updateSession.bind(this));

    this.router.delete("/:sessionId", this.deleteSession.bind(this));
  }

  /**
   * GET /api/v1/chat-sessions?projectId=:projectId
   * List chat sessions for a project or user.
   */
  private async listSessions(req: Request, res: Response): Promise<void> {
    try {
      const user = req.user;
      if (!user) {
        res.status(401).json({ success: false, message: "Unauthorized" });
        return;
      }

      const projectId = req.query.projectId as string | undefined;

      let sessions;
      if (projectId) {
        sessions = await ChatSessionService.getByProject(projectId);
      } else {
        sessions = await ChatSessionService.getByUser(user._id);
      }

      res.json({
        success: true,
        data: sessions,
      });
    } catch (error) {
      const err = error as Error;
      this.log.error("failed to list chat sessions", { error: err.message });
      res.status(500).json({
        success: false,
        message: err.message,
      });
    }
  }

  /**
   * POST /api/v1/chat-sessions
   * Create a new chat session.
   */
  private async createSession(req: Request, res: Response): Promise<void> {
    try {
      const user = req.user;
      if (!user) {
        res.status(401).json({ success: false, message: "Unauthorized" });
        return;
      }

      const { projectId, providerId, selectedModel, mode, name } = req.body;

      if (!projectId) {
        res.status(400).json({
          success: false,
          message: "projectId is required",
        });
        return;
      }

      if (!providerId) {
        res.status(400).json({
          success: false,
          message: "providerId is required",
        });
        return;
      }

      if (!selectedModel) {
        res.status(400).json({
          success: false,
          message: "selectedModel is required",
        });
        return;
      }

      const sessionMode = mode
        ? (mode as ChatSessionMode)
        : ChatSessionMode.Build;

      const session = await ChatSessionService.create(
        user._id,
        projectId,
        providerId,
        selectedModel,
        sessionMode,
        name,
      );

      res.status(201).json({
        success: true,
        data: session,
      });
    } catch (error) {
      const err = error as Error;
      this.log.error("failed to create chat session", { error: err.message });
      res.status(500).json({
        success: false,
        message: err.message,
      });
    }
  }

  /**
   * GET /api/v1/chat-sessions/:id
   * Get a specific chat session.
   */
  private async getSession(_req: Request, res: Response): Promise<void> {
    try {
      res.json({
        success: true,
        data: res.locals.chatSession,
      });
    } catch (error) {
      const err = error as Error;
      this.log.error("failed to get chat session", { error: err.message });

      if (err.message.includes("not found")) {
        res.status(404).json({
          success: false,
          message: err.message,
        });
      } else {
        res.status(500).json({
          success: false,
          message: err.message,
        });
      }
    }
  }

  /**
   * PUT /api/v1/chat-sessions/:id
   * Update a chat session.
   */
  private async updateSession(req: Request, res: Response): Promise<void> {
    try {
      const updates = req.body;

      // Validate allowed updates
      const allowedUpdates: Partial<{
        name: string;
        provider: string;
        selectedModel: string;
        mode: ChatSessionMode;
        reasoningEffort: "off" | "low" | "medium" | "high";
        numCtx: number;
      }> = {};

      if (updates.name !== undefined) {
        allowedUpdates.name = updates.name;
      }
      if (updates.provider !== undefined) {
        allowedUpdates.provider = updates.provider;
      }
      if (updates.selectedModel !== undefined) {
        allowedUpdates.selectedModel = updates.selectedModel;
      }
      if (updates.mode !== undefined) {
        if (Object.values(ChatSessionMode).includes(updates.mode as ChatSessionMode)) {
          allowedUpdates.mode = updates.mode as ChatSessionMode;
        } else {
          throw new Error(`Invalid mode: ${updates.mode}`);
        }
      }
      if (updates.reasoningEffort !== undefined) {
        const validValues = ["off", "low", "medium", "high"] as const;
        if (!validValues.includes(updates.reasoningEffort)) {
          throw new Error(`Invalid reasoningEffort: ${updates.reasoningEffort}. Must be one of: ${validValues.join(", ")}`);
        }
        allowedUpdates.reasoningEffort = updates.reasoningEffort;
      }
      if (updates.numCtx !== undefined) {
        if (typeof updates.numCtx !== "number" || !Number.isInteger(updates.numCtx) || updates.numCtx < 16384 || updates.numCtx % 8192 !== 0) {
          throw new Error(`Invalid numCtx: ${updates.numCtx}. Must be an integer >= 16384 and a multiple of 8192.`);
        }
        allowedUpdates.numCtx = updates.numCtx;
      }

      const session = await ChatSessionService.update(
        res.locals.chatSession._id,
        allowedUpdates,
      );

      res.json({
        success: true,
        data: session,
      });
    } catch (error) {
      const err = error as Error;
      this.log.error("failed to update chat session", { error: err.message });

      if (err.message.includes("not found")) {
        res.status(404).json({
          success: false,
          message: err.message,
        });
      } else {
        res.status(500).json({
          success: false,
          message: err.message,
        });
      }
    }
  }

  /**
   * DELETE /api/v1/chat-sessions/:id
   * Delete a chat session.
   */
  private async deleteSession(_req: Request, res: Response): Promise<void> {
    try {
      await ChatSessionService.delete(res.locals.chatSession._id);
      res.json({
        success: true,
        message: "Chat session deleted",
      });
    } catch (error) {
      const err = error as Error;
      this.log.error("failed to delete chat session", { error: err.message });

      if (err.message.includes("not found")) {
        res.status(404).json({
          success: false,
          message: err.message,
        });
      } else {
        res.status(500).json({
          success: false,
          message: err.message,
        });
      }
    }
  }

  /**
   * GET /api/v1/chat-sessions/:id/turns
   * Get all turns for a chat session.
   */
  private async getSessionTurns(_req: Request, res: Response): Promise<void> {
    try {
      const turns = await ChatSessionService.getTurns(
        res.locals.chatSession._id,
      );

      res.json({
        success: true,
        data: turns,
      });
    } catch (error) {
      const err = error as Error;
      this.log.error("failed to get chat session turns", {
        error: err.message,
      });
      res.status(500).json({
        success: false,
        message: err.message,
      });
    }
  }
}

export default ChatSessionController;
