// ============================================
// AD MANAGEMENT TOOLS
// ============================================

import type { ToolDefinition, GoogleAdsClient, ToolResult } from "../types.js";
import { microsToMoney, formatPercent, formatNumber } from "../types.js";

// ---- Helpers ----

function resolveCustomerId(args: Record<string, any>, client: GoogleAdsClient): string {
  const cid = args.customer_id || client.getCustomerId();
  if (!cid) throw new Error("customer_id is required — pass it explicitly or set GOOGLE_ADS_CUSTOMER_ID.");
  return cid.replace(/-/g, "");
}

function textResult(text: string): ToolResult {
  return { content: [{ type: "text", text }] };
}

function errorResult(message: string): ToolResult {
  return { content: [{ type: "text", text: `❌ ${message}` }], isError: true };
}

// ---- Tools ----

const tools: ToolDefinition[] = [
  // ============================
  // list_ads
  // ============================
  {
    name: "list_ads",
    description:
      "List ads in an ad group with type, status, approval status, policy info, and performance metrics.",
    category: "ads",
    annotations: {
      title: "List Ads",
      readOnlyHint: true,
      destructiveHint: false,
      idempotentHint: true,
      openWorldHint: false,
    },
    inputSchema: {
      type: "object",
      properties: {
        customer_id: {
          type: "string",
          description: "Google Ads customer ID (defaults to configured account).",
        },
        ad_group_id: {
          type: "string",
          description: "Ad group ID to list ads for.",
        },
        status_filter: {
          type: "string",
          description: "Filter by ad status.",
          enum: ["ENABLED", "PAUSED", "REMOVED"],
        },
        limit: {
          type: "number",
          description: "Maximum number of ads to return (default: 50).",
        },
      },
      required: ["ad_group_id"],
    },
    _meta: {
      labels: {
        category: "ads",
        access: "read",
        complexity: "simple"
      }
    },
    handler: async (args, client) => {
      const cid = resolveCustomerId(args, client);
      const limit = Math.min(args.limit || 50, 200);

      let query = `
        SELECT
          ad_group_ad.ad.id,
          ad_group_ad.ad.type,
          ad_group_ad.ad.name,
          ad_group_ad.status,
          ad_group_ad.ad.final_urls,
          ad_group_ad.ad.responsive_search_ad.headlines,
          ad_group_ad.ad.responsive_search_ad.descriptions,
          ad_group_ad.policy_summary.approval_status,
          ad_group_ad.policy_summary.review_status,
          ad_group_ad.ad.resource_name,
          ad_group.id,
          ad_group.name,
          metrics.impressions,
          metrics.clicks,
          metrics.cost_micros,
          metrics.conversions,
          metrics.ctr
        FROM ad_group_ad
        WHERE ad_group.id = ${args.ad_group_id}
          AND segments.date DURING LAST_30_DAYS`;

      if (args.status_filter) {
        query += `\n          AND ad_group_ad.status = '${args.status_filter}'`;
      }

      query += `\n        ORDER BY metrics.impressions DESC\n        LIMIT ${limit}`;

      const rows = await client.query(cid, query);

      if (!rows.length) {
        return textResult("No ads found in this ad group.");
      }

      const lines = rows.map((r: any) => {
        const ad = r.adGroupAd?.ad || {};
        const status = r.adGroupAd?.status || "UNKNOWN";
        const policy = r.adGroupAd?.policySummary || {};
        const m = r.metrics || {};

        const headlines = ad.responsiveSearchAd?.headlines?.map((h: any) => h.text).slice(0, 3) || [];
        const headlinePreview = headlines.length ? headlines.join(" | ") : "—";
        const urls = ad.finalUrls?.join(", ") || "—";

        return [
          `**Ad ${ad.id}** (${ad.type || "UNKNOWN"})`,
          `  Status: ${status} | Approval: ${policy.approvalStatus || "—"} | Review: ${policy.reviewStatus || "—"}`,
          `  Headlines: ${headlinePreview}${headlines.length < (ad.responsiveSearchAd?.headlines?.length || 0) ? " ..." : ""}`,
          `  URLs: ${urls}`,
          `  Impressions: ${formatNumber(m.impressions || 0)} | Clicks: ${formatNumber(m.clicks || 0)} | CTR: ${formatPercent(m.ctr || 0)} | Cost: $${microsToMoney(m.costMicros || 0)}`,
        ].join("\n");
      });

      return textResult(
        `## Ads in Ad Group ${args.ad_group_id}\n\n${lines.join("\n\n")}\n\n_${rows.length} ad(s) returned._`
      );
    },
  },

  // ============================
  // get_ad
  // ============================
  {
    name: "get_ad",
    description:
      "Get full details for a specific ad including all headlines, descriptions, URLs, type, and policy status.",
    category: "ads",
    annotations: {
      title: "Get Ad Details",
      readOnlyHint: true,
      destructiveHint: false,
      idempotentHint: true,
      openWorldHint: false,
    },
    inputSchema: {
      type: "object",
      properties: {
        customer_id: {
          type: "string",
          description: "Google Ads customer ID (defaults to configured account).",
        },
        ad_group_id: {
          type: "string",
          description: "Ad group ID the ad belongs to.",
        },
        ad_id: {
          type: "string",
          description: "The ad ID to retrieve.",
        },
      },
      required: ["ad_group_id", "ad_id"],
    },
    _meta: {
      labels: {
        category: "ads",
        access: "read",
        complexity: "simple"
      }
    },
    handler: async (args, client) => {
      const cid = resolveCustomerId(args, client);

      const query = `
        SELECT
          ad_group_ad.ad.id,
          ad_group_ad.ad.type,
          ad_group_ad.ad.name,
          ad_group_ad.status,
          ad_group_ad.ad.final_urls,
          ad_group_ad.ad.final_mobile_urls,
          ad_group_ad.ad.tracking_url_template,
          ad_group_ad.ad.display_url,
          ad_group_ad.ad.responsive_search_ad.headlines,
          ad_group_ad.ad.responsive_search_ad.descriptions,
          ad_group_ad.ad.responsive_search_ad.path1,
          ad_group_ad.ad.responsive_search_ad.path2,
          ad_group_ad.policy_summary.approval_status,
          ad_group_ad.policy_summary.review_status,
          ad_group_ad.policy_summary.policy_topic_entries,
          ad_group_ad.ad.resource_name,
          ad_group.id,
          ad_group.name,
          campaign.id,
          campaign.name,
          metrics.impressions,
          metrics.clicks,
          metrics.cost_micros,
          metrics.conversions,
          metrics.ctr,
          metrics.average_cpc
        FROM ad_group_ad
        WHERE ad_group.id = ${args.ad_group_id}
          AND ad_group_ad.ad.id = ${args.ad_id}
          AND segments.date DURING LAST_30_DAYS
        LIMIT 1`;

      const rows = await client.query(cid, query);

      if (!rows.length) {
        return errorResult(`Ad ${args.ad_id} not found in ad group ${args.ad_group_id}.`);
      }

      const r = rows[0];
      const ad = r.adGroupAd?.ad || {};
      const status = r.adGroupAd?.status || "UNKNOWN";
      const policy = r.adGroupAd?.policySummary || {};
      const camp = r.campaign || {};
      const ag = r.adGroup || {};
      const m = r.metrics || {};

      const sections: string[] = [
        `## Ad Details: ${ad.id}`,
        "",
        `| Field | Value |`,
        `|-------|-------|`,
        `| Ad ID | ${ad.id} |`,
        `| Type | ${ad.type || "—"} |`,
        `| Status | ${status} |`,
        `| Campaign | ${camp.name || "—"} (ID: ${camp.id || "—"}) |`,
        `| Ad Group | ${ag.name || "—"} (ID: ${ag.id || "—"}) |`,
        `| Approval | ${policy.approvalStatus || "—"} |`,
        `| Review Status | ${policy.reviewStatus || "—"} |`,
        `| Final URLs | ${ad.finalUrls?.join(", ") || "—"} |`,
        `| Mobile URLs | ${ad.finalMobileUrls?.join(", ") || "—"} |`,
        `| Display URL | ${ad.displayUrl || "—"} |`,
        `| Tracking Template | ${ad.trackingUrlTemplate || "—"} |`,
      ];

      // RSA details
      const rsa = ad.responsiveSearchAd;
      if (rsa) {
        sections.push(
          "",
          `### Responsive Search Ad`,
          `**Path:** ${rsa.path1 || "—"}/${rsa.path2 || "—"}`,
          "",
          `**Headlines (${rsa.headlines?.length || 0}):**`
        );
        (rsa.headlines || []).forEach((h: any, i: number) => {
          const pin = h.pinnedField ? ` 📌 ${h.pinnedField}` : "";
          sections.push(`  ${i + 1}. ${h.text}${pin}`);
        });

        sections.push("", `**Descriptions (${rsa.descriptions?.length || 0}):**`);
        (rsa.descriptions || []).forEach((d: any, i: number) => {
          const pin = d.pinnedField ? ` 📌 ${d.pinnedField}` : "";
          sections.push(`  ${i + 1}. ${d.text}${pin}`);
        });
      }

      // Policy topics
      if (policy.policyTopicEntries?.length) {
        sections.push("", `### Policy Topics`);
        policy.policyTopicEntries.forEach((entry: any) => {
          sections.push(`- **${entry.type || "—"}**: ${entry.topic || "—"}`);
        });
      }

      // Performance
      sections.push(
        "",
        `### Performance (Last 30 Days)`,
        `| Metric | Value |`,
        `|--------|-------|`,
        `| Impressions | ${formatNumber(m.impressions || 0)} |`,
        `| Clicks | ${formatNumber(m.clicks || 0)} |`,
        `| CTR | ${formatPercent(m.ctr || 0)} |`,
        `| Cost | $${microsToMoney(m.costMicros || 0)} |`,
        `| Avg CPC | $${microsToMoney(m.averageCpc || 0)} |`,
        `| Conversions | ${formatNumber(m.conversions || 0)} |`
      );

      return textResult(sections.join("\n"));
    },
  },

  // ============================
  // create_responsive_search_ad
  // ============================
  {
    name: "create_responsive_search_ad",
    description:
      "Create a Responsive Search Ad (RSA) with up to 15 headlines and 4 descriptions. " +
      "⚠️ This creates a live ad that may start serving immediately if the ad group is enabled. " +
      "Google requires at least 3 headlines and 2 descriptions.",
    category: "ads",
    annotations: {
      title: "Create Responsive Search Ad",
      readOnlyHint: false,
      destructiveHint: true,
      idempotentHint: false,
      openWorldHint: false,
    },
    inputSchema: {
      type: "object",
      properties: {
        customer_id: {
          type: "string",
          description: "Google Ads customer ID (defaults to configured account).",
        },
        ad_group_id: {
          type: "string",
          description: "Ad group ID to create the ad in.",
        },
        headlines: {
          type: "array",
          items: { type: "string" },
          description: "Array of headline strings (3-15). Each headline max 30 characters.",
        },
        descriptions: {
          type: "array",
          items: { type: "string" },
          description: "Array of description strings (2-4). Each description max 90 characters.",
        },
        final_urls: {
          type: "array",
          items: { type: "string" },
          description: "Landing page URL(s). At least one required.",
        },
        path1: {
          type: "string",
          description: "First URL path text (max 15 chars, displayed after domain in ad).",
        },
        path2: {
          type: "string",
          description: "Second URL path text (max 15 chars, displayed after path1).",
        },
        status: {
          type: "string",
          description: "Initial ad status. Default: ENABLED.",
          enum: ["ENABLED", "PAUSED"],
        },
      },
      required: ["ad_group_id", "headlines", "descriptions", "final_urls"],
    },
    _meta: {
      labels: {
        category: "ads",
        access: "delete",
        complexity: "simple"
      }
    },
    handler: async (args, client) => {
      const cid = resolveCustomerId(args, client);

      // Validation
      if (!args.headlines?.length || args.headlines.length < 3) {
        return errorResult("At least 3 headlines are required for a Responsive Search Ad.");
      }
      if (args.headlines.length > 15) {
        return errorResult("Maximum 15 headlines allowed.");
      }
      if (!args.descriptions?.length || args.descriptions.length < 2) {
        return errorResult("At least 2 descriptions are required for a Responsive Search Ad.");
      }
      if (args.descriptions.length > 4) {
        return errorResult("Maximum 4 descriptions allowed.");
      }
      if (!args.final_urls?.length) {
        return errorResult("At least one final URL is required.");
      }

      // Check headline length limits
      const longHeadlines = args.headlines.filter((h: string) => h.length > 30);
      if (longHeadlines.length) {
        return errorResult(`Headlines must be 30 characters or fewer. Too long: "${longHeadlines[0]}" (${longHeadlines[0].length} chars)`);
      }

      // Check description length limits
      const longDescs = args.descriptions.filter((d: string) => d.length > 90);
      if (longDescs.length) {
        return errorResult(`Descriptions must be 90 characters or fewer. Too long: "${longDescs[0]}" (${longDescs[0].length} chars)`);
      }

      const responsiveSearchAd: Record<string, any> = {
        headlines: args.headlines.map((text: string) => ({ text })),
        descriptions: args.descriptions.map((text: string) => ({ text })),
      };

      if (args.path1) responsiveSearchAd.path1 = args.path1;
      if (args.path2) responsiveSearchAd.path2 = args.path2;

      const resource: Record<string, any> = {
        adGroup: `customers/${cid}/adGroups/${args.ad_group_id}`,
        status: args.status || "ENABLED",
        ad: {
          responsiveSearchAd,
          finalUrls: args.final_urls,
        },
      };

      const result = await client.mutate(cid, [
        {
          entity: "adGroupAd",
          operation: "create",
          resource,
        },
      ]);

      const createdName =
        result?.mutateOperationResponses?.[0]?.adGroupAdResult?.resourceName ||
        result?.results?.[0]?.resourceName ||
        "unknown";

      return textResult(
        `✅ Responsive Search Ad created successfully.\n\n` +
          `- Ad Group: ${args.ad_group_id}\n` +
          `- Headlines: ${args.headlines.length}\n` +
          `- Descriptions: ${args.descriptions.length}\n` +
          `- Final URL: ${args.final_urls[0]}\n` +
          `- Path: ${args.path1 || "—"}/${args.path2 || "—"}\n` +
          `- Status: ${args.status || "ENABLED"}\n` +
          `- Resource: \`${createdName}\``
      );
    },
  },

  // ============================
  // update_ad
  // ============================
  {
    name: "update_ad",
    description:
      "Update an existing ad's copy (headlines, descriptions, URLs, paths). " +
      "⚠️ This modifies a live ad — the ad will go through re-review after changes. " +
      "Note: Google Ads requires replacing the entire RSA asset set, not individual headlines.",
    category: "ads",
    annotations: {
      title: "Update Ad",
      readOnlyHint: false,
      destructiveHint: true,
      idempotentHint: true,
      openWorldHint: false,
    },
    inputSchema: {
      type: "object",
      properties: {
        customer_id: {
          type: "string",
          description: "Google Ads customer ID (defaults to configured account).",
        },
        ad_group_id: {
          type: "string",
          description: "Ad group ID the ad belongs to.",
        },
        ad_id: {
          type: "string",
          description: "The ad ID to update.",
        },
        headlines: {
          type: "array",
          items: { type: "string" },
          description: "Full replacement set of headlines (3-15). Replaces ALL existing headlines.",
        },
        descriptions: {
          type: "array",
          items: { type: "string" },
          description: "Full replacement set of descriptions (2-4). Replaces ALL existing descriptions.",
        },
        final_urls: {
          type: "array",
          items: { type: "string" },
          description: "New final URL(s).",
        },
        path1: {
          type: "string",
          description: "New first URL path text.",
        },
        path2: {
          type: "string",
          description: "New second URL path text.",
        },
      },
      required: ["ad_group_id", "ad_id"],
    },
    _meta: {
      labels: {
        category: "ads",
        access: "delete",
        complexity: "simple"
      }
    },
    handler: async (args, client) => {
      const cid = resolveCustomerId(args, client);
      const resourceName = `customers/${cid}/adGroupAds/${args.ad_group_id}~${args.ad_id}`;

      const adUpdate: Record<string, any> = {
        resourceName: `customers/${cid}/ads/${args.ad_id}`,
      };
      const updateFields: string[] = [];

      if (args.headlines?.length) {
        if (args.headlines.length < 3 || args.headlines.length > 15) {
          return errorResult("Headlines must be between 3 and 15.");
        }
        adUpdate.responsiveSearchAd = adUpdate.responsiveSearchAd || {};
        adUpdate.responsiveSearchAd.headlines = args.headlines.map((text: string) => ({ text }));
        updateFields.push("responsive_search_ad.headlines");
      }

      if (args.descriptions?.length) {
        if (args.descriptions.length < 2 || args.descriptions.length > 4) {
          return errorResult("Descriptions must be between 2 and 4.");
        }
        adUpdate.responsiveSearchAd = adUpdate.responsiveSearchAd || {};
        adUpdate.responsiveSearchAd.descriptions = args.descriptions.map((text: string) => ({ text }));
        updateFields.push("responsive_search_ad.descriptions");
      }

      if (args.final_urls?.length) {
        adUpdate.finalUrls = args.final_urls;
        updateFields.push("final_urls");
      }

      if (args.path1 !== undefined) {
        adUpdate.responsiveSearchAd = adUpdate.responsiveSearchAd || {};
        adUpdate.responsiveSearchAd.path1 = args.path1;
        updateFields.push("responsive_search_ad.path1");
      }

      if (args.path2 !== undefined) {
        adUpdate.responsiveSearchAd = adUpdate.responsiveSearchAd || {};
        adUpdate.responsiveSearchAd.path2 = args.path2;
        updateFields.push("responsive_search_ad.path2");
      }

      if (!updateFields.length) {
        return errorResult("No fields provided to update. Specify headlines, descriptions, final_urls, path1, or path2.");
      }

      // Ad updates use the ad resource directly via adGroupAd entity
      const resource: Record<string, any> = {
        resourceName,
        ad: adUpdate,
      };

      await client.mutate(cid, [
        {
          entity: "adGroupAd",
          operation: "update",
          resource,
          updateMask: updateFields.map((f) => `ad.${f}`).join(","),
        },
      ]);

      return textResult(
        `✅ Ad **${args.ad_id}** updated successfully.\n\n` +
          `Updated fields: ${updateFields.join(", ")}\n\n` +
          `_Note: The ad may go through re-review after changes._`
      );
    },
  },

  // ============================
  // pause_ad
  // ============================
  {
    name: "pause_ad",
    description:
      "Pause a specific ad, stopping it from serving. ⚠️ This immediately pauses a live ad.",
    category: "ads",
    annotations: {
      title: "Pause Ad",
      readOnlyHint: false,
      destructiveHint: true,
      idempotentHint: true,
      openWorldHint: false,
    },
    inputSchema: {
      type: "object",
      properties: {
        customer_id: {
          type: "string",
          description: "Google Ads customer ID (defaults to configured account).",
        },
        ad_group_id: {
          type: "string",
          description: "Ad group ID the ad belongs to.",
        },
        ad_id: {
          type: "string",
          description: "The ad ID to pause.",
        },
      },
      required: ["ad_group_id", "ad_id"],
    },
    _meta: {
      labels: {
        category: "ads",
        access: "delete",
        complexity: "simple"
      }
    },
    handler: async (args, client) => {
      const cid = resolveCustomerId(args, client);
      const resourceName = `customers/${cid}/adGroupAds/${args.ad_group_id}~${args.ad_id}`;

      await client.mutate(cid, [
        {
          entity: "adGroupAd",
          operation: "update",
          resource: { resourceName, status: "PAUSED" },
          updateMask: "status",
        },
      ]);

      return textResult(`⏸️ Ad **${args.ad_id}** in ad group ${args.ad_group_id} has been paused.`);
    },
  },
];

export default tools;
