/**
 * BackendExecutionEnvironment — implements IExecutionEnvironment
 * by routing all filesystem and process operations through the onebox API
 * to Docker exec on the target container.
 */

import * as plugins from '../plugins.js';
import * as interfaces from '../../ts_interfaces/index.js';

// Import IExecutionEnvironment type from dees-catalog
type IExecutionEnvironment = import('@design.estate/dees-catalog').IExecutionEnvironment;
type IFileEntry = import('@design.estate/dees-catalog').IFileEntry;
type IFileWatcher = import('@design.estate/dees-catalog').IFileWatcher;
type IProcessHandle = import('@design.estate/dees-catalog').IProcessHandle;

const domtools = plugins.deesElement.domtools;

export class BackendExecutionEnvironment implements IExecutionEnvironment {
  readonly type = 'backend' as const;
  private _ready = false;
  private identity: interfaces.data.IIdentity;

  constructor(
    private serviceName: string,
    identity: interfaces.data.IIdentity,
  ) {
    this.identity = identity;
  }

  get ready(): boolean {
    return this._ready;
  }

  async init(): Promise<void> {
    // Verify the container is accessible by checking if root exists
    const result = await this.fireRequest<interfaces.requests.IReq_WorkspaceExists>(
      'workspaceExists',
      { path: '/' },
    );
    if (!result.exists) {
      throw new Error(`Cannot access container filesystem for service: ${this.serviceName}`);
    }
    this._ready = true;
  }

  async destroy(): Promise<void> {
    this._ready = false;
  }

  async readFile(path: string): Promise<string> {
    const result = await this.fireRequest<interfaces.requests.IReq_WorkspaceReadFile>(
      'workspaceReadFile',
      { path },
    );
    return result.content;
  }

  async writeFile(path: string, contents: string): Promise<void> {
    await this.fireRequest<interfaces.requests.IReq_WorkspaceWriteFile>(
      'workspaceWriteFile',
      { path, content: contents },
    );
  }

  async readDir(path: string): Promise<IFileEntry[]> {
    const result = await this.fireRequest<interfaces.requests.IReq_WorkspaceReadDir>(
      'workspaceReadDir',
      { path },
    );
    return result.entries;
  }

  async mkdir(path: string): Promise<void> {
    await this.fireRequest<interfaces.requests.IReq_WorkspaceMkdir>(
      'workspaceMkdir',
      { path },
    );
  }

  async rm(path: string, options?: { recursive?: boolean }): Promise<void> {
    await this.fireRequest<interfaces.requests.IReq_WorkspaceRm>(
      'workspaceRm',
      { path, recursive: options?.recursive },
    );
  }

  async exists(path: string): Promise<boolean> {
    const result = await this.fireRequest<interfaces.requests.IReq_WorkspaceExists>(
      'workspaceExists',
      { path },
    );
    return result.exists;
  }

  watch(
    _path: string,
    _callback: (event: 'rename' | 'change', filename: string | null) => void,
    _options?: { recursive?: boolean },
  ): IFileWatcher {
    // Polling-based file watching — check for changes periodically
    // For now, return a no-op watcher. Full implementation would poll readDir.
    return { stop: () => {} };
  }

  async spawn(command: string, args?: string[]): Promise<IProcessHandle> {
    // For interactive shell: execute the command via the workspace exec API
    // and return a process handle that bridges stdin/stdout
    const cmd = args ? [command, ...args] : [command];
    const fullCommand = cmd.join(' ');

    // Use a non-interactive exec for now — full interactive shell would need
    // TypedSocket bidirectional streaming (to be implemented)
    const result = await this.fireRequest<interfaces.requests.IReq_WorkspaceExec>(
      'workspaceExec',
      { command: cmd[0], args: cmd.slice(1) },
    );

    // Create a ReadableStream from the exec output
    const output = new ReadableStream<string>({
      start(controller) {
        if (result.stdout) controller.enqueue(result.stdout);
        if (result.stderr) controller.enqueue(result.stderr);
        controller.close();
      },
    });

    // Create a writable stream (no-op for non-interactive)
    const inputStream = new WritableStream<string>();

    return {
      output,
      input: inputStream,
      exit: Promise.resolve(result.exitCode),
      kill: () => {},
    };
  }

  /**
   * Helper to fire TypedRequests to the workspace API
   */
  private async fireRequest<T extends { method: string; request: any; response: any }>(
    method: string,
    data: Omit<T['request'], 'identity' | 'serviceName'>,
  ): Promise<T['response']> {
    const typedRequest = new domtools.plugins.typedrequest.TypedRequest<T>(
      '/typedrequest',
      method,
    );
    return await typedRequest.fire({
      identity: this.identity,
      serviceName: this.serviceName,
      ...data,
    } as T['request']);
  }
}
