// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import { z } from 'zod';
import { ManifestSchema } from './manifest.zod';

/**
 * # Package Upgrade Protocol
 * 
 * Defines the complete lifecycle for upgrading installed packages,
 * including pre-upgrade analysis, snapshot/backup, execution, validation,
 * and rollback capabilities.
 * 
 * ## Architecture Alignment
 * - **Salesforce**: Managed Package upgrade with push upgrades and subscriber control
 * - **ServiceNow**: Update Sets with preview, commit, and back-out support
 * - **Helm**: Helm upgrade with rollback to previous release
 * - **Kubernetes**: Rolling update with readiness probes and automatic rollback
 * 
 * ## Upgrade Flow
 * ```
 * 1. PreCheck    → Validate compatibility, check dependencies
 * 2. Plan        → Generate upgrade plan with metadata diff
 * 3. Snapshot    → Backup current state (metadata + customizations)
 * 4. Execute     → Apply new package metadata with 3-way merge
 * 5. Validate    → Run post-upgrade health checks
 * 6. Commit      → Finalize upgrade (or Rollback on failure)
 * ```
 */

// ==========================================
// Upgrade Plan & Analysis
// ==========================================

/**
 * Metadata Change Type
 * Type of change detected between package versions.
 */
import { lazySchema } from '../shared/lazy-schema';
export const MetadataChangeTypeSchema = lazySchema(() => z.enum([
  'added',    // New metadata item added in new version
  'modified', // Existing metadata item modified
  'removed',  // Metadata item removed in new version
  'renamed',  // Metadata item renamed
]).describe('Type of metadata change between package versions'));

/**
 * Metadata Diff Item
 * Describes a single metadata change between two package versions.
 */
export const MetadataDiffItemSchema = lazySchema(() => z.object({
  /** Metadata type (e.g. "object", "view", "flow") */
  type: z.string().describe('Metadata type'),

  /** Metadata name */
  name: z.string().describe('Metadata name'),

  /** Type of change */
  changeType: MetadataChangeTypeSchema.describe('Category of metadata modification (added, modified, removed, or renamed)'),

  /** Whether this change has potential conflicts with customizations */
  hasConflict: z.boolean().default(false)
    .describe('Whether this change may conflict with customizations'),

  /** Human-readable summary of the change */
  summary: z.string().optional().describe('Human-readable change summary'),

  /** Previous name (for renames) */
  previousName: z.string().optional().describe('Previous name if renamed'),
}).describe('Single metadata change between package versions'));

/**
 * Upgrade Impact Level
 * Indicates the severity of impact the upgrade will have.
 */
export const UpgradeImpactLevelSchema = lazySchema(() => z.enum([
  'none',     // No impact, seamless upgrade
  'low',      // Minor changes, no user action needed
  'medium',   // Some changes that may affect workflows
  'high',     // Significant changes, user review recommended
  'critical', // Breaking changes, manual intervention required
]).describe('Severity of upgrade impact'));

/**
 * Upgrade Plan Schema
 * The analysis result before executing an upgrade.
 * Generated by comparing old version metadata with new version metadata.
 */
export const UpgradePlanSchema = lazySchema(() => z.object({
  /** Package being upgraded */
  packageId: z.string().describe('Package identifier'),

  /** Current installed version */
  fromVersion: z.string().describe('Currently installed version'),

  /** Target version to upgrade to */
  toVersion: z.string().describe('Target upgrade version'),

  /** Overall impact level */
  impactLevel: UpgradeImpactLevelSchema.describe('Severity assessment from none (seamless) to critical (breaking changes)'),

  /** List of all metadata changes between versions */
  changes: z.array(MetadataDiffItemSchema).describe('All metadata changes'),

  /** Number of customer customizations that may be affected */
  affectedCustomizations: z.number().int().min(0).default(0)
    .describe('Count of customizations that may be affected'),

  /** Whether any migration scripts need to run */
  requiresMigration: z.boolean().default(false)
    .describe('Whether data migration scripts are needed'),

  /** Migration script paths (relative to package root) */
  migrationScripts: z.array(z.string()).optional()
    .describe('Paths to migration scripts'),

  /** Dependencies that also need upgrading */
  dependencyUpgrades: z.array(z.object({
    packageId: z.string(),
    fromVersion: z.string(),
    toVersion: z.string(),
  })).optional().describe('Dependent packages that also need upgrading'),

  /** Estimated upgrade duration in seconds */
  estimatedDuration: z.number().int().min(0).optional()
    .describe('Estimated upgrade duration in seconds'),

  /** Human-readable summary */
  summary: z.string().optional().describe('Human-readable upgrade summary'),
}).describe('Upgrade analysis plan generated before execution'));

// ==========================================
// Upgrade Snapshot (Pre-Upgrade Backup)
// ==========================================

/**
 * Upgrade Snapshot Schema
 * Captures the complete state before an upgrade for rollback capability.
 */
export const UpgradeSnapshotSchema = lazySchema(() => z.object({
  /** Snapshot ID (UUID) */
  id: z.string().describe('Snapshot identifier'),

  /** Package being upgraded */
  packageId: z.string().describe('Package identifier'),

  /** Version being upgraded from */
  fromVersion: z.string().describe('Version before upgrade'),

  /** Version being upgraded to */
  toVersion: z.string().describe('Target upgrade version'),

  /** Tenant ID */
  tenantId: z.string().optional().describe('Tenant identifier'),

  /** Complete manifest of the old package version */
  previousManifest: ManifestSchema.describe('Complete manifest of the previous package version'),

  /**
   * Snapshot of all metadata records owned by this package.
   * Stored as array of { type, name, metadata } tuples.
   */
  metadataSnapshot: z.array(z.object({
    type: z.string(),
    name: z.string(),
    metadata: z.record(z.string(), z.unknown()),
  })).describe('Snapshot of all package metadata'),

  /**
   * Snapshot of all customer customizations (overlays) for this package's metadata.
   */
  customizationSnapshot: z.array(z.record(z.string(), z.unknown())).optional()
    .describe('Snapshot of customer customizations'),

  /** When the snapshot was created */
  createdAt: z.string().datetime().describe('Snapshot creation timestamp'),

  /** Expiry time for snapshot cleanup */
  expiresAt: z.string().datetime().optional().describe('Snapshot expiry timestamp'),
}).describe('Pre-upgrade state snapshot for rollback capability'));

