/**
 * A/B Testing & Experiments Tools (Tier 2 - Advanced)
 * Tools for creating and analyzing split tests
 */

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

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

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

const budgetSplitSchema = z
  .union([
    z.literal("even"),
    z
      .array(
        z.object({
          campaign_id: z.string(),
          percentage: z.number().min(0).max(100),
        })
      )
      .describe("Custom budget allocation by campaign"),
  ])
  .describe("Budget split: 'even' or custom percentages");

const experimentStatusSchema = z
  .enum(["ACTIVE", "PAUSED", "DELETED", "SCHEDULED", "COMPLETED"])
  .optional()
  .describe("Filter experiments by status");

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

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

  // ============================================================
  // create_experiment
  // ============================================================
  registry.registerTool({
    name: "create_experiment",
    description: "Set up an A/B test experiment to compare campaign variants",
    inputSchema: z.object({
      account_id: z
        .string()
        .describe("Ad account ID (with or without act_ prefix)"),
      name: z.string().describe("Name for the experiment"),
      description: z.string().optional().describe("Experiment description"),
      control_campaign_id: z
        .string()
        .describe("Campaign ID to use as control (variant A)"),
      treatment_campaign_ids: z
        .array(z.string())
        .describe("Campaign IDs for treatment variants (B, C, etc.)"),
      objective_metric: z
        .string()
        .describe(
          "Metric to optimize (e.g., 'cost_per_result', 'ctr', 'reach')"
        ),
      start_time: z
        .string()
        .describe("Experiment start time (ISO 8601 format)"),
      end_time: z
        .string()
        .describe("Experiment end time (ISO 8601 format)"),
      budget_split: budgetSplitSchema.optional(),
    }),
    annotations: {
      title: "Create A/B Test Experiment",
      readOnlyHint: false,
      destructiveHint: false,
      idempotentHint: false,
      openWorldHint: true,
    },
    _meta: {
      labels: {
        category: "experiments",
        access: "read",
        complexity: "simple"
      }
    },
    handler: async (args) => {
      try {
        const params = args as {
          account_id: string;
          name: string;
          description?: string;
          control_campaign_id: string;
          treatment_campaign_ids: string[];
          objective_metric: string;
          start_time: string;
          end_time: string;
          budget_split?:
            | "even"
            | Array<{ campaign_id: string; percentage: number }>;
        };

        const accountId = params.account_id.startsWith("act_")
          ? params.account_id
          : `act_${params.account_id}`;

        const experimentData: Record<string, unknown> = {
          name: params.name,
          type: "SPLIT_TEST",
          control_id: params.control_campaign_id,
          treatment_ids: params.treatment_campaign_ids,
          objective: params.objective_metric,
          start_time: params.start_time,
          end_time: params.end_time,
        };

        if (params.description) {
          experimentData.description = params.description;
        }

        // Handle budget split
        if (params.budget_split === "even") {
          const totalVariants =
            1 + (params.treatment_campaign_ids?.length || 0);
          const evenPercentage = 100 / totalVariants;
          experimentData.budget_distribution = [
            { id: params.control_campaign_id, percentage: evenPercentage },
            ...(params.treatment_campaign_ids || []).map((id: string) => ({
              id,
              percentage: evenPercentage,
            })),
          ];
        } else if (Array.isArray(params.budget_split)) {
          experimentData.budget_distribution = params.budget_split;
        }

        const response = (await client.post(
          `${accountId}/experiments`,
          experimentData
        )) as { id: string };

        const result = {
          experiment_id: response.id,
          name: params.name,
          control_campaign_id: params.control_campaign_id,
          treatment_campaign_ids: params.treatment_campaign_ids,
          objective_metric: params.objective_metric,
          start_time: params.start_time,
          end_time: params.end_time,
          message: "A/B test experiment created successfully",
        };

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

  // ============================================================
  // get_experiment_results
  // ============================================================
  registry.registerTool({
    name: "get_experiment_results",
    description:
      "Get experiment results with statistical significance and winner declaration",
    inputSchema: z.object({
      experiment_id: z.string().describe("Experiment ID to retrieve results"),
    }),
    annotations: {
      title: "Get Experiment Results",
      readOnlyHint: true,
      destructiveHint: false,
      idempotentHint: true,
      openWorldHint: false,
    },
    _meta: {
      labels: {
        category: "experiments",
        access: "read",
        complexity: "simple"
      }
    },
    handler: async (args) => {
      try {
        const params = args as {
          experiment_id: string;
        };

        // Get experiment details
        const experiment = (await client.get(params.experiment_id, {
          fields:
            "id,name,description,type,status,start_time,end_time,control_ids,treatment_ids,winner_declaring_time,winner_campaign_id",
        })) as Experiment;

        // Get results for each variant
        const allVariantIds = [
          ...(experiment.control_ids || []),
          ...(experiment.treatment_ids || []),
        ];

        const variantResults = await Promise.all(
          allVariantIds.map(async (campaignId: string) => {
            const insights = (await client.get(`${campaignId}/insights`, {
              date_preset: "maximum",
              fields:
                "impressions,clicks,spend,reach,ctr,cpm,cpc,actions,cost_per_action_type",
            })) as {
              data: Array<{
                impressions: string;
                clicks: string;
                spend: string;
                reach: string;
                ctr: string;
                cpm: string;
                cpc: string;
                actions?: Array<{ action_type: string; value: string }>;
              }>;
            };

            const data = insights.data[0] || {};

            return {
              campaign_id: campaignId,
              is_control: experiment.control_ids?.includes(campaignId),
              metrics: {
                impressions: parseInt(data.impressions || "0", 10),
                clicks: parseInt(data.clicks || "0", 10),
                spend: parseFloat(data.spend || "0"),
                reach: parseInt(data.reach || "0", 10),
                ctr: parseFloat(data.ctr || "0"),
                cpm: parseFloat(data.cpm || "0"),
                cpc: parseFloat(data.cpc || "0"),
              },
              actions: data.actions || [],
            };
          })
        );

        // Calculate statistical significance (simplified)
        const controlResult = variantResults.find((r) => r.is_control);
        const treatmentResults = variantResults.filter((r) => !r.is_control);

        const resultsWithSignificance = treatmentResults.map((treatment) => {
          // Simple lift calculation
          const controlCtr = controlResult?.metrics.ctr || 0;
          const treatmentCtr = treatment.metrics.ctr || 0;
          const ctrLift =
            controlCtr > 0
              ? ((treatmentCtr - controlCtr) / controlCtr) * 100
              : 0;

          const controlCpc = controlResult?.metrics.cpc || 0;
          const treatmentCpc = treatment.metrics.cpc || 0;
          const cpcImprovement =
            controlCpc > 0
              ? ((controlCpc - treatmentCpc) / controlCpc) * 100
              : 0;

          // Simplified confidence calculation (would need more data for proper stats)
          const controlImpressions = controlResult?.metrics.impressions || 0;
          const treatmentImpressions = treatment.metrics.impressions || 0;
          const sampleSizeScore = Math.min(
            100,
            ((controlImpressions + treatmentImpressions) / 10000) * 100
          );

          return {
            campaign_id: treatment.campaign_id,
            metrics: treatment.metrics,
            vs_control: {
              ctr_lift_percent: ctrLift,
              cpc_improvement_percent: cpcImprovement,
              spend_difference:
                treatment.metrics.spend - (controlResult?.metrics.spend || 0),
            },
            confidence_score: sampleSizeScore,
            is_winner: experiment.winner_campaign_id === treatment.campaign_id,
          };
        });

        const result = {
          experiment_id: params.experiment_id,
          name: experiment.name,
          status: experiment.status,
          start_time: experiment.start_time,
          end_time: experiment.end_time,
          control: {
            campaign_id: controlResult?.campaign_id,
            metrics: controlResult?.metrics,
          },
          treatments: resultsWithSignificance,
          winner: {
            declared: !!experiment.winner_campaign_id,
            campaign_id: experiment.winner_campaign_id,
            declared_at: experiment.winner_declaring_time,
          },
          note: "Confidence scores are simplified. For production use, implement proper statistical significance testing (chi-square, t-test, etc.)",
        };

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

  // ============================================================
  // list_experiments
  // ============================================================
  registry.registerTool({
    name: "list_experiments",
    description: "List all A/B test experiments for an account",
    inputSchema: z.object({
      account_id: z
        .string()
        .describe("Ad account ID (with or without act_ prefix)"),
      status: experimentStatusSchema,
      limit: z.number().optional().describe("Maximum number of results"),
    }),
    annotations: {
      title: "List Experiments",
      readOnlyHint: true,
      destructiveHint: false,
      idempotentHint: true,
      openWorldHint: false,
    },
    _meta: {
      labels: {
        category: "experiments",
        access: "read",
        complexity: "simple"
      }
    },
    handler: async (args) => {
      try {
        const params = args as {
          account_id: string;
          status?: string;
          limit?: number;
        };

        const accountId = params.account_id.startsWith("act_")
          ? params.account_id
          : `act_${params.account_id}`;

        const queryParams: Record<string, string> = {
          fields:
            "id,name,description,type,status,start_time,end_time,control_ids,treatment_ids,winner_campaign_id,winner_declaring_time",
        };

        if (params.limit) {
          queryParams.limit = params.limit.toString();
        }

        if (params.status) {
          queryParams.filtering = JSON.stringify([
            {
              field: "status",
              operator: "EQUAL",
              value: params.status,
            },
          ]);
        }

        const experiments = (await client.getAllPages(
          `${accountId}/experiments`,
          queryParams
        )) as Experiment[];

        const result = {
          experiments: experiments.map((exp) => ({
            id: exp.id,
            name: exp.name,
            description: exp.description,
            status: exp.status,
            type: exp.type,
            start_time: exp.start_time,
            end_time: exp.end_time,
            control_count: exp.control_ids?.length || 0,
            treatment_count: exp.treatment_ids?.length || 0,
            has_winner: !!exp.winner_campaign_id,
            winner_campaign_id: exp.winner_campaign_id,
          })),
          account_id: accountId,
          total_count: experiments.length,
        };

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