/**
 * Base interface and types for platform service providers
 */

import type {
  IService,
  IServiceVolume,
  IPlatformService,
  IPlatformResource,
  IPlatformServiceConfig,
  IProvisionedResource,
  IEnvVarMapping,
  TPlatformServiceType,
  TPlatformResourceType,
} from '../../../types.js';
import * as plugins from '../../../plugins.js';
import type { Onebox } from '../../onebox.js';

/**
 * Interface that all platform service providers must implement
 */
export interface IPlatformServiceProvider {
  /** Unique identifier for this provider type */
  readonly type: TPlatformServiceType;

  /** Human-readable display name */
  readonly displayName: string;

  /** Resource types this provider can provision */
  readonly resourceTypes: TPlatformResourceType[];

  /**
   * Get the default configuration for this platform service
   */
  getDefaultConfig(): Promise<IPlatformServiceConfig>;

  /**
   * Deploy the platform service container
   * @returns The container ID
   */
  deployContainer(): Promise<string>;

  /**
   * Stop the platform service container
   */
  stopContainer(containerId: string): Promise<void>;

  /**
   * Check if the platform service is healthy and ready to accept connections
   */
  healthCheck(): Promise<boolean>;

  /**
   * Provision a resource for a user service (e.g., create database, bucket)
   * @param userService The user service requesting the resource
   * @returns Provisioned resource with credentials and env var mappings
   */
  provisionResource(userService: IService): Promise<IProvisionedResource>;

  /**
   * Deprovision a resource (e.g., drop database, delete bucket)
   * @param resource The resource to deprovision
   */
  deprovisionResource(resource: IPlatformResource, credentials: Record<string, string>): Promise<void>;

  /**
   * Get the environment variable mappings for this provider
   */
  getEnvVarMappings(): IEnvVarMapping[];
}

/**
 * Base class for platform service providers with common functionality
 */
export abstract class BasePlatformServiceProvider implements IPlatformServiceProvider {
  abstract readonly type: TPlatformServiceType;
  abstract readonly displayName: string;
  abstract readonly resourceTypes: TPlatformResourceType[];

  protected oneboxRef: Onebox;

  constructor(oneboxRef: Onebox) {
    this.oneboxRef = oneboxRef;
  }

  abstract getDefaultConfig(): Promise<IPlatformServiceConfig>;
  abstract deployContainer(): Promise<string>;
  abstract stopContainer(containerId: string): Promise<void>;
  abstract healthCheck(): Promise<boolean>;
  abstract provisionResource(userService: IService): Promise<IProvisionedResource>;
  abstract deprovisionResource(resource: IPlatformResource, credentials: Record<string, string>): Promise<void>;
  abstract getEnvVarMappings(): IEnvVarMapping[];

  /**
   * Get the internal Docker network name for platform services
   */
  protected getNetworkName(): string {
    return 'onebox-network';
  }

  /**
   * Get the container name for this platform service
   */
  protected getContainerName(): string {
    return `onebox-${this.type}`;
  }

  /**
   * Get the host data directory for a platform service.
   */
  protected async getPlatformDataDir(serviceDirectoryArg: string): Promise<string> {
    const configuredDataDir = await this.oneboxRef.database.getSetting('dataDir');
    const baseDataDir = configuredDataDir ||
      (process.env.ONEBOX_DEV === 'true' ? './.nogit/platform-data' : '/var/lib/onebox');
    const absoluteBaseDataDir = baseDataDir.startsWith('/') ? baseDataDir : plugins.path.join(process.cwd(), baseDataDir);
    return `${absoluteBaseDataDir.replace(/\/+$/, '')}/${serviceDirectoryArg}`;
  }

  protected async getPlatformStorageClass(): Promise<string> {
    return (await this.oneboxRef.database.getSetting(`${this.type}StorageClass`)) ||
      (await this.oneboxRef.database.getSetting('platformStorageClass')) ||
      await this.oneboxRef.database.getDefaultStorageClassName('database');
  }

  protected async getPlatformDataVolume(
    mountPathArg: string,
    sourceArg = this.type,
  ): Promise<IServiceVolume> {
    return await this.oneboxRef.database.resolveServiceVolumeStorage({
      source: sourceArg,
      mountPath: mountPathArg,
      storageClass: await this.getPlatformStorageClass(),
      backup: true,
    }, 'database');
  }

  /**
   * Generate a resource name from a user service name
   */
  protected generateResourceName(serviceName: string, prefix: string = 'onebox'): string {
    // Replace dashes with underscores for database compatibility
    const sanitized = serviceName.replace(/-/g, '_');
    return `${prefix}_${sanitized}`;
  }

  /**
   * Generate a bucket name from a user service name
   */
  protected generateBucketName(serviceName: string, prefix: string = 'onebox'): string {
    // Buckets use dashes, lowercase
    const sanitized = serviceName.toLowerCase().replace(/_/g, '-');
    return `${prefix}-${sanitized}`;
  }
}
