// lib/reflection/policy.ts
// ========================================
//  Policy Manager with Adjustment Support
// ========================================

import { isDryRun, isVerbose } from './flags';
import {
  POLICY_NAMES,
  type PolicyName,
  type Policy,
  type PolicyState,
  type PolicyAdjustment,
  type FeedbackDecision,
} from './types';

// ========================================
//   Default & Initial State
// ========================================

const DEFAULT_POLICIES: Record<string, Policy> = {
  [POLICY_NAMES.THROTTLE_WINDOWS]: { name: POLICY_NAMES.THROTTLE_WINDOWS, value: 100 }, // ms between window opens
  [POLICY_NAMES.DEFER_ANIMATIONS]: { name: POLICY_NAMES.DEFER_ANIMATIONS, value: false }, // Whether to defer non-critical animations
  [POLICY_NAMES.ADJUST_PREFETCH]: { name: POLICY_NAMES.ADJUST_PREFETCH, value: 1.0 }, // Multiplier for prefetching resources (1.0 = normal)
  [POLICY_NAMES.TUNE_FOCUS_TIMEOUT]: { name: POLICY_NAMES.TUNE_FOCUS_TIMEOUT, value: 150 }, // ms for focus-related timeouts
};

const createInitialPolicyState = (): PolicyState => ({
  version: 1,
  policies: { ...DEFAULT_POLICIES },
  timestamp: performance.now(),
});

// ========================================
//   Module State
// ========================================

let currentState: PolicyState = createInitialPolicyState();
const history: PolicyState[] = [currentState];

// ========================================
//   Public API
// ========================================

/**
 * Returns the current policy state.
 */
export function getPolicyState(): Readonly<PolicyState> {
  return currentState;
}

/**
 * Returns the value of a specific policy.
 * @param name The name of the policy to retrieve.
 * @returns The value of the policy.
 */
export function getPolicyValue(name: PolicyName): number | boolean {
  return currentState.policies[name]?.value ?? DEFAULT_POLICIES[name].value;
}

/**
 * Applies a set of adjustments to the current policy state.
 * Creates a new, versioned policy state.
 * Respects DRY_RUN flag.
 *
 * @param adjustments An array of policy adjustments to apply.
 * @returns The new policy state, or the current state if in DRY_RUN mode.
 */
export function applyPolicyAdjustments(adjustments: PolicyAdjustment[]): PolicyState {
  if (!adjustments?.length) return currentState;

  if (isDryRun()) {
    if (isVerbose()) {
      console.log('[Reflection/Policy] DRY RUN: Would apply adjustments:', adjustments);
    }
    return currentState;
  }

  const newPolicies = { ...currentState.policies };
  let hasChanges = false;

  for (const adj of adjustments) {
    const prevValue = newPolicies[adj.policyName]?.value;
    if (prevValue !== adj.newValue) {
      newPolicies[adj.policyName] = { name: adj.policyName, value: adj.newValue };
      hasChanges = true;

      if (isVerbose()) {
        console.log(
          `[Reflection/Policy] Adjusted ${adj.policyName}: ${prevValue} → ${adj.newValue}`
        );
      }
    }
  }

  if (!hasChanges) return currentState;

  const newState: PolicyState = {
    version: currentState.version + 1,
    policies: newPolicies,
    timestamp: performance.now(),
  };

  currentState = newState;
  history.push(newState);

  if (isVerbose()) {
    console.log(`[Reflection/Policy] Policy state updated to v${newState.version}`, newState);
  }

  return newState;
}

/**
 * Applies a single policy change from a feedback decision.
 * This is the bridge called by the Feedback system.
 */
export function applyPolicyChange(decision: FeedbackDecision): void {
  if (isDryRun()) {
    if (isVerbose()) {
      console.log('[Reflection/Policy] DRY RUN (applyPolicyChange):', decision);
    }
    return;
  }

  const adjustments: PolicyAdjustment[] = [];

  switch (decision.action) {
    case 'THROTTLE_WINDOWS':
      adjustments.push({
        policyName: POLICY_NAMES.THROTTLE_WINDOWS,
        newValue: 500, // ms delay between new windows
      });
      break;

    case 'DEFER_ANIM':
    case 'DEFER_ANIMATIONS':
      adjustments.push({
        policyName: POLICY_NAMES.DEFER_ANIMATIONS,
        newValue: true,
      });
      break;

    case 'ADJUST_PREFETCH':
      adjustments.push({
        policyName: POLICY_NAMES.ADJUST_PREFETCH,
        newValue: 0.5, // reduce prefetch intensity
      });
      break;

    case 'TUNE_FOCUS_TIMEOUT':
      adjustments.push({
        policyName: POLICY_NAMES.TUNE_FOCUS_TIMEOUT,
        newValue: 200, // increase focus timeout
      });
      break;

    default:
      if (isVerbose()) {
        console.warn('[Reflection/Policy] Unknown decision action:', decision.action);
      }
      return;
  }

  applyPolicyAdjustments(adjustments);
}

/**
 * Reverts the policy state to a previous version.
 * @param version The version number to revert to. If not provided, reverts to the previous version.
 * @returns The policy state after rollback, or null if the version is invalid.
 */
export function rollbackPolicy(version?: number): PolicyState | null {
  if (history.length < 2) {
    return null; // Cannot rollback initial state
  }

  const targetVersion = version ?? currentState.version - 1;
  const targetState = history.find((s) => s.version === targetVersion);

  if (!targetState) {
    console.error(
      `[Reflection/Policy] Cannot find policy version ${targetVersion} to roll back to.`
    );
    return null;
  }

  currentState = targetState;

  if (isVerbose()) {
    console.warn(
      `[Reflection/Policy] Rolled back to policy state v${targetState.version}`,
      targetState
    );
  }

  return currentState;
}

/**
 * Resets the policy manager to its initial state.
 * (For testing purposes)
 */
export function _resetPolicyManager(): void {
  currentState = createInitialPolicyState();
  history.length = 0;
  history.push(currentState);
}
