// @lifecycle canonical - Abstract base class for resource handlers
/**
 * Base Resource Handler
 *
 * Abstract base class that consolidates common patterns across:
 * - GateManager
 * - FrameworkManager
 * - StyleManager
 *
 * Provides unified lifecycle management, CRUD operations, and introspection.
 * Subclasses implement domain-specific behavior via abstract methods.
 *
 * @example
 * ```typescript
 * class GateManager extends BaseResourceHandler<GateGuide, GateGuideEntry> {
 *   protected get managerName() { return 'GateManager'; }
 *   protected async createRegistry() { return createGateRegistry(this.logger); }
 *   // ... implement other abstract methods
 * }
 * ```
 */

import type { ResourceManagerStats, BaseResourceHandlerConfig } from './types.js';
import type { Logger } from '../../types/index.js';

/**
 * Abstract base class for resource handlers
 *
 * Provides a template for common lifecycle and CRUD operations.
 * Subclasses manage their own registry type and implement abstract methods.
 *
 * @template TResource - The type of resource being managed
 * @template TEntry - The registry entry type (domain-specific entry wrapper)
 * @template TConfig - The configuration type (must extend BaseResourceHandlerConfig)
 * @template TStats - The statistics type (unconstrained to allow domain-specific stats)
 */
export abstract class BaseResourceHandler<
  TResource,
  TEntry = unknown,
  TConfig extends BaseResourceHandlerConfig = BaseResourceHandlerConfig,
  TStats = ResourceManagerStats,
