// ============================================
// CONVERSION TRACKING TOOLS
// ============================================

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

// ---- list_conversion_actions ----

async function listConversionActions(
  args: Record<string, any>,
  client: GoogleAdsClient
): Promise<ToolResult> {
  const customerId = args.customer_id || client.getCustomerId();
  if (!customerId) {
    return {
      content: [{ type: "text", text: "Error: customer_id is required." }],
      isError: true,
    };
  }

  const query = `
    SELECT
      conversion_action.id,
      conversion_action.name,
      conversion_action.type,
      conversion_action.status,
      conversion_action.category,
      conversion_action.counting_type,
      conversion_action.tag_snippets
    FROM conversion_action
    ORDER BY conversion_action.name
  `;

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

  if (!rows.length) {
    return {
      content: [{ type: "text", text: "No conversion actions found in this account." }],
    };
  }

  const lines = rows.map((row) => {
    const ca = row.conversionAction;
    const snippetInfo =
      ca.tagSnippets?.length
        ? `\n    Tag snippets: ${ca.tagSnippets.length} configured`
        : "";
    return [
      `  • **${ca.name}** (ID: ${ca.id})`,
      `    Type: ${ca.type} | Status: ${ca.status}`,
      `    Category: ${ca.category} | Counting: ${ca.countingType}${snippetInfo}`,
    ].join("\n");
  });

  return {
    content: [
      {
        type: "text",
        text: `## Conversion Actions (${rows.length})\n\n${lines.join("\n\n")}`,
      },
    ],
  };
}

// ---- get_conversion_metrics ----

async function getConversionMetrics(
  args: Record<string, any>,
  client: GoogleAdsClient
): Promise<ToolResult> {
  const customerId = args.customer_id || client.getCustomerId();
  if (!customerId) {
    return {
      content: [{ type: "text", text: "Error: customer_id is required." }],
      isError: true,
    };
  }

  const dateRange = args.date_range || "LAST_30_DAYS";
  const groupBy = args.campaign_id ? "ad_group" : "campaign";

  let query: string;

  if (args.campaign_id) {
    // Drill into ad groups within a campaign
    query = `
      SELECT
        ad_group.id,
        ad_group.name,
        campaign.id,
        campaign.name,
        metrics.conversions,
        metrics.conversions_value,
        metrics.cost_per_conversion,
        metrics.cost_micros,
        metrics.clicks,
        metrics.impressions,
        metrics.conversions_from_interactions_rate
      FROM ad_group
      WHERE campaign.id = ${args.campaign_id}
        AND segments.date DURING ${dateRange}
        AND metrics.impressions > 0
      ORDER BY metrics.conversions DESC
    `;
  } else {
    // Campaign-level conversion metrics
    query = `
      SELECT
        campaign.id,
        campaign.name,
        campaign.status,
        metrics.conversions,
        metrics.conversions_value,
        metrics.cost_per_conversion,
        metrics.cost_micros,
        metrics.clicks,
        metrics.impressions,
        metrics.conversions_from_interactions_rate
      FROM campaign
      WHERE segments.date DURING ${dateRange}
        AND metrics.impressions > 0
      ORDER BY metrics.conversions DESC
    `;
  }

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

  if (!rows.length) {
    return {
      content: [
        {
          type: "text",
          text: `No conversion data found for ${dateRange}.${args.campaign_id ? ` Campaign ID: ${args.campaign_id}` : ""}`,
        },
      ],
    };
  }

  // Compute totals
  let totalConversions = 0;
  let totalValue = 0;
  let totalCost = 0;

  const lines = rows.map((row) => {
    const m = row.metrics;
    const conversions = m.conversions || 0;
    const convValue = m.conversionsValue || 0;
    const costPerConv = m.costPerConversion ? microsToMoney(m.costPerConversion) : "N/A";
    const convRate = m.conversionsFromInteractionsRate
      ? (m.conversionsFromInteractionsRate * 100).toFixed(2) + "%"
      : "N/A";

    totalConversions += conversions;
    totalValue += convValue;
    totalCost += parseInt(m.costMicros || "0");

    if (groupBy === "ad_group") {
      const ag = row.adGroup;
      return [
        `  • **${ag.name}** (ID: ${ag.id})`,
        `    Conversions: ${conversions.toFixed(1)} | Value: $${convValue.toFixed(2)}`,
        `    Cost/Conv: $${costPerConv} | Conv Rate: ${convRate}`,
        `    Spend: $${microsToMoney(m.costMicros)} | Clicks: ${formatNumber(m.clicks)}`,
      ].join("\n");
    } else {
      const c = row.campaign;
      return [
        `  • **${c.name}** [${c.status}] (ID: ${c.id})`,
        `    Conversions: ${conversions.toFixed(1)} | Value: $${convValue.toFixed(2)}`,
        `    Cost/Conv: $${costPerConv} | Conv Rate: ${convRate}`,
        `    Spend: $${microsToMoney(m.costMicros)} | Clicks: ${formatNumber(m.clicks)}`,
      ].join("\n");
    }
  });

  const avgCostPerConv =
    totalConversions > 0
      ? "$" + (totalCost / 1_000_000 / totalConversions).toFixed(2)
      : "N/A";

  const summary = [
    `**Total Conversions:** ${totalConversions.toFixed(1)}`,
    `**Total Value:** $${totalValue.toFixed(2)}`,
    `**Total Spend:** $${microsToMoney(totalCost)}`,
    `**Avg Cost/Conversion:** ${avgCostPerConv}`,
  ].join(" | ");

  const heading = args.campaign_id
    ? `## Conversion Metrics by Ad Group (Campaign ${args.campaign_id})`
    : `## Conversion Metrics by Campaign`;

  return {
    content: [
      {
        type: "text",
        text: `${heading}\n📅 ${dateRange}\n\n${summary}\n\n${lines.join("\n\n")}`,
      },
    ],
  };
}

