import { Type } from "@sinclair/typebox";
import { stringEnum } from "../../../src/agents/schema/typebox.js";
import type { AnyAgentTool } from "../../../src/plugins/types.js";

const ACTIONS = [
  "get_me",
  "list_issues",
  "get_issue",
  "create_issue",
  "update_issue",
  "checkout_issue",
  "release_issue",
  "post_comment",
  "get_comments",
  "list_agents",
] as const;

type Action = (typeof ACTIONS)[number];

const PaperclipToolSchema = Type.Object({
  action: stringEnum(ACTIONS, {
    description:
      "API action to perform. " +
      "get_me: fetch your agent profile. " +
      "list_issues: list issues assigned to you. " +
      "get_issue: fetch a single issue by ID. " +
      "create_issue: create a new issue. " +
      "update_issue: update an issue (status, title, description, assignee). " +
      "checkout_issue: claim an issue for work. " +
      "release_issue: release a checked-out issue. " +
      "post_comment: add a comment to an issue. " +
      "get_comments: list comments on an issue. " +
      "list_agents: list agents in the company.",
  }),
  issueId: Type.Optional(
    Type.String({ description: "Issue ID (required for issue-specific actions)." }),
  ),
  body: Type.Optional(
    Type.Object(
      {
        title: Type.Optional(Type.String()),
        description: Type.Optional(Type.String()),
        status: Type.Optional(Type.String()),
        assigneeAgentId: Type.Optional(Type.String()),
        parentId: Type.Optional(Type.String()),
        comment: Type.Optional(Type.String()),
        expectedStatuses: Type.Optional(Type.Array(Type.String())),
      },
      { description: "Request body for mutations.", additionalProperties: true },
    ),
  ),
});

export type PaperclipConfig = {
  apiUrl: string;
  apiKey: string;
  agentId?: string;
  companyId?: string;
  runId?: string;
};

function resolveConfig(pluginConfig?: Record<string, unknown>): PaperclipConfig | null {
  const apiUrl = str(pluginConfig?.apiUrl) ?? process.env.PAPERCLIP_API_URL;
  const apiKey = str(pluginConfig?.apiKey) ?? process.env.PAPERCLIP_API_KEY;

  if (!apiUrl || !apiKey) return null;

  return {
    apiUrl: apiUrl.replace(/\/+$/, ""),
    apiKey,
    agentId: str(pluginConfig?.agentId) ?? process.env.PAPERCLIP_AGENT_ID,
    companyId: str(pluginConfig?.companyId) ?? process.env.PAPERCLIP_COMPANY_ID,
    runId: process.env.PAPERCLIP_RUN_ID,
  };
}

function str(value: unknown): string | null {
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
}

async function paperclipFetch(
  cfg: PaperclipConfig,
  method: string,
  path: string,
  body?: unknown,
): Promise<unknown> {
  const url = `${cfg.apiUrl}${path}`;
  const headers: Record<string, string> = {
    authorization: `Bearer ${cfg.apiKey}`,
    "content-type": "application/json",
    accept: "application/json",
  };
  if (cfg.runId) {
    headers["x-paperclip-run-id"] = cfg.runId;
  }

  const res = await fetch(url, {
    method,
    headers,
    ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
  });

  const text = await res.text();
  let json: unknown;
  try {
    json = JSON.parse(text);
  } catch {
    json = { rawResponse: text };
  }

  if (!res.ok) {
    return {
      error: true,
      status: res.status,
      statusText: res.statusText,
      body: json,
    };
  }
  return json;
}

function requireIssueId(params: Record<string, unknown>): string {
  const id = str(params.issueId);
  if (!id) throw new Error("issueId is required for this action");
  return id;
}

