/**
 * Budget & Optimization Tools (Tier 2 - Advanced)
 * Tools for budget recommendations, bid suggestions, and spend pacing
 */

import { z } from "zod";
import type { ToolRegistry } from "../server.js";
import type {
  ToolCategory,
  LoadTier,
} from "../types/meta-api.js";

export const category: ToolCategory = "budget";
export const tier: LoadTier = "advanced";

// ============================================================
// Shared Schemas
// ============================================================

const targetingSpecSchema = z
  .record(z.unknown())
  .describe("Targeting specification object (JSON)");

const campaignObjectiveSchema = z
  .enum([
    "OUTCOME_APP_PROMOTION",
    "OUTCOME_AWARENESS",
    "OUTCOME_ENGAGEMENT",
    "OUTCOME_LEADS",
    "OUTCOME_SALES",
    "OUTCOME_TRAFFIC",
  ])
  .describe("Campaign objective");

const optimizationGoalSchema = z
  .enum([
    "NONE",
    "APP_INSTALLS",
    "AD_RECALL_LIFT",
    "ENGAGED_USERS",
    "EVENT_RESPONSES",
    "IMPRESSIONS",
    "LEAD_GENERATION",
    "LINK_CLICKS",
    "OFFSITE_CONVERSIONS",
    "PAGE_LIKES",
    "POST_ENGAGEMENT",
    "QUALITY_CALL",
    "QUALITY_LEAD",
    "REACH",
    "LANDING_PAGE_VIEWS",
    "VALUE",
    "THRUPLAY",
    "CONVERSATIONS",
    "DERIVED_EVENTS",
  ])
  .describe("Optimization goal");

const billingEventSchema = z
  .enum([
    "APP_INSTALLS",
    "CLICKS",
    "IMPRESSIONS",
    "LINK_CLICKS",
    "NONE",
    "OFFER_CLAIMS",
    "PAGE_LIKES",
    "POST_ENGAGEMENT",
    "THRUPLAY",
  ])
  .describe("Billing event");

// ============================================================
// Tool Registration
// ============================================================