// ---- create_conversion_action ----

async function createConversionAction(
  args: Record<string, any>,
  client: GoogleAdsClient
): Promise<ToolResult> {
  const customerId = args.customer_id || client.getCustomerId();
  if (!customerId) {
    return {
      content: [{ type: "text", text: "Error: customer_id is required." }],
      isError: true,
    };
  }

  const { name, type, category, counting_type } = args;

  if (!name || !type || !category) {
    return {
      content: [
        {
          type: "text",
          text: "Error: name, type, and category are required fields.",
        },
      ],
      isError: true,
    };
  }

  const resource: Record<string, any> = {
    name,
    type,
    category,
    countingType: counting_type || "ONE_PER_CLICK",
    status: "ENABLED",
  };

  // Set sensible defaults based on type
  if (type === "WEBPAGE") {
    resource.viewThroughLookbackWindowDays = 1;
    resource.clickThroughLookbackWindowDays = 30;
  } else if (type === "PHONE_CALL") {
    resource.phoneCallDurationSeconds = 60;
  }

  // Include optional value settings if provided
  if (args.default_value !== undefined) {
    resource.valueSettings = {
      defaultValue: args.default_value,
      alwaysUseDefaultValue: args.always_use_default_value ?? false,
    };
  }

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

  const createdResource =
    result.mutateOperationResponses?.[0]?.conversionActionResult?.resourceName ||
    "unknown";

  return {
    content: [
      {
        type: "text",
        text: [
          `## ✅ Conversion Action Created`,
          ``,
          `**Name:** ${name}`,
          `**Type:** ${type}`,
          `**Category:** ${category}`,
          `**Counting:** ${counting_type || "ONE_PER_CLICK"}`,
          `**Resource:** \`${createdResource}\``,
          ``,
          type === "WEBPAGE"
            ? `> 💡 Use \`list_conversion_actions\` to retrieve the tag snippet for installation.`
            : `> Conversion action is now active and ready to track.`,
        ].join("\n"),
      },
    ],
  };
}

// ---- upload_offline_conversions ----

