/**
 * Feature flags types — production-grade feature management system.
 *
 * Supports:
 * - Multi-variate flags (boolean, string, number, JSON)
 * - Targeting rules with attribute matching (AND/OR)
 * - Percentage rollouts (deterministic via FNV-1a)
 * - Platform / region / OS version targeting
 * - User segment targeting with CRUD
 * - Prerequisite flag dependencies
 * - Scheduled flag activation/deactivation
 * - Full audit trail (before/after snapshots)
 * - SSE streaming for real-time updates
 */

import { z } from 'zod';

// ── Flag value types ────────────────────────────────────────────────────────

export type FlagType = 'boolean' | 'string' | 'number' | 'json';

/** A single variation for multi-variate flags. */
export interface FlagVariation {
  key: string;
  value: boolean | string | number | Record<string, unknown>;
  description?: string;
}

// ── Targeting ───────────────────────────────────────────────────────────────

export type TargetingOperator =
  | 'eq'
  | 'neq'
  | 'contains'
  | 'not_contains'
  | 'starts_with'
  | 'ends_with'
  | 'gt'
  | 'gte'
  | 'lt'
  | 'lte'
  | 'in'
  | 'not_in'
  | 'semver_gt'
  | 'semver_gte'
  | 'semver_lt'
  | 'semver_lte'
  | 'semver_eq'
  | 'regex';

export interface TargetingClause {
  attribute: string;
  operator: TargetingOperator;
  values: (string | number | boolean)[];
}

export interface TargetingRule {
  id: string;
  description?: string;
  clauses: TargetingClause[];
  variationKey: string;
  rolloutPercentage: number;
}

export interface OsVersionRange {
  platform: string;
  minVersion?: string;
  maxVersion?: string;
}

// ── Scheduling ──────────────────────────────────────────────────────────────

export interface FlagSchedule {
  enableAt?: string;
  disableAt?: string;
  gradualRollout?: {
    startPercentage: number;
    endPercentage: number;
    startAt: string;
    endAt: string;
  };
}

// ── Prerequisites ───────────────────────────────────────────────────────────

export interface FlagPrerequisite {
  flagKey: string;
  variationKey: string;
}

// ── Flag Document ───────────────────────────────────────────────────────────

export interface FeatureFlagDoc {
  id: string;
  productId: string;
  key: string;
  flagType: FlagType;
  enabled: boolean;
  archived: boolean;
  description: string;
  tags: string[];

  variations: FlagVariation[];
  defaultVariation: string;
  offVariation: string;

  platforms: string[];
  regions: string[];
  osVersions: OsVersionRange[];
  segments: string[];
  targetingRules: TargetingRule[];
  individualTargets: Record<string, string>;
  prerequisites: FlagPrerequisite[];
  percentage: number;

  schedule?: FlagSchedule;

  createdAt: string;
  updatedAt: string;
  createdBy?: string;
  updatedBy?: string;
  version: number;
}

// ── Segment Document ────────────────────────────────────────────────────────

export interface SegmentDoc {
  id: string;
  productId: string;
  key: string;
  name: string;
  description: string;
  rules: TargetingClause[];
  includedUsers: string[];
  excludedUsers: string[];
  createdAt: string;
  updatedAt: string;
}

// ── Audit Log ───────────────────────────────────────────────────────────────

export interface FlagAuditDoc {
  id: string;
  productId: string;
  flagKey: string;
  action: 'created' | 'updated' | 'deleted' | 'toggled' | 'archived' | 'scheduled' | 'kill_switch';
  actor: string;
  before?: Partial<FeatureFlagDoc>;
  after?: Partial<FeatureFlagDoc>;
  changes?: string[];
  timestamp: string;
}

// ── Evaluation Context ──────────────────────────────────────────────────────

export interface EvaluationContext {
  userId?: string;
  platform?: string;
  region?: string;
  osVersion?: string;
  appVersion?: string;
  email?: string;
  custom?: Record<string, string | number | boolean>;
}

export interface EvaluationResult {
  key: string;
  value: boolean | string | number | Record<string, unknown>;
  variationKey: string;
  reason:
    | 'off'
    | 'prerequisite_failed'
    | 'individual_target'
    | 'rule_match'
    | 'segment_match'
    | 'percentage_rollout'
    | 'default'
    | 'schedule_inactive'
    | 'error';
}

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

export const OsVersionRangeSchema = z.object({
  platform: z.string().min(1),
  minVersion: z.string().optional(),
  maxVersion: z.string().optional(),
});

