/**
 * Intelligent A/B Testing — Extended types and schemas.
 * Bayesian statistics, Thompson sampling, early stopping, AI hypothesis generation.
 */

import { z } from 'zod';

// ─────────────────────────────────────────────────────────────────────────────
// Enums and Constants
// ─────────────────────────────────────────────────────────────────────────────

export type ExperimentStatus = 'draft' | 'running' | 'paused' | 'stopped' | 'completed';

export type AllocationStrategy = 'random' | 'thompson' | 'epsilon_greedy' | 'ucb';

export type MetricType = 'conversion' | 'count' | 'duration' | 'revenue' | 'custom';

export type MetricAggregation = 'sum' | 'mean' | 'count' | 'unique';

export type MetricDirection = 'increase' | 'decrease';

// ─────────────────────────────────────────────────────────────────────────────
// Core Types
// ─────────────────────────────────────────────────────────────────────────────

export interface ExperimentVariant {
  id: string; // var_<uuid>
  key: string; // e.g., 'control', 'variant_a'
  name: string;
  description?: string;
  isControl: boolean;
  flagConfig: Record<string, unknown>; // Arbitrary config payload
  currentAllocationPercent: number; // 0–100%, dynamic for bandit
}

export interface PrimaryMetric {
  name: string;
  type: MetricType;
  eventName: string; // Telemetry event to track
  aggregation: MetricAggregation;
  direction: MetricDirection; // Is higher better?
  minimumDetectableEffect: number; // % change we want to detect
}

export interface SecondaryMetric {
  name: string;
  type: MetricType;
  eventName: string;
}

export interface TargetingConfig {
  platforms?: string[]; // ios, android, web
  appVersions?: { min: string; max?: string };
  regions?: string[];
  userSegments?: string[]; // pro, free, enterprise
  userProperties?: Record<string, string | number | boolean>;
}

export interface GuardrailsConfig {
  minSampleSizePerVariant: number; // Default: 100
  maxDurationDays: number; // Safety limit, default: 30
  autoStopEnabled: boolean;
  winnerThreshold: number; // % probability to auto-stop, default: 95
  requireApprovalFor: 'none' | 'revenue' | 'all';
}

// ─────────────────────────────────────────────────────────────────────────────
// Document Types (Cosmos DB)
// ─────────────────────────────────────────────────────────────────────────────

export interface ExperimentDoc {
  id: string; // exp_<uuid>
  productId: string; // partition key

  // Experiment definition
  name: string;
  description: string;
  hypothesis: string;
  aiGeneratedHypothesis?: boolean;

  // Status lifecycle
  status: ExperimentStatus;

  // Variants
  controlVariantId: string;
  variantIds: string[];

  // Configuration
  allocationStrategy: AllocationStrategy;
  targetPercent: number; // % of eligible traffic

  // Audience targeting
  targeting: TargetingConfig;

  // Metrics
  primaryMetric: PrimaryMetric;
  secondaryMetrics: SecondaryMetric[];

  // Guardrails
  guardrails: GuardrailsConfig;

  // Scheduling
  startAt?: string;
  endAt?: string;

  // Stats (denormalized for fast reads)
  totalParticipants: number;
  totalEvents: number;

  // Timestamps
  createdAt: string;
  updatedAt: string;
  startedAt?: string;
  completedAt?: string;

  // TTL for completed experiments (2 years)
  ttl?: number;
}

export interface VariantDoc {
  id: string; // var_<uuid>
  experimentId: string; // partition key

  // Variant definition
  name: string;
  description?: string;
  isControl: boolean;

  // Feature flag configuration
  flagConfig: Record<string, unknown>;

  // Traffic allocation (dynamic for bandit)
  currentAllocationPercent: number;

  // Statistics (real-time computed)
  stats: {
    participants: number;
    events: number;
    primaryMetricValue: number;
    primaryMetricStdDev?: number;
    conversions?: number;
    conversionRate?: number;

    // Bayesian posterior parameters
    betaAlpha?: number;
    betaBeta?: number;
    gammaShape?: number;
    gammaScale?: number;
  };

