import { Type } from "@sinclair/typebox";
import {
  listMessages,
  readMessage,
  sendMessage,
  replyToMessage,
  searchMessages,
  listFolders,
  markAsRead,
  moveMessage,
  type MailMessage,
} from "./graph-mail.js";

function formatMessage(msg: MailMessage, includeBody = false): string {
  const from = msg.from?.emailAddress;
  const to = msg.toRecipients?.map((r) => r.emailAddress?.address).join(", ") ?? "";
  const lines = [
    `ID: ${msg.id}`,
    `From: ${from?.name ?? ""} <${from?.address ?? ""}>`,
    `To: ${to}`,
    `Subject: ${msg.subject ?? "(no subject)"}`,
    `Date: ${msg.receivedDateTime ?? ""}`,
    `Read: ${msg.isRead ? "yes" : "no"}`,
    msg.hasAttachments ? "Attachments: yes" : "",
    msg.importance && msg.importance !== "normal" ? `Importance: ${msg.importance}` : "",
  ].filter(Boolean);

  if (includeBody && msg.body?.content) {
    const body =
      msg.body.contentType === "html"
        ? msg.body.content
            .replace(/<[^>]+>/g, " ")
            .replace(/\s+/g, " ")
            .trim()
        : msg.body.content;
    lines.push("", "--- Body ---", body.slice(0, 8000));
  } else if (msg.bodyPreview) {
    lines.push("", `Preview: ${msg.bodyPreview}`);
  }

  return lines.join("\n");
}

export function createOutlookListTool() {
  return {
    name: "outlook_list",
    label: "Outlook: List Emails",
    description:
      "List recent emails from an Outlook 365 mailbox folder. Returns subject, sender, date, and preview for each message.",
    parameters: Type.Object({
      folder: Type.Optional(
        Type.String({
          description: 'Mail folder (default: "inbox"). Use "sentitems", "drafts", "archive", etc.',
        }),
      ),
      count: Type.Optional(
        Type.Number({ description: "Number of emails to return (default: 10, max: 25)" }),
      ),
      unread_only: Type.Optional(
        Type.Boolean({ description: "Only show unread emails (default: false)" }),
      ),
    }),
    async execute(_id: string, params: Record<string, unknown>) {
      const folder = (params.folder as string) ?? "inbox";
      const count = Math.min(Number(params.count) || 10, 25);
      const filter = params.unread_only ? "isRead eq false" : undefined;

      const messages = await listMessages({ folder, top: count, filter });

      if (messages.length === 0) {
        return { content: [{ type: "text", text: `No emails found in ${folder}.` }] };
      }

      const text = messages.map((m) => formatMessage(m)).join("\n\n---\n\n");
      return {
        content: [{ type: "text", text: `${messages.length} email(s) from ${folder}:\n\n${text}` }],
      };
    },
  };
}

export function createOutlookReadTool() {
  return {
    name: "outlook_read",
    label: "Outlook: Read Email",
    description:
      "Read the full content of a specific email by its ID. Returns complete body, headers, and metadata.",
    parameters: Type.Object({
      message_id: Type.String({ description: "The email message ID to read" }),
      mark_read: Type.Optional(
        Type.Boolean({ description: "Mark the email as read after opening (default: true)" }),
      ),
    }),
    async execute(_id: string, params: Record<string, unknown>) {
      const messageId = params.message_id as string;
      if (!messageId) {
        throw new Error("message_id is required");
      }

      const msg = await readMessage(messageId);
      const shouldMark = params.mark_read !== false;
      if (shouldMark && !msg.isRead) {
        await markAsRead(messageId).catch(() => {});
      }

      return { content: [{ type: "text", text: formatMessage(msg, true) }] };
    },
  };
}