> {
  protected logger: Logger;
  protected config: TConfig;
  protected initialized: boolean = false;

  constructor(logger: Logger, config: TConfig) {
    this.logger = logger;
    this.config = this.applyDefaultConfig(config);
  }

  // ============================================================================
  // Lifecycle Methods
  // ============================================================================

  /**
   * Initialize the resource manager
   *
   * Must be called before using any other methods.
   * Creates the registry and loads initial resources.
   */
  async initialize(): Promise<void> {
    if (this.initialized) {
      this.logger.debug(`${this.managerName} already initialized`);
      return;
    }

    this.logger.info(`Initializing ${this.managerName}...`);
    const startTime = performance.now();

    try {
      await this.initializeRegistry();
      await this.postRegistryInit();
      this.initialized = true;

      const loadTime = performance.now() - startTime;
      this.logger.info(`${this.managerName} initialized in ${loadTime.toFixed(1)}ms`);
    } catch (error) {
      this.logger.error(`Failed to initialize ${this.managerName}:`, error);
      throw error;
    }
  }

  /**
   * Check if manager is initialized
   */
  get isInitialized(): boolean {
    return this.initialized;
  }

  // ============================================================================
  // CRUD Operations
  // ============================================================================

  /**
   * Get a resource by ID
   *
   * @param id - Resource identifier (case-insensitive)
   * @returns The resource or undefined if not found/disabled
   */
  get(id: string): TResource | undefined {
    this.ensureInitialized();
    return this.getResource(this.normalizeId(id));
  }

  /**
   * Check if a resource exists
   *
   * @param id - Resource identifier (case-insensitive)
   * @returns true if the resource exists (regardless of enabled state)
   */
  has(id: string): boolean {
    this.ensureInitialized();
    return this.hasResource(this.normalizeId(id));
  }

  /**
   * List all resources
   *
   * @param enabledOnly - If true, only return enabled resources (default: true)
   * @returns Array of resources
   */
  list(enabledOnly: boolean = true): TResource[] {
    this.ensureInitialized();
    return this.listResources(enabledOnly);
  }

  /**
   * Get resource entries with metadata
   *
   * @param enabledOnly - If true, only return enabled entries (default: true)
   * @returns Array of entries with metadata
   */
  getEntries(enabledOnly: boolean = true): TEntry[] {
    this.ensureInitialized();
    return this.getResourceEntries(enabledOnly);
  }

  /**
   * Set the enabled state of a resource
   *
   * @param id - Resource identifier (case-insensitive)
   * @param enabled - Whether to enable the resource
   * @returns true if the state was changed
   */
  setEnabled(id: string, enabled: boolean): boolean {
    this.ensureInitialized();
    return this.setResourceEnabled(this.normalizeId(id), enabled);
  }

  /**
   * Reload a resource from disk
   *
   * @param id - Resource identifier (case-insensitive)
   * @returns true if reload was successful
   */
  async reload(id: string): Promise<boolean> {
    this.ensureInitialized();
    return this.reloadResource(this.normalizeId(id));
  }

  /**
   * Unregister a resource
   *
   * @param id - Resource identifier (case-insensitive)
   * @returns true if the resource was unregistered
   */
  unregister(id: string): boolean {
    this.ensureInitialized();
    return this.unregisterResource(this.normalizeId(id));
  }

  // ============================================================================
  // Cache Management
  // ============================================================================

  /**
   * Clear the cache
   *
   * @param id - Optional specific ID to clear; if omitted, clears all
   */
  clearCache(id?: string): void {
    this.ensureInitialized();
    this.clearResourceCache(id != null ? this.normalizeId(id) : undefined);
  }

  // ============================================================================
  // Introspection
  // ============================================================================

  /**
   * Get manager statistics
   *
   * @returns Statistics object with counts and performance metrics
   */
  getStats(): TStats {
    this.ensureInitialized();
    return this.getResourceStats();
  }

  /**
   * Get base manager status
   *
   * Subclasses may override to add domain-specific status fields.
   *
   * @returns Status object with initialization state and enabled state
   */
  getStatus(): { initialized: boolean; enabled: boolean } {
    return {
      initialized: this.initialized,
      enabled: this.isSystemEnabled(),
    };
  }

  // ============================================================================
  // Abstract Methods (subclass must implement)
  // ============================================================================

  /**
   * Manager name for logging
   */
  protected abstract get managerName(): string;

  /**
   * Initialize the domain-specific registry
   *
   * Subclass should create and store its registry in this method.
   */
  protected abstract initializeRegistry(): Promise<void>;

  /**
   * Apply default configuration values
   */
  protected abstract applyDefaultConfig(config: TConfig): TConfig;

  /**
   * Get a resource by normalized ID
   */
  protected abstract getResource(id: string): TResource | undefined;

  /**
   * Check if a resource exists by normalized ID
   */
  protected abstract hasResource(id: string): boolean;

  /**
   * List all resources
   */
  protected abstract listResources(enabledOnly: boolean): TResource[];

  /**
   * Get resource entries with metadata
   */
  protected abstract getResourceEntries(enabledOnly: boolean): TEntry[];

  /**
   * Set resource enabled state
   */
  protected abstract setResourceEnabled(id: string, enabled: boolean): boolean;

  /**
   * Reload a specific resource from disk
   */
  protected abstract reloadResource(id: string): Promise<boolean>;

  /**
   * Unregister a resource
   */
  protected abstract unregisterResource(id: string): boolean;

  /**
   * Clear resource cache
   */
  protected abstract clearResourceCache(id?: string): void;

  /**
   * Get statistics
   */
  protected abstract getResourceStats(): TStats;

  // ============================================================================
  // Overridable Methods
  // ============================================================================

  /**
   * Normalize resource ID
   *
   * Default implementation converts to lowercase.
   * Override if different normalization is needed.
   */
  protected normalizeId(id: string): string {
    return id.toLowerCase();
  }

  /**
   * Check if the system is enabled
   *
   * Default implementation returns true.
   * Override to integrate with state managers.
   */
  protected isSystemEnabled(): boolean {
    return true;
  }

  /**
   * Post-registry initialization hook
   *
   * Called after registry is created but before initialization completes.
   * Override to perform additional setup (e.g., generate derived definitions).
   */
  protected async postRegistryInit(): Promise<void> {
    // Default: no-op. Subclasses can override.
  }

  // ============================================================================
  // Protected Utilities
  // ============================================================================

  /**
   * Ensure manager is initialized before use
   *
   * @throws Error if manager is not initialized
   */
  protected ensureInitialized(): void {
    if (!this.initialized) {
      throw new Error(`${this.managerName} not initialized. Call initialize() first.`);
    }
  }
}