const TargetingClauseSchema = z.object({
  attribute: z.string().min(1),
  operator: z.enum([
    'eq',
    'neq',
    'contains',
    'not_contains',
    'starts_with',
    'ends_with',
    'gt',
    'gte',
    'lt',
    'lte',
    'in',
    'not_in',
    'semver_gt',
    'semver_gte',
    'semver_lt',
    'semver_lte',
    'semver_eq',
    'regex',
  ]),
  values: z.array(z.union([z.string(), z.number(), z.boolean()])).min(1),
});

const TargetingRuleSchema = z.object({
  id: z.string().min(1),
  description: z.string().optional(),
  clauses: z.array(TargetingClauseSchema).min(1),
  variationKey: z.string().min(1),
  rolloutPercentage: z.number().min(0).max(100).default(100),
});

const FlagVariationSchema = z.object({
  key: z.string().min(1),
  value: z.union([z.boolean(), z.string(), z.number(), z.record(z.unknown())]),
  description: z.string().optional(),
});

const FlagScheduleSchema = z.object({
  enableAt: z.string().datetime().optional(),
  disableAt: z.string().datetime().optional(),
  gradualRollout: z
    .object({
      startPercentage: z.number().min(0).max(100),
      endPercentage: z.number().min(0).max(100),
      startAt: z.string().datetime(),
      endAt: z.string().datetime(),
    })
    .optional(),
});

const FlagPrerequisiteSchema = z.object({
  flagKey: z.string().min(1),
  variationKey: z.string().min(1),
});

export const CreateFlagSchema = z.object({
  key: z
    .string()
    .min(1)
    .regex(/^[a-z0-9_.]+$/),
  flagType: z.enum(['boolean', 'string', 'number', 'json']).default('boolean'),
  enabled: z.boolean().default(true),
  description: z.string().default(''),
  tags: z.array(z.string()).default([]),

  variations: z.array(FlagVariationSchema).optional(),
  defaultVariation: z.string().optional(),
  offVariation: z.string().optional(),

  platforms: z.array(z.string()).default([]),
  regions: z.array(z.string()).default([]),
  osVersions: z.array(OsVersionRangeSchema).default([]),
  segments: z.array(z.string()).default([]),
  targetingRules: z.array(TargetingRuleSchema).default([]),
  individualTargets: z.record(z.string()).default({}),
  prerequisites: z.array(FlagPrerequisiteSchema).default([]),
  percentage: z.number().min(0).max(100).default(100),

  schedule: FlagScheduleSchema.optional(),
});

export const UpdateFlagSchema = z.object({
  enabled: z.boolean().optional(),
  description: z.string().optional(),
  tags: z.array(z.string()).optional(),

  variations: z.array(FlagVariationSchema).optional(),
  defaultVariation: z.string().optional(),
  offVariation: z.string().optional(),

  platforms: z.array(z.string()).optional(),
  regions: z.array(z.string()).optional(),
  osVersions: z.array(OsVersionRangeSchema).optional(),
  segments: z.array(z.string()).optional(),
  targetingRules: z.array(TargetingRuleSchema).optional(),
  individualTargets: z.record(z.string()).optional(),
  prerequisites: z.array(FlagPrerequisiteSchema).optional(),
  percentage: z.number().min(0).max(100).optional(),
  archived: z.boolean().optional(),

  schedule: FlagScheduleSchema.optional(),
});

export const CreateSegmentSchema = z.object({
  key: z
    .string()
    .min(1)
    .regex(/^[a-z0-9_]+$/),
  name: z.string().min(1),
  description: z.string().default(''),
  rules: z.array(TargetingClauseSchema).default([]),
  includedUsers: z.array(z.string()).default([]),
  excludedUsers: z.array(z.string()).default([]),
});

export const UpdateSegmentSchema = z.object({
  name: z.string().min(1).optional(),
  description: z.string().optional(),
  rules: z.array(TargetingClauseSchema).optional(),
  includedUsers: z.array(z.string()).optional(),
  excludedUsers: z.array(z.string()).optional(),
});

export const EvaluateSchema = z.object({
  userId: z.string().optional(),
  platform: z.string().optional(),
  region: z.string().optional(),
  osVersion: z.string().optional(),
  appVersion: z.string().optional(),
  email: z.string().optional(),
  custom: z.record(z.union([z.string(), z.number(), z.boolean()])).optional(),
});

export type CreateFlagInput = z.infer<typeof CreateFlagSchema>;
export type UpdateFlagInput = z.infer<typeof UpdateFlagSchema>;
export type CreateSegmentInput = z.infer<typeof CreateSegmentSchema>;
export type UpdateSegmentInput = z.infer<typeof UpdateSegmentSchema>;
export type EvaluateInput = z.infer<typeof EvaluateSchema>;