export function createOutlookSendTool() {
  return {
    name: "outlook_send",
    label: "Outlook: Send Email",
    description:
      "Send an email from the connected Outlook 365 account. Supports To, CC, subject, and text body.",
    parameters: Type.Object({
      to: Type.Array(Type.String(), {
        description: "Recipient email addresses",
        minItems: 1,
      }),
      subject: Type.String({ description: "Email subject line" }),
      body: Type.String({ description: "Email body (plain text)" }),
      cc: Type.Optional(Type.Array(Type.String(), { description: "CC recipient email addresses" })),
    }),
    async execute(_id: string, params: Record<string, unknown>) {
      const to = params.to as string[];
      const subject = params.subject as string;
      const body = params.body as string;
      const cc = params.cc as string[] | undefined;

      if (!to?.length) throw new Error("At least one recipient is required");
      if (!subject?.trim()) throw new Error("Subject is required");
      if (!body?.trim()) throw new Error("Body is required");

      await sendMessage({ to, cc, subject, body });
      return {
        content: [{ type: "text", text: `Email sent to ${to.join(", ")}.\nSubject: ${subject}` }],
      };
    },
  };
}

export function createOutlookReplyTool() {
  return {
    name: "outlook_reply",
    label: "Outlook: Reply to Email",
    description: "Reply to an existing email by its ID. Supports reply and reply-all.",
    parameters: Type.Object({
      message_id: Type.String({ description: "The email message ID to reply to" }),
      body: Type.String({ description: "Reply body text" }),
      reply_all: Type.Optional(
        Type.Boolean({ description: "Reply to all recipients (default: false)" }),
      ),
    }),
    async execute(_id: string, params: Record<string, unknown>) {
      const messageId = params.message_id as string;
      const body = params.body as string;
      const replyAll = Boolean(params.reply_all);

      if (!messageId) throw new Error("message_id is required");
      if (!body?.trim()) throw new Error("Reply body is required");

      await replyToMessage({ messageId, body, replyAll });
      return {
        content: [
          {
            type: "text",
            text: `Reply ${replyAll ? "(all) " : ""}sent to message ${messageId}.`,
          },
        ],
      };
    },
  };
}

export function createOutlookSearchTool() {
  return {
    name: "outlook_search",
    label: "Outlook: Search Emails",
    description:
      "Search emails in the Outlook 365 mailbox using keywords. Searches across subject, body, and sender.",
    parameters: Type.Object({
      query: Type.String({ description: "Search query (keywords, sender name, subject text)" }),
      count: Type.Optional(
        Type.Number({ description: "Max results to return (default: 10, max: 25)" }),
      ),
    }),
    async execute(_id: string, params: Record<string, unknown>) {
      const query = params.query as string;
      const count = Math.min(Number(params.count) || 10, 25);

      if (!query?.trim()) throw new Error("Search query is required");

      const messages = await searchMessages(query, count);

      if (messages.length === 0) {
        return { content: [{ type: "text", text: `No emails found for "${query}".` }] };
      }

      const text = messages.map((m) => formatMessage(m)).join("\n\n---\n\n");
      return {
        content: [
          { type: "text", text: `${messages.length} result(s) for "${query}":\n\n${text}` },
        ],
      };
    },
  };
}

export function createOutlookFoldersTool() {
  return {
    name: "outlook_folders",
    label: "Outlook: List Folders",
    description:
      "List all mail folders in the Outlook 365 mailbox with item counts and unread counts.",
    parameters: Type.Object({}),
    async execute() {
      const folders = await listFolders();
      const text = folders
        .map(
          (f) =>
            `${f.displayName} (${f.unreadItemCount ?? 0} unread / ${f.totalItemCount ?? 0} total) [id: ${f.id}]`,
        )
        .join("\n");
      return { content: [{ type: "text", text: text || "No folders found." }] };
    },
  };
}

export function createOutlookMoveTool() {
  return {
    name: "outlook_move",
    label: "Outlook: Move Email",
    description: "Move an email to a different folder by message ID and destination folder ID.",
    parameters: Type.Object({
      message_id: Type.String({ description: "The email message ID to move" }),
      destination: Type.String({
        description:
          'Destination folder ID or well-known name ("archive", "deleteditems", "junkemail")',
      }),
    }),
    async execute(_id: string, params: Record<string, unknown>) {
      const messageId = params.message_id as string;
      const destination = params.destination as string;

      if (!messageId) throw new Error("message_id is required");
      if (!destination) throw new Error("destination folder is required");

      await moveMessage(messageId, destination);
      return {
        content: [{ type: "text", text: `Email ${messageId} moved to ${destination}.` }],
      };
    },
  };
}