async function executeAction(
  cfg: PaperclipConfig,
  action: Action,
  params: Record<string, unknown>,
): Promise<unknown> {
  const body = (params.body ?? {}) as Record<string, unknown>;

  switch (action) {
    case "get_me":
      return paperclipFetch(cfg, "GET", "/api/agents/me");

    case "list_issues": {
      const companyId = str(body.companyId) ?? cfg.companyId;
      if (!companyId) return { error: true, message: "companyId required (set in config or body)" };
      const agentId = cfg.agentId ?? "";
      const statuses = str(body.status) ?? "todo,in_progress,blocked";
      return paperclipFetch(
        cfg,
        "GET",
        `/api/companies/${companyId}/issues?assigneeAgentId=${agentId}&status=${statuses}`,
      );
    }

    case "get_issue":
      return paperclipFetch(cfg, "GET", `/api/issues/${requireIssueId(params)}`);

    case "create_issue": {
      const companyId = str(body.companyId) ?? cfg.companyId;
      if (!companyId) return { error: true, message: "companyId required (set in config or body)" };
      const { companyId: _, ...issueBody } = body;
      return paperclipFetch(cfg, "POST", `/api/companies/${companyId}/issues`, issueBody);
    }

    case "update_issue":
      return paperclipFetch(cfg, "PATCH", `/api/issues/${requireIssueId(params)}`, body);

    case "checkout_issue": {
      const issueId = requireIssueId(params);
      const checkoutBody: Record<string, unknown> = {};
      if (cfg.agentId) checkoutBody.agentId = cfg.agentId;
      if (Array.isArray(body.expectedStatuses)) {
        checkoutBody.expectedStatuses = body.expectedStatuses;
      } else {
        checkoutBody.expectedStatuses = ["todo", "backlog", "blocked"];
      }
      return paperclipFetch(cfg, "POST", `/api/issues/${issueId}/checkout`, checkoutBody);
    }

    case "release_issue":
      return paperclipFetch(cfg, "POST", `/api/issues/${requireIssueId(params)}/release`, {});

    case "post_comment": {
      const issueId = requireIssueId(params);
      const commentBody = str(body.body) ?? str(body.comment);
      if (!commentBody) return { error: true, message: "body.body or body.comment is required" };
      return paperclipFetch(cfg, "POST", `/api/issues/${issueId}/comments`, {
        body: commentBody,
      });
    }

    case "get_comments":
      return paperclipFetch(cfg, "GET", `/api/issues/${requireIssueId(params)}/comments`);

    case "list_agents": {
      const companyId = str(body.companyId) ?? cfg.companyId;
      if (!companyId) return { error: true, message: "companyId required (set in config or body)" };
      return paperclipFetch(cfg, "GET", `/api/companies/${companyId}/agents`);
    }

    default:
      return { error: true, message: `Unknown action: ${action}` };
  }
}

export function createPaperclipTool(pluginConfig?: Record<string, unknown>): AnyAgentTool {
  return {
    name: "paperclip",
    label: "Paperclip",
    description:
      "Interact with the Paperclip control plane API to manage issues, post comments, " +
      "coordinate with other agents, and track your work assignments.",
    parameters: PaperclipToolSchema,
    async execute(_toolCallId: string, params: Record<string, unknown>) {
      const cfg = resolveConfig(pluginConfig);
      if (!cfg) {
        return {
          content: [
            {
              type: "text" as const,
              text: JSON.stringify(
                {
                  error: true,
                  message:
                    "Paperclip not configured. Set PAPERCLIP_API_URL and PAPERCLIP_API_KEY " +
                    "environment variables, or configure integrations.paperclip in your gateway config.",
                },
                null,
                2,
              ),
            },
          ],
          details: { error: true, message: "Paperclip not configured" },
        };
      }

      try {
        const result = await executeAction(cfg, params.action as Action, params);
        return {
          content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }],
          details: result,
        };
      } catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        return {
          content: [
            {
              type: "text" as const,
              text: JSON.stringify({ error: true, message }, null, 2),
            },
          ],
          details: { error: true, message },
        };
      }
    },
  };
}