async function uploadOfflineConversions(
  args: Record<string, any>,
  client: GoogleAdsClient
): Promise<ToolResult> {
  const customerId = args.customer_id || client.getCustomerId();
  if (!customerId) {
    return {
      content: [{ type: "text", text: "Error: customer_id is required." }],
      isError: true,
    };
  }

  const conversions: Array<{
    gclid: string;
    conversion_action: string;
    conversion_date_time: string;
    conversion_value?: number;
    currency_code?: string;
  }> = args.conversions;

  if (!Array.isArray(conversions) || conversions.length === 0) {
    return {
      content: [
        {
          type: "text",
          text: 'Error: conversions array is required and must not be empty. Each entry needs: gclid, conversion_action (resource name like "customers/123/conversionActions/456"), conversion_date_time (format: "2025-01-15 12:30:00-05:00").',
        },
      ],
      isError: true,
    };
  }

  // Validate each conversion entry
  const errors: string[] = [];
  conversions.forEach((conv, i) => {
    if (!conv.gclid) errors.push(`Conversion ${i + 1}: missing gclid`);
    if (!conv.conversion_action)
      errors.push(`Conversion ${i + 1}: missing conversion_action`);
    if (!conv.conversion_date_time)
      errors.push(`Conversion ${i + 1}: missing conversion_date_time`);
  });

  if (errors.length > 0) {
    return {
      content: [
        {
          type: "text",
          text: `Validation errors:\n${errors.map((e) => `  • ${e}`).join("\n")}`,
        },
      ],
      isError: true,
    };
  }

  // Build the click conversion upload request
  // Uses the ConversionUploadService REST endpoint
  const cid = customerId.replace(/-/g, "");
  const uploadConversions = conversions.map((conv) => {
    const entry: Record<string, any> = {
      gclid: conv.gclid,
      conversionAction: conv.conversion_action.includes("/")
        ? conv.conversion_action
        : `customers/${cid}/conversionActions/${conv.conversion_action}`,
      conversionDateTime: conv.conversion_date_time,
    };
    if (conv.conversion_value !== undefined) {
      entry.conversionValue = conv.conversion_value;
    }
    if (conv.currency_code) {
      entry.currencyCode = conv.currency_code;
    }
    return entry;
  });

  // The upload endpoint requires direct REST call through the client.
  // We use the GoogleAdsClient interface — the upload_click_conversions
  // endpoint is: POST /customers/{cid}:uploadClickConversions
  //
  // Since our client interface exposes query() and mutate(), we call
  // the underlying request method via mutate-style operations.
  // The REST API endpoint is:
  //   POST https://googleads.googleapis.com/v21/customers/{cid}:uploadClickConversions
  //
  // We need to go through the client's request pipeline. The cleanest
  // approach: use the mutate interface with a special entity type that
  // the server can route, or call the API directly.
  //
  // For this implementation, we construct the operations so the MCP
  // server routes them through the conversion upload endpoint.

  // Use the mutate pathway — the server will need to handle
  // conversionUpload operations specially, but we structure it cleanly
  const result = await client.mutate(customerId, [
    {
      entity: "conversionUpload",
      operation: "create",
      resource: {
        conversions: uploadConversions,
        partialFailure: true,
      },
    },
  ]);

  // Parse the response
  const successCount = conversions.length;
  const partialErrors = result.partialFailureError?.details || [];
  const failedCount = partialErrors.length;
  const actualSuccess = successCount - failedCount;

  const lines = [
    `## Offline Conversion Upload Results`,
    ``,
    `**Submitted:** ${successCount} conversions`,
    `**Succeeded:** ${actualSuccess}`,
    failedCount > 0 ? `**Failed:** ${failedCount}` : null,
    ``,
  ].filter(Boolean) as string[];

  if (failedCount > 0) {
    lines.push(`### Errors:`);
    partialErrors.forEach((err: any, i: number) => {
      lines.push(
        `  • Entry ${i + 1}: ${err.message || JSON.stringify(err)}`
      );
    });
    lines.push(``);
  }

  lines.push(
    `> 💡 Offline conversions typically take up to 24 hours to appear in reports.`
  );

  return {
    content: [{ type: "text", text: lines.join("\n") }],
    isError: failedCount === successCount,
  };
}

// ============================================
// TOOL DEFINITIONS
// ============================================

const tools: ToolDefinition[] = [
  {
    name: "list_conversion_actions",
    description:
      "List all configured conversion actions in the account, including type, status, category, and counting type. " +
      "Shows tag snippet availability for web-based conversions.",
    category: "conversions",
    annotations: {
      title: "List Conversion Actions",
      readOnlyHint: true,
      destructiveHint: false,
      idempotentHint: true,
      openWorldHint: false,
    },
    inputSchema: {
      type: "object",
      properties: {
        customer_id: {
          type: "string",
          description:
            "Google Ads customer ID (without dashes). Defaults to configured account.",
        },
      },
    },
    _meta: {
      labels: {
        category: "conversions",
        access: "read",
        complexity: "simple"
      }
    },
    handler: listConversionActions,
  },
  {
    name: "get_conversion_metrics",
    description:
      "Get conversion performance data aggregated by campaign or by ad group (when campaign_id is specified). " +
      "Includes conversions, conversion value, cost per conversion, and conversion rate.",
    category: "conversions",
    annotations: {
      title: "Get Conversion Metrics",
      readOnlyHint: true,
      destructiveHint: false,
      idempotentHint: true,
      openWorldHint: false,
    },
    inputSchema: {
      type: "object",
      properties: {
        customer_id: {
          type: "string",
          description:
            "Google Ads customer ID (without dashes). Defaults to configured account.",
        },
        campaign_id: {
          type: "string",
          description:
            "Campaign ID to drill into ad group-level conversion data. Omit for campaign-level overview.",
        },
        date_range: {
          type: "string",
          description:
            "GAQL date range predicate. Examples: LAST_7_DAYS, LAST_30_DAYS, LAST_90_DAYS, THIS_MONTH, LAST_MONTH.",
          enum: [
            "TODAY",
            "YESTERDAY",
            "LAST_7_DAYS",
            "LAST_14_DAYS",
            "LAST_30_DAYS",
            "LAST_90_DAYS",
            "THIS_MONTH",
            "LAST_MONTH",
            "THIS_QUARTER",
            "LAST_QUARTER",
          ],
          default: "LAST_30_DAYS",
        },
      },
    },
    _meta: {
      labels: {
        category: "conversions",
        access: "read",
        complexity: "simple"
      }
    },
    handler: getConversionMetrics,
  },
  {
    name: "create_conversion_action",
    description:
      "Create a new conversion action for tracking. Supports webpage, phone call, import (offline), " +
      "and other conversion types. Configure category and counting behavior.",
    category: "conversions",
    annotations: {
      title: "Create Conversion Action",
      readOnlyHint: false,
      destructiveHint: true,
      idempotentHint: false,
      openWorldHint: false,
    },
    inputSchema: {
      type: "object",
      properties: {
        customer_id: {
          type: "string",
          description:
            "Google Ads customer ID (without dashes). Defaults to configured account.",
        },
        name: {
          type: "string",
          description: "Name for the conversion action (e.g., 'Purchase', 'Lead Form Submit').",
        },
        type: {
          type: "string",
          description: "Type of conversion to track.",
          enum: [
            "WEBPAGE",
            "PHONE_CALL",
            "IMPORT",
            "CLICK_TO_CALL",
            "APP_INSTALL",
            "APP_ENGAGEMENT",
            "STORE_VISIT",
            "STORE_SALE",
          ],
        },
        category: {
          type: "string",
          description: "Conversion category for reporting classification.",
          enum: [
            "PURCHASE",
            "LEAD",
            "SIGN_UP",
            "PAGE_VIEW",
            "ADD_TO_CART",
            "BEGIN_CHECKOUT",
            "SUBSCRIBE_PAID",
            "PHONE_CALL_LEAD",
            "IMPORTED_LEAD",
            "SUBMIT_LEAD_FORM",
            "BOOK_APPOINTMENT",
            "REQUEST_QUOTE",
            "GET_DIRECTIONS",
            "OUTBOUND_CLICK",
            "CONTACT",
            "ENGAGEMENT",
            "STORE_VISIT",
            "STORE_SALE",
            "DEFAULT",
          ],
        },
        counting_type: {
          type: "string",
          description:
            "How to count conversions. ONE_PER_CLICK counts at most one conversion per click. " +
            "MANY_PER_CLICK counts every conversion.",
          enum: ["ONE_PER_CLICK", "MANY_PER_CLICK"],
          default: "ONE_PER_CLICK",
        },
        default_value: {
          type: "number",
          description: "Default monetary value for each conversion (optional).",
        },
        always_use_default_value: {
          type: "boolean",
          description:
            "If true, always use the default value even when a specific value is provided. Default: false.",
        },
      },
      required: ["name", "type", "category"],
    },
    _meta: {
      labels: {
        category: "conversions",
        access: "delete",
        complexity: "simple"
      }
    },
    handler: createConversionAction,
  },
  {
    name: "upload_offline_conversions",
    description:
      "Upload offline click conversions (e.g., CRM sales, phone leads that closed) back to Google Ads. " +
      "Requires GCLID, conversion action resource name, and timestamp. " +
      "Conversions take up to 24 hours to reflect in reports.",
    category: "conversions",
    annotations: {
      title: "Upload Offline Conversions",
      readOnlyHint: false,
      destructiveHint: true,
      idempotentHint: false,
      openWorldHint: false,
    },
    inputSchema: {
      type: "object",
      properties: {
        customer_id: {
          type: "string",
          description:
            "Google Ads customer ID (without dashes). Defaults to configured account.",
        },
        conversions: {
          type: "array",
          description: "Array of offline conversions to upload.",
          items: {
            type: "object",
            properties: {
              gclid: {
                type: "string",
                description: "Google Click ID captured at the time of the ad click.",
              },
              conversion_action: {
                type: "string",
                description:
                  'Conversion action resource name (e.g., "customers/123/conversionActions/456") or just the action ID.',
              },
              conversion_date_time: {
                type: "string",
                description:
                  'Timestamp of the conversion in "yyyy-MM-dd HH:mm:ssZ" format (e.g., "2025-01-15 12:30:00-05:00").',
              },
              conversion_value: {
                type: "number",
                description: "Monetary value of the conversion (optional).",
              },
              currency_code: {
                type: "string",
                description: 'ISO 4217 currency code (e.g., "USD"). Defaults to account currency.',
              },
            },
            required: ["gclid", "conversion_action", "conversion_date_time"],
          },
        },
      },
      required: ["conversions"],
    },
    _meta: {
      labels: {
        category: "conversions",
        access: "delete",
        complexity: "simple"
      }
    },
    handler: uploadOfflineConversions,
  },
];

export default tools;