export function registerBudgetTools(registry: ToolRegistry): void {
  const client = registry.getClient();

  // ============================================================
  // get_budget_recommendations
  // ============================================================
  registry.registerTool({
    name: "get_budget_recommendations",
    description: "Get recommended budget for a campaign objective and targeting",
    inputSchema: z.object({
      account_id: z
        .string()
        .describe("Ad account ID (with or without act_ prefix)"),
      campaign_objective: campaignObjectiveSchema.describe(
        "Campaign objective to optimize for"
      ),
      targeting_spec: targetingSpecSchema.describe("Targeting specification"),
      optimization_goal: optimizationGoalSchema
        .optional()
        .describe("Specific optimization goal"),
    }),
    annotations: {
      title: "Get Budget Recommendations",
      readOnlyHint: true,
      destructiveHint: false,
      idempotentHint: true,
      openWorldHint: false,
    },
    _meta: {
      labels: {
        category: "budgets",
        access: "read",
        complexity: "simple"
      }
    },
    handler: async (args) => {
      try {
        const params = args as {
          account_id: string;
          campaign_objective: string;
          targeting_spec: Record<string, unknown>;
          optimization_goal?: string;
        };
        
        const accountId = params.account_id.startsWith("act_")
          ? params.account_id
          : `act_${params.account_id}`;

        const queryParams: Record<string, string> = {
          objective: params.campaign_objective,
          targeting_spec: JSON.stringify(params.targeting_spec),
        };

        if (params.optimization_goal) {
          queryParams.optimization_goal = params.optimization_goal;
        }

        const response = await client.get<{
          data: Array<{
            min_budget: number;
            max_budget: number;
            recommended_budget: number;
            currency: string;
          }>;
        }>(`${accountId}/minimum_budgets`, queryParams);

        const budget = response.data[0];

        const result = {
          account_id: accountId,
          objective: params.campaign_objective,
          budget_recommendations: {
            minimum_daily_budget: budget?.min_budget || 0,
            maximum_daily_budget: budget?.max_budget || 0,
            recommended_daily_budget: budget?.recommended_budget || 0,
            currency: budget?.currency || "USD",
          },
          targeting_spec: params.targeting_spec,
        };

        return {
          content: [{
            type: 'text',
            text: JSON.stringify(result, null, 2),
          }],
        };
      } catch (error) {
        return {
          content: [{
            type: 'text',
            text: `Error: ${error instanceof Error ? error.message : String(error)}`,
          }],
          isError: true,
        };
      }
    },
  });

  // ============================================================
  // get_bid_suggestions
  // ============================================================
  registry.registerTool({
    name: "get_bid_suggestions",
    description: "Get recommended bid amount for targeting and optimization goal",
    inputSchema: z.object({
      account_id: z
        .string()
        .describe("Ad account ID (with or without act_ prefix)"),
      targeting_spec: targetingSpecSchema.describe("Targeting specification"),
      optimization_goal: optimizationGoalSchema.describe("Optimization goal"),
      billing_event: billingEventSchema
        .optional()
        .describe("Billing event (default: based on optimization goal)"),
    }),
    annotations: {
      title: "Get Bid Suggestions",
      readOnlyHint: true,
      destructiveHint: false,
      idempotentHint: true,
      openWorldHint: false,
    },
    _meta: {
      labels: {
        category: "budgets",
        access: "read",
        complexity: "simple"
      }
    },
    handler: async (args) => {
      try {
        const params = args as {
          account_id: string;
          targeting_spec: Record<string, unknown>;
          optimization_goal: string;
          billing_event?: string;
        };
        
        const accountId = params.account_id.startsWith("act_")
          ? params.account_id
          : `act_${params.account_id}`;

        const queryParams: Record<string, string> = {
          targeting_spec: JSON.stringify(params.targeting_spec),
          optimization_goal: params.optimization_goal,
        };

        if (params.billing_event) {
          queryParams.billing_event = params.billing_event;
        }

        const response = await client.get<{
          data: Array<{
            bid_estimate: {
              min: number;
              median: number;
              max: number;
            };
            currency: string;
          }>;
        }>(`${accountId}/bidsuggestion`, queryParams);

        const suggestion = response.data[0];

        const result = {
          account_id: accountId,
          optimization_goal: params.optimization_goal,
          bid_suggestions: {
            minimum_bid: suggestion?.bid_estimate?.min || 0,
            median_bid: suggestion?.bid_estimate?.median || 0,
            maximum_bid: suggestion?.bid_estimate?.max || 0,
            currency: suggestion?.currency || "USD",
          },
          billing_event: params.billing_event,
          targeting_spec: params.targeting_spec,
        };

        return {
          content: [{
            type: 'text',
            text: JSON.stringify(result, null, 2),
          }],
        };
      } catch (error) {
        return {
          content: [{
            type: 'text',
            text: `Error: ${error instanceof Error ? error.message : String(error)}`,
          }],
          isError: true,
        };
      }
    },
  });

  // ============================================================
  // get_spend_pacing
  // ============================================================
  registry.registerTool({
    name: "get_spend_pacing",
    description: "Check budget pacing status to see if spending is ahead, behind, or on track",
    inputSchema: z.object({
      object_id: z
        .string()
        .describe("Campaign ID or Ad Set ID to check pacing for"),
    }),
    annotations: {
      title: "Get Spend Pacing",
      readOnlyHint: true,
      destructiveHint: false,
      idempotentHint: true,
      openWorldHint: false,
    },
    _meta: {
      labels: {
        category: "budgets",
        access: "read",
        complexity: "simple"
      }
    },
    handler: async (args) => {
      try {
        const params = args as { object_id: string };
        
        // Get object details with budget and spend info
        const response = await client.get<{
          id: string;
          name: string;
          daily_budget?: string;
          lifetime_budget?: string;
          budget_remaining: string;
          start_time?: string;
          end_time?: string;
        }>(params.object_id, {
          fields:
            "id,name,daily_budget,lifetime_budget,budget_remaining,start_time,end_time,spent",
        });

        // Get recent insights to calculate pacing
        const insightsResponse = await client.get<{
          data: Array<{
            spend: string;
            date_start: string;
            date_stop: string;
          }>;
        }>(`${params.object_id}/insights`, {
          date_preset: "last_7d",
          time_increment: "1",
          fields: "spend",
        });

        const dailySpend = insightsResponse.data || [];
        const avgDailySpend =
          dailySpend.length > 0
            ? dailySpend.reduce((sum, day) => sum + parseFloat(day.spend), 0) /
              dailySpend.length
            : 0;

        const budgetRemaining = parseFloat(response.budget_remaining || "0");
        const dailyBudget = response.daily_budget
          ? parseFloat(response.daily_budget)
          : null;
        const lifetimeBudget = response.lifetime_budget
          ? parseFloat(response.lifetime_budget)
          : null;

        // Calculate pacing status
        let pacingStatus = "on_track";
        let daysRemaining = 0;
        let projectedTotalSpend = 0;

        if (response.end_time) {
          const endDate = new Date(response.end_time);
          const now = new Date();
          daysRemaining = Math.max(
            0,
            Math.ceil((endDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24))
          );
          projectedTotalSpend = avgDailySpend * daysRemaining + budgetRemaining;

          if (lifetimeBudget) {
            const spendRate = avgDailySpend / (lifetimeBudget / daysRemaining);
            if (spendRate > 1.2) pacingStatus = "ahead";
            else if (spendRate < 0.8) pacingStatus = "behind";
          }
        }

        const result = {
          object_id: params.object_id,
          name: response.name,
          budget: {
            daily_budget: dailyBudget,
            lifetime_budget: lifetimeBudget,
            budget_remaining: budgetRemaining,
          },
          pacing: {
            status: pacingStatus,
            average_daily_spend_last_7d: avgDailySpend,
            days_remaining: daysRemaining,
            projected_total_spend: projectedTotalSpend,
          },
          daily_spend_history: dailySpend.map((day) => ({
            date: day.date_start,
            spend: parseFloat(day.spend),
          })),
        };

        return {
          content: [{
            type: 'text',
            text: JSON.stringify(result, null, 2),
          }],
        };
      } catch (error) {
        return {
          content: [{
            type: 'text',
            text: `Error: ${error instanceof Error ? error.message : String(error)}`,
          }],
          isError: true,
        };
      }
    },
  });

  // ============================================================
  // get_delivery_estimate
  // ============================================================
  registry.registerTool({
    name: "get_delivery_estimate",
    description: "Estimate daily reach and impressions for a given budget and targeting",
    inputSchema: z.object({
      account_id: z
        .string()
        .describe("Ad account ID (with or without act_ prefix)"),
      targeting_spec: targetingSpecSchema.describe("Targeting specification"),
      optimization_goal: optimizationGoalSchema.describe("Optimization goal"),
      daily_budget: z
        .number()
        .describe("Daily budget amount in account currency"),
    }),
    annotations: {
      title: "Get Delivery Estimate",
      readOnlyHint: true,
      destructiveHint: false,
      idempotentHint: true,
      openWorldHint: false,
    },
    _meta: {
      labels: {
        category: "budgets",
        access: "read",
        complexity: "simple"
      }
    },
    handler: async (args) => {
      try {
        const params = args as {
          account_id: string;
          targeting_spec: Record<string, unknown>;
          optimization_goal: string;
          daily_budget: number;
        };
        
        const accountId = params.account_id.startsWith("act_")
          ? params.account_id
          : `act_${params.account_id}`;

        const queryParams: Record<string, string> = {
          targeting_spec: JSON.stringify(params.targeting_spec),
          optimization_goal: params.optimization_goal,
          daily_budget: params.daily_budget.toString(),
        };

        const response = await client.get<{
          data: Array<{
            daily_outcomes_curve: Array<{
              spend: number;
              reach: number;
              impressions: number;
              actions: number;
            }>;
            estimate_dau: number;
            estimate_mau: number;
          }>;
        }>(`${accountId}/delivery_estimate`, queryParams);

        const estimate = response.data[0];
        const curvePoint = estimate?.daily_outcomes_curve?.find(
          (point) => point.spend >= params.daily_budget
        );

        const result = {
          account_id: accountId,
          daily_budget: params.daily_budget,
          optimization_goal: params.optimization_goal,
          delivery_estimate: {
            estimated_daily_reach: curvePoint?.reach || 0,
            estimated_daily_impressions: curvePoint?.impressions || 0,
            estimated_daily_actions: curvePoint?.actions || 0,
            estimated_daily_active_audience: estimate?.estimate_dau || 0,
            estimated_monthly_audience: estimate?.estimate_mau || 0,
          },
          targeting_spec: params.targeting_spec,
        };

        return {
          content: [{
            type: 'text',
            text: JSON.stringify(result, null, 2),
          }],
        };
      } catch (error) {
        return {
          content: [{
            type: 'text',
            text: `Error: ${error instanceof Error ? error.message : String(error)}`,
          }],
          isError: true,
        };
      }
    },
  });

  // ============================================================
  // get_account_spend_limit
  // ============================================================
  registry.registerTool({
    name: "get_account_spend_limit",
    description: "Get account spend caps, billing thresholds, and payment information",
    inputSchema: z.object({
      account_id: z
        .string()
        .describe("Ad account ID (with or without act_ prefix)"),
    }),
    annotations: {
      title: "Get Account Spend Limit",
      readOnlyHint: true,
      destructiveHint: false,
      idempotentHint: true,
      openWorldHint: false,
    },
    _meta: {
      labels: {
        category: "budgets",
        access: "read",
        complexity: "simple"
      }
    },
    handler: async (args) => {
      try {
        const params = args as { account_id: string };
        
        const accountId = params.account_id.startsWith("act_")
          ? params.account_id
          : `act_${params.account_id}`;

        const response = await client.get<{
          id: string;
          name: string;
          account_id: string;
          spend_cap: string;
          amount_spent: string;
          balance: string;
          currency: string;
          adspaymentcycle?: {
            data: Array<{
              threshold_amount: string;
              billing_event: string;
            }>;
          };
          funding_source_details?: {
            display_string: string;
            type: number;
          };
          min_campaign_group_spend_cap?: string;
          min_daily_budget?: number;
        }>(accountId, {
          fields:
            "id,name,account_id,spend_cap,amount_spent,balance,currency,adspaymentcycle,funding_source_details,min_campaign_group_spend_cap,min_daily_budget",
        });

        const spendCap = parseFloat(response.spend_cap || "0");
        const amountSpent = parseFloat(response.amount_spent || "0");
        const balance = parseFloat(response.balance || "0");
        const spendCapRemaining = spendCap > 0 ? spendCap - amountSpent : null;
        const spendCapUsagePercent =
          spendCap > 0 ? (amountSpent / spendCap) * 100 : 0;

        const result = {
          account_id: accountId,
          account_name: response.name,
          currency: response.currency,
          spend_cap: {
            limit: spendCap,
            amount_spent: amountSpent,
            remaining: spendCapRemaining,
            usage_percentage: spendCapUsagePercent,
          },
          balance: balance,
          billing: {
            payment_cycles:
              response.adspaymentcycle?.data.map((cycle) => ({
                threshold_amount: parseFloat(cycle.threshold_amount),
                billing_event: cycle.billing_event,
              })) || [],
            funding_source: response.funding_source_details?.display_string,
            funding_source_type: response.funding_source_details?.type,
          },
          limits: {
            min_campaign_group_spend_cap:
              response.min_campaign_group_spend_cap || null,
            min_daily_budget: response.min_daily_budget || null,
          },
        };

        return {
          content: [{
            type: 'text',
            text: JSON.stringify(result, null, 2),
          }],
        };
      } catch (error) {
        return {
          content: [{
            type: 'text',
            text: `Error: ${error instanceof Error ? error.message : String(error)}`,
          }],
          isError: true,
        };
      }
    },
  });
}