  // Bayesian results
  bayesianResults?: {
    probabilityBeatsControl: number;
    probabilityBeatsAll: number;
    expectedLiftPercent: number;
    expectedLoss: number;
    credibleInterval: {
      lower: number;
      mean: number;
      upper: number;
    };
  };

  createdAt: string;
  updatedAt: string;
}

export interface ExperimentAssignmentDoc {
  id: string; // ea_<uuid>
  userId: string; // partition key

  experimentId: string;
  variantId: string;

  // Assignment metadata
  assignedAt: string;
  firstExposedAt?: string;

  // Context at assignment
  assignmentContext: {
    platform: string;
    appVersion: string;
    osVersion: string;
    deviceModel?: string;
    region?: string;
  };

  // Events attributed
  eventCount: number;
  lastEventAt?: string;

  // TTL: Remove after experiment completes + analysis period
  ttl?: number;
}

export interface ExperimentEventDoc {
  id: string; // ee_<uuid>
  experimentId: string; // partition key
  timestamp: string; // Sort key for time-series

  // Attribution
  userId: string;
  variantId: string;
  assignmentId: string;

  // Event details
  metricName: string;
  metricType: MetricType;
  value: number;

  // Conversion tracking
  converted: boolean;

  // Context
  eventMetadata?: Record<string, unknown>;

  // Denormalized for filtering
  platform: string;
  appVersion: string;

  // TTL
  ttl?: number;
}

export interface ExperimentMetricDoc {
  id: string; // em_<uuid>
  experimentId: string; // partition key

  metricName: string;
  variantId: string;

  // Aggregated values
  count: number;
  sum: number;
  mean: number;
  stdDev: number;
  min: number;
  max: number;

  // Conversion-specific
  conversions: number;
  conversionRate: number;

  // Last updated
  updatedAt: string;
}

export interface ExperimentResult {
  experimentId: string;
  status: 'in_progress' | 'winner_found' | 'no_winner' | 'stopped';

  // Overall stats
  totalParticipants: number;
  totalEvents: number;
  daysRunning: number;

  // Winner info
  winnerVariantId?: string;
  winnerProbability?: number;

  // Variant results
  variantResults: Array<{
    variantId: string;
    variantName: string;
    isControl: boolean;
    participants: number;
    primaryMetricValue: number;
    probabilityBeatsControl: number;
    expectedLiftPercent: number;
    credibleInterval: { lower: number; mean: number; upper: number };
  }>;

  // Statistical summary
  statisticalSummary: {
    probabilityAnyBeatsControl: number;
    expectedLossIfShipped: number;
    recommendedAction: 'ship' | 'rollback' | 'continue' | 'stop';
  };

  // Early stopping
  earlyStopped: boolean;
  stopReason?: string;

  generatedAt: string;
}

// ─────────────────────────────────────────────────────────────────────────────
// AI Hypothesis Types
// ─────────────────────────────────────────────────────────────────────────────

export interface HypothesisInput {
  featureName: string;
  adoptionRate: number;
  baselineRate: number;
  segmentData: Record<string, number>;
  feedbackSamples: string[];
  competitorFeatures?: string[];
}

export interface GeneratedHypothesis {
  primary: string;
  alternatives: string[];
  expectedEffectSize: number;
  successMetric: string;
  riskAssessment: 'low' | 'medium' | 'high';
  impactScore: number; // 0–100
  difficultyScore: number; // 0–100
  powerPrediction: number; // Statistical power 0–100
}

export interface ExperimentSuggestion {
  id: string;
  hypothesis: GeneratedHypothesis;
  suggestedVariants: Array<{ name: string; description: string }>;
  suggestedMetrics: PrimaryMetric[];
  suggestedDuration: number;
  suggestedSampleSize: number;
  priority: number;
  aiGenerated: boolean;
  createdAt: string;
}

