import { COMMENT_ENTITY_TYPE_VALUES, type CommentEntityType } from "@capakraken/shared";
import type { TRPCContext } from "../../trpc.js";
import { getCommentToolEntityDescription, getCommentToolScopeSentence } from "../../lib/comment-entity-registry.js";
import { withToolAccess, type ToolContext, type ToolDef, type ToolExecutor } from "./shared.js";

type AssistantToolErrorResult = { error: string };

type CommentRecord = {
  id: string;
  body: string;
  resolved: boolean;
  createdAt: Date;
  author: {
    name: string | null;
    email: string;
  };
};

type CommentListRecord = CommentRecord & {
  replies: CommentRecord[];
};

type CommentsDeps = {
  createCommentCaller: (ctx: TRPCContext) => {
    list: (params: {
      entityType: CommentEntityType;
      entityId: string;
    }) => Promise<CommentListRecord[]>;
    create: (params: {
      entityType: CommentEntityType;
      entityId: string;
      body: string;
    }) => Promise<CommentRecord>;
    resolve: (params: {
      id: string;
      resolved: boolean;
    }) => Promise<CommentRecord>;
  };
  createScopedCallerContext: (ctx: ToolContext) => TRPCContext;
  toAssistantCommentCreationError: (
    error: unknown,
  ) => AssistantToolErrorResult | null;
  toAssistantCommentResolveError: (
    error: unknown,
  ) => AssistantToolErrorResult | null;
};

export const commentReadToolDefinitions: ToolDef[] = withToolAccess([
  {
    type: "function",
    function: {
      name: "list_comments",
      description: `List comments (with replies) for a supported comment-enabled entity. ${getCommentToolScopeSentence()} Entity visibility is required.`,
      parameters: {
        type: "object",
        properties: {
          entityType: { type: "string", enum: [...COMMENT_ENTITY_TYPE_VALUES], description: getCommentToolEntityDescription() },
          entityId: { type: "string", description: "Entity ID" },
        },
        required: ["entityType", "entityId"],
      },
    },
  },
], {});

export const commentMutationToolDefinitions: ToolDef[] = withToolAccess([
  {
    type: "function",
    function: {
      name: "create_comment",
      description: `Add a comment to a supported comment-enabled entity. ${getCommentToolScopeSentence()} Entity visibility is required. Supports @mentions. Always confirm with the user first.`,
      parameters: {
        type: "object",
        properties: {
          entityType: { type: "string", enum: [...COMMENT_ENTITY_TYPE_VALUES], description: getCommentToolEntityDescription() },
          entityId: { type: "string", description: "Entity ID" },
          body: { type: "string", description: "Comment body text. Use @[Name](userId) for mentions." },
        },
        required: ["entityType", "entityId", "body"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "resolve_comment",
      description: `Mark a comment as resolved (or unresolve it) on a supported comment-enabled entity. ${getCommentToolScopeSentence()} Entity visibility is required, and only the comment author or an admin can change resolution.`,
      parameters: {
        type: "object",
        properties: {
          commentId: { type: "string", description: "Comment ID to resolve" },
          resolved: { type: "boolean", description: "Set to true to resolve, false to unresolve. Default: true" },
        },
        required: ["commentId"],
      },
    },
  },
], {});

export function createCommentExecutors(
  deps: CommentsDeps,
): Record<string, ToolExecutor> {
  return {
    async list_comments(
      params: { entityType: CommentEntityType; entityId: string },
      ctx: ToolContext,
    ) {
      const caller = deps.createCommentCaller(deps.createScopedCallerContext(ctx));
      const comments = await caller.list({
        entityType: params.entityType,
        entityId: params.entityId,
      });

      return comments.map((comment) => ({
        id: comment.id,
        author: comment.author.name ?? comment.author.email,
        body: comment.body,
        resolved: comment.resolved,
        createdAt: comment.createdAt.toISOString(),
        replyCount: comment.replies.length,
        replies: comment.replies.map((reply) => ({
          id: reply.id,
          author: reply.author.name ?? reply.author.email,
          body: reply.body,
          resolved: reply.resolved,
          createdAt: reply.createdAt.toISOString(),
        })),
      }));
    },

    async create_comment(
      params: { entityType: CommentEntityType; entityId: string; body: string },
      ctx: ToolContext,
    ) {
      if (params.body.length === 0) {
        return { error: "Comment body is required." };
      }

      if (params.body.length > 10_000) {
        return { error: "Comment body must be at most 10000 characters." };
      }

      const caller = deps.createCommentCaller(deps.createScopedCallerContext(ctx));
      let comment;
      try {
        comment = await caller.create({
          entityType: params.entityType,
          entityId: params.entityId,
          body: params.body,
        });
      } catch (error) {
        const mapped = deps.toAssistantCommentCreationError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate",
        scope: ["comment"],
        id: comment.id,
        author: comment.author.name ?? comment.author.email,
        body: comment.body,
        createdAt: comment.createdAt.toISOString(),
      };
    },

    async resolve_comment(
      params: { commentId: string; resolved?: boolean },
      ctx: ToolContext,
    ) {
      const caller = deps.createCommentCaller(deps.createScopedCallerContext(ctx));
      let updated;
      try {
        updated = await caller.resolve({
          id: params.commentId,
          resolved: params.resolved !== false,
        });
      } catch (error) {
        const mapped = deps.toAssistantCommentResolveError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate",
        scope: ["comment"],
        id: updated.id,
        resolved: updated.resolved,
        author: updated.author.name ?? updated.author.email,
        body: updated.body.slice(0, 100),
      };
    },
  };
}
