// src/services/chat.service.ts
// Copyright (C) 2026 DTP Technologies, LLC
// All Rights Reserved

import { Types } from "mongoose";
import {
  ChatSession,
  ChatSessionType,
  ChatSessionMode,
} from "../models/chat-session.js";
import {
  ChatHistory,
  IChatHistory as IChatHistoryModel,
  ChatHistoryStatus,
} from "../models/chat-history.js";
import { DtpService } from "../lib/service.js";

export interface IChatSessionListItem {
  _id: string;
  name: string;
  lastMessageAt: Date | null;
  turnCount: number;
  toolCallCount: number;
  inputTokens: number;
  outputTokens: number;
  createdAt: Date;
  mode: ChatSessionMode;
}

export interface IChatSessionDetail extends IChatSessionListItem {
  user: string;
  project?: string;
  type: ChatSessionType;
  pins: Array<{ _id?: string; content: string }>;
}

export interface IChatHistoryEntry {
  _id: string;
  sessionId: string;
  prompt: string;
  response: {
    thinking?: string;
    message?: string;
  };
  status: ChatHistoryStatus;
  toolCalls: Array<{
    tool: {
      name: string;
      callId: string;
      parameters: Array<{ name: string; value: string }>;
    };
    response?: string;
    fileOperation?: {
      type: "read" | "write" | "edit" | "shell";
      path?: string;
      diff?: string;
      linesAdded?: number;
      linesRemoved?: number;
      isBinary?: boolean;
    };
    subagentStats?: {
      inputTokens: number;
      outputTokens: number;
      toolCallCount: number;
    };
  }>;
  fileOperations: Array<{
    type: "read" | "write" | "edit" | "shell";
    path?: string;
    diff?: string;
    linesAdded?: number;
    linesRemoved?: number;
    isBinary?: boolean;
  }>;
  inputTokens: number;
  outputTokens: number;
  createdAt: Date;
  mode: ChatSessionMode;
  isSubagent: boolean;
  error?: {
    message: string;
    stack?: string;
    timestamp: Date;
  };
}

export class ChatService extends DtpService {
  get name(): string {
    return "ChatService";
  }
  get slug(): string {
    return "chat";
  }

  async start(): Promise<void> {
    this.log.info("service started");
  }

  async stop(): Promise<void> {
    this.log.info("service stopped");
  }

  // ==================== Session Management ====================

  async listByProject(
    projectId: string,
    userId: string,
  ): Promise<IChatSessionListItem[]> {
    const sessions = await ChatSession.find({
      user: userId,
      project: projectId,
    })
      .sort({ lastMessageAt: -1, createdAt: -1 })
      .lean();

    return sessions.map((s) => ({
      _id: s._id.toString(),
      name: s.name,
      lastMessageAt: s.lastMessageAt ?? null,
      turnCount: s.stats.turnCount,
      toolCallCount: s.stats.toolCallCount,
      inputTokens: s.stats.inputTokens,
      outputTokens: s.stats.outputTokens,
      createdAt: s.createdAt,
      mode: s.mode,
    }));
  }

  async listAll(userId: string): Promise<IChatSessionListItem[]> {
    const sessions = await ChatSession.find({
      user: userId,
    })
      .sort({ lastMessageAt: -1, createdAt: -1 })
      .limit(50)
      .lean();

    return sessions.map((s) => ({
      _id: s._id.toString(),
      name: s.name,
      lastMessageAt: s.lastMessageAt ?? null,
      turnCount: s.stats.turnCount,
      toolCallCount: s.stats.toolCallCount,
      inputTokens: s.stats.inputTokens,
      outputTokens: s.stats.outputTokens,
      createdAt: s.createdAt,
      mode: s.mode,
    }));
  }

  async create(
    userId: string,
    projectId?: string,
    name?: string,
  ): Promise<IChatSessionListItem> {
    const session = new ChatSession({
      user: userId,
      project: projectId,
      name: name ?? "New Session",
      type: ChatSessionType.Desktop,
      mode: ChatSessionMode.Build,
    });
    await session.save();

    this.log.info("ChatSession created", {
      sessionId: session._id,
      userId,
      projectId,
    });

    return {
      _id: session._id.toString(),
      name: session.name,
      lastMessageAt: null,
      turnCount: 0,
      toolCallCount: 0,
      inputTokens: 0,
      outputTokens: 0,
      createdAt: session.createdAt,
      mode: session.mode,
    };
  }

  async findById(sessionId: string): Promise<IChatSessionDetail | null> {
    const session = await ChatSession.findById(sessionId).lean();
    if (!session) {
      return null;
    }

    const sessionWithProject = await ChatSession.findById(sessionId)
      .populate("project", "name")
      .lean() as any;
    const projectName = (sessionWithProject?.project as any)?.name;

    const userId =
      session.user instanceof Types.ObjectId
        ? session.user.toString()
        : String(session.user);

    return {
      _id: session._id.toString(),
      name: session.name,
      lastMessageAt: session.lastMessageAt ?? null,
      turnCount: session.stats.turnCount,
      toolCallCount: session.stats.toolCallCount,
      inputTokens: session.stats.inputTokens,
      outputTokens: session.stats.outputTokens,
      createdAt: session.createdAt,
      mode: session.mode,
      user: userId,
      project: projectName,
      type: session.type,
      pins: session.pins.map((p) => ({
        _id: p._id?.toString(),
        content: p.content,
      })),
    };
  }