// ─────────────────────────────────────────────────────────────────────────────
// Zod Schemas
// ─────────────────────────────────────────────────────────────────────────────

const VariantInputSchema = z.object({
  key: z
    .string()
    .min(1)
    .regex(/^[a-z0-9_]+$/),
  name: z.string().min(1).max(200),
  description: z.string().default(''),
  isControl: z.boolean().default(false),
  flagConfig: z.record(z.unknown()).default({}),
});

const PrimaryMetricSchema = z.object({
  name: z.string().min(1),
  type: z.enum(['conversion', 'count', 'duration', 'revenue', 'custom']),
  eventName: z.string().min(1),
  aggregation: z.enum(['sum', 'mean', 'count', 'unique']),
  direction: z.enum(['increase', 'decrease']),
  minimumDetectableEffect: z.number().min(0.01).max(100).default(5),
});

const SecondaryMetricSchema = z.object({
  name: z.string().min(1),
  type: z.enum(['conversion', 'count', 'duration', 'revenue', 'custom']),
  eventName: z.string().min(1),
});

const TargetingSchema = z.object({
  platforms: z.array(z.enum(['ios', 'android', 'web'])).optional(),
  appVersions: z
    .object({
      min: z.string(),
      max: z.string().optional(),
    })
    .optional(),
  regions: z.array(z.string()).optional(),
  userSegments: z.array(z.string()).optional(),
  userProperties: z.record(z.union([z.string(), z.number(), z.boolean()])).optional(),
});

const GuardrailsSchema = z.object({
  minSampleSizePerVariant: z.number().int().min(10).default(100),
  maxDurationDays: z.number().int().min(1).max(90).default(30),
  autoStopEnabled: z.boolean().default(true),
  winnerThreshold: z.number().int().min(80).max(99).default(95),
  requireApprovalFor: z.enum(['none', 'revenue', 'all']).default('none'),
});

export const CreateExperimentSchema = z.object({
  name: z.string().min(1).max(200),
  description: z.string().default(''),
  hypothesis: z.string().min(1),
  variants: z.array(VariantInputSchema).min(2).max(10),
  allocationStrategy: z.enum(['random', 'thompson', 'epsilon_greedy', 'ucb']).default('random'),
  targetPercent: z.number().int().min(1).max(100).default(100),
  targeting: TargetingSchema.default({}),
  primaryMetric: PrimaryMetricSchema,
  secondaryMetrics: z.array(SecondaryMetricSchema).default([]),
  guardrails: GuardrailsSchema.default({}),
  startAt: z.string().datetime().optional(),
});

export const UpdateExperimentSchema = z.object({
  name: z.string().min(1).max(200).optional(),
  description: z.string().optional(),
  hypothesis: z.string().optional(),
  status: z.enum(['draft', 'running', 'paused', 'stopped', 'completed']).optional(),
  targetPercent: z.number().int().min(1).max(100).optional(),
  targeting: TargetingSchema.optional(),
  guardrails: GuardrailsSchema.optional(),
});

export const TrackEventSchema = z.object({
  experimentId: z.string(),
  userId: z.string(),
  metricName: z.string(),
  metricType: z.enum(['conversion', 'count', 'duration', 'revenue', 'custom']),
  value: z.number(),
  converted: z.boolean().default(true),
  eventMetadata: z.record(z.unknown()).optional(),
  platform: z.string().default('unknown'),
  appVersion: z.string().default('unknown'),
});

export const AdjustAllocationSchema = z.object({
  variantId: z.string(),
  newAllocationPercent: z.number().int().min(0).max(100),
});

// ─────────────────────────────────────────────────────────────────────────────
// Type Exports
// ─────────────────────────────────────────────────────────────────────────────

export type CreateExperimentInput = z.infer<typeof CreateExperimentSchema>;
export type UpdateExperimentInput = z.infer<typeof UpdateExperimentSchema>;
export type TrackEventInput = z.infer<typeof TrackEventSchema>;
export type AdjustAllocationInput = z.infer<typeof AdjustAllocationSchema>;