// ==========================================
// Upgrade Request/Response
// ==========================================

/**
 * Upgrade Package Request
 */
export const UpgradePackageRequestSchema = lazySchema(() => z.object({
  /** Package ID to upgrade */
  packageId: z.string().describe('Package ID to upgrade'),

  /** Target version (if omitted, upgrades to latest) */
  targetVersion: z.string().optional().describe('Target version (defaults to latest)'),

  /** New manifest for the target version */
  manifest: ManifestSchema.optional().describe('New manifest (if installing from local)'),

  /** Whether to create a pre-upgrade snapshot */
  createSnapshot: z.boolean().default(true)
    .describe('Whether to create a pre-upgrade backup snapshot'),

  /** Merge strategy for handling customizations */
  mergeStrategy: z.enum([
    'keep-custom',
    'accept-incoming',
    'three-way-merge',
  ]).default('three-way-merge').describe('How to handle customer customizations'),

  /** Whether to run in dry-run mode (preview only, no changes) */
  dryRun: z.boolean().default(false)
    .describe('Preview upgrade without making changes'),

  /** Whether to skip pre-upgrade validation */
  skipValidation: z.boolean().default(false)
    .describe('Skip pre-upgrade compatibility checks'),
}).describe('Upgrade package request'));

/**
 * Upgrade Phase
 * Current phase of the upgrade process.
 */
export const UpgradePhaseSchema = lazySchema(() => z.enum([
  'pending',     // Upgrade requested but not started
  'analyzing',   // Generating upgrade plan
  'snapshot',    // Creating pre-upgrade snapshot
  'executing',   // Applying metadata changes
  'migrating',   // Running migration scripts
  'validating',  // Post-upgrade validation
  'completed',   // Upgrade completed successfully
  'failed',      // Upgrade failed
  'rolling-back', // Rollback in progress
  'rolled-back', // Rollback completed
]).describe('Current phase of the upgrade process'));

/**
 * Upgrade Package Response
 */
export const UpgradePackageResponseSchema = lazySchema(() => z.object({
  /** Whether the upgrade was successful */
  success: z.boolean().describe('Whether the upgrade succeeded'),

  /** Current upgrade phase */
  phase: UpgradePhaseSchema.describe('Current upgrade phase'),

  /** The upgrade plan that was executed */
  plan: UpgradePlanSchema.optional().describe('Upgrade plan'),

  /** Snapshot ID for rollback */
  snapshotId: z.string().optional().describe('Snapshot ID for rollback'),

  /** Merge conflicts that need manual resolution (if any) */
  conflicts: z.array(z.object({
    path: z.string(),
    baseValue: z.unknown(),
    incomingValue: z.unknown(),
    customValue: z.unknown(),
  })).optional().describe('Unresolved merge conflicts'),

  /** Error message (if failed) */
  errorMessage: z.string().optional().describe('Error message if upgrade failed'),

  /** Human-readable summary */
  message: z.string().optional().describe('Human-readable status message'),
}).describe('Upgrade package response'));

// ==========================================
// Rollback
// ==========================================

/**
 * Rollback Package Request
 */
export const RollbackPackageRequestSchema = lazySchema(() => z.object({
  /** Package ID to rollback */
  packageId: z.string().describe('Package ID to rollback'),

  /** Snapshot ID to restore from */
  snapshotId: z.string().describe('Snapshot ID to restore from'),

  /** Whether to also rollback customizations */
  rollbackCustomizations: z.boolean().default(true)
    .describe('Whether to restore pre-upgrade customizations'),
}).describe('Rollback package request'));

/**
 * Rollback Package Response
 */
export const RollbackPackageResponseSchema = lazySchema(() => z.object({
  /** Whether the rollback was successful */
  success: z.boolean().describe('Whether the rollback succeeded'),

  /** Restored version */
  restoredVersion: z.string().optional().describe('Version restored to'),

  /** Message */
  message: z.string().optional().describe('Rollback status message'),
}).describe('Rollback package response'));

// ==========================================
// Export Types
// ==========================================

export type MetadataChangeType = z.infer<typeof MetadataChangeTypeSchema>;
export type MetadataDiffItem = z.infer<typeof MetadataDiffItemSchema>;
export type UpgradeImpactLevel = z.infer<typeof UpgradeImpactLevelSchema>;
export type UpgradePlan = z.infer<typeof UpgradePlanSchema>;
export type UpgradeSnapshot = z.infer<typeof UpgradeSnapshotSchema>;
export type UpgradePackageRequest = z.infer<typeof UpgradePackageRequestSchema>;
export type UpgradePhase = z.infer<typeof UpgradePhaseSchema>;
export type UpgradePackageResponse = z.infer<typeof UpgradePackageResponseSchema>;
export type RollbackPackageRequest = z.infer<typeof RollbackPackageRequestSchema>;
export type RollbackPackageResponse = z.infer<typeof RollbackPackageResponseSchema>;