  async updateName(sessionId: string, name: string): Promise<void> {
    await ChatSession.findByIdAndUpdate(sessionId, { name });
    this.log.info("ChatSession name updated", { sessionId, name });
  }

  async updateMode(sessionId: string, mode: ChatSessionMode): Promise<void> {
    await ChatSession.findByIdAndUpdate(sessionId, { mode });
    this.log.info("ChatSession mode updated", { sessionId, mode });
  }

  async delete(sessionId: string, userId: string): Promise<void> {
    const result = await ChatSession.deleteOne({
      _id: sessionId,
      user: userId,
    });
    if (result.deletedCount === 0) {
      throw new Error("ChatSession not found or not owned by user");
    }
    this.log.info("ChatSession deleted", { sessionId, userId });
  }

  // ==================== History Management ====================

  async getHistory(sessionId: string): Promise<IChatHistoryEntry[]> {
    const history = await ChatHistory.find({ session: sessionId })
      .sort({ createdAt: 1 })
      .lean();

    return history.map((h) => ({
      _id: h._id.toString(),
      sessionId: h.session.toString(),
      prompt: h.prompt,
      response: {
        thinking: h.response?.thinking,
        message: h.response?.message,
      },
      status: h.status,
      toolCalls: h.toolCalls.map((tc) => ({
        tool: {
          name: tc.tool.name,
          callId: tc.tool.callId,
          parameters: tc.tool.parameters.map((p) => ({
            name: p.name,
            value: p.value,
          })),
        },
        response: tc.response,
        fileOperation: tc.fileOperation,
        subagentStats: tc.subagentStats,
      })),
      fileOperations: h.fileOperations,
      inputTokens: h.inputTokens,
      outputTokens: h.outputTokens,
      createdAt: h.createdAt,
      mode: h.mode,
      isSubagent: h.isSubagent ?? false,
      subagentHistory: h.subagentHistory || [],
      error: h.error,
    }));
  }

  async createHistoryEntry(
    sessionId: string,
    userId: string,
    prompt: string,
    mode: ChatSessionMode,
  ): Promise<IChatHistoryEntry> {
    const entry = new ChatHistory({
      session: sessionId,
      user: userId,
      prompt,
      mode,
      status: ChatHistoryStatus.Processing,
    });
    await entry.save();

    // Update session stats
    await ChatSession.findByIdAndUpdate(sessionId, {
      $inc: {
        "stats.turnCount": 1,
        "stats.inputTokens": entry.inputTokens,
        "stats.outputTokens": entry.outputTokens,
      },
      lastMessageAt: new Date(),
    });

    this.log.info("ChatHistory entry created", {
      historyId: entry._id,
      sessionId,
      userId,
    });

    return {
      _id: entry._id.toString(),
      sessionId,
      prompt: entry.prompt,
      response: {
        thinking: entry.response?.thinking,
        message: entry.response?.message,
      },
      status: entry.status,
      toolCalls: [],
      fileOperations: [],
      inputTokens: 0,
      outputTokens: 0,
      createdAt: entry.createdAt,
      mode: entry.mode,
      isSubagent: entry.isSubagent ?? false,
    };
  }

  async updateHistoryEntry(
    historyId: string,
    updates: Partial<{
      response: { thinking?: string; message?: string };
      status: ChatHistoryStatus;
      toolCalls: IChatHistoryModel["toolCalls"];
      fileOperations: IChatHistoryModel["fileOperations"];
      inputTokens: number;
      outputTokens: number;
      error: { message: string; stack?: string; timestamp: Date };
    }>,
  ): Promise<void> {
    const updateData: any = {};
    if (updates.response !== undefined) {
      updateData.response = updates.response;
    }
    if (updates.status !== undefined) {
      updateData.status = updates.status;
    }
    if (updates.toolCalls !== undefined) {
      updateData.toolCalls = updates.toolCalls;
    }
    if (updates.fileOperations !== undefined) {
      updateData.fileOperations = updates.fileOperations;
    }
    if (updates.inputTokens !== undefined) {
      updateData.inputTokens = updates.inputTokens;
    }
    if (updates.outputTokens !== undefined) {
      updateData.outputTokens = updates.outputTokens;
    }
    if (updates.error !== undefined) {
      updateData.error = updates.error;
    }

    await ChatHistory.findByIdAndUpdate(historyId, updateData);
    this.log.info("ChatHistory entry updated", { historyId });
  }
}

export default new ChatService();
