import * as plugins from '../../plugins.ts';
import { logger } from '../../logging.ts';
import type { OpsServer } from '../classes.opsserver.ts';
import * as interfaces from '../../../ts_interfaces/index.ts';
import { requireAdminIdentity } from '../helpers/guards.ts';
import { getErrorMessage } from '../../utils/error.ts';

export class WorkspaceHandler {
  public typedrouter = new plugins.typedrequest.TypedRouter();

  constructor(private opsServerRef: OpsServer) {
    this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter);
    this.registerHandlers();
  }

  /**
   * Resolve a service name to a container ID (handling Swarm service IDs)
   */
  private async resolveContainerId(serviceName: string): Promise<string> {
    const service = this.opsServerRef.oneboxRef.services.getService(serviceName);
    if (!service || !service.containerID) {
      throw new plugins.typedrequest.TypedResponseError(`Service not found or has no container: ${serviceName}`);
    }
    return service.containerID;
  }

  private registerHandlers(): void {
    // Read file from container
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_WorkspaceReadFile>(
        'workspaceReadFile',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const containerId = await this.resolveContainerId(dataArg.serviceName);
          const result = await this.opsServerRef.oneboxRef.docker.execInContainer(
            containerId,
            ['cat', dataArg.path],
          );
          if (result.exitCode !== 0) {
            throw new plugins.typedrequest.TypedResponseError(`Failed to read file: ${result.stderr || 'File not found'}`);
          }
          return { content: result.stdout };
        },
      ),
    );

    // Write file to container
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_WorkspaceWriteFile>(
        'workspaceWriteFile',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const containerId = await this.resolveContainerId(dataArg.serviceName);
          // Use sh -c with printf to write content (handles special characters)
          const escaped = dataArg.content.replace(/'/g, "'\\''");
          const result = await this.opsServerRef.oneboxRef.docker.execInContainer(
            containerId,
            ['sh', '-c', `printf '%s' '${escaped}' > ${dataArg.path}`],
          );
          if (result.exitCode !== 0) {
            throw new plugins.typedrequest.TypedResponseError(`Failed to write file: ${result.stderr}`);
          }
          return {};
        },
      ),
    );

    // Read directory from container
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_WorkspaceReadDir>(
        'workspaceReadDir',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const containerId = await this.resolveContainerId(dataArg.serviceName);
          // Use ls with -1 -F to get entries with type indicators (/ for dirs)
          const result = await this.opsServerRef.oneboxRef.docker.execInContainer(
            containerId,
            ['ls', '-1', '-F', dataArg.path],
          );
          if (result.exitCode !== 0) {
            throw new plugins.typedrequest.TypedResponseError(`Failed to read directory: ${result.stderr}`);
          }
          const entries = result.stdout
            .split('\n')
            .filter((line) => line.trim())
            .map((line) => {
              const isDir = line.endsWith('/');
              const name = isDir ? line.slice(0, -1) : line.replace(/[*@=|]$/, '');
              const basePath = dataArg.path.endsWith('/') ? dataArg.path : dataArg.path + '/';
              return {
                type: (isDir ? 'directory' : 'file') as 'file' | 'directory',
                name,
                path: basePath + name,
              };
            });
          return { entries };
        },
      ),
    );

    // Create directory in container
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_WorkspaceMkdir>(
        'workspaceMkdir',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const containerId = await this.resolveContainerId(dataArg.serviceName);
          const result = await this.opsServerRef.oneboxRef.docker.execInContainer(
            containerId,
            ['mkdir', '-p', dataArg.path],
          );
          if (result.exitCode !== 0) {
            throw new plugins.typedrequest.TypedResponseError(`Failed to create directory: ${result.stderr}`);
          }
          return {};
        },
      ),
    );

    // Remove file/directory from container
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_WorkspaceRm>(
        'workspaceRm',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const containerId = await this.resolveContainerId(dataArg.serviceName);
          const args = dataArg.recursive ? ['rm', '-rf', dataArg.path] : ['rm', '-f', dataArg.path];
          const result = await this.opsServerRef.oneboxRef.docker.execInContainer(
            containerId,
            args,
          );
          if (result.exitCode !== 0) {
            throw new plugins.typedrequest.TypedResponseError(`Failed to remove: ${result.stderr}`);
          }
          return {};
        },
      ),
    );

    // Check if path exists in container
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_WorkspaceExists>(
        'workspaceExists',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const containerId = await this.resolveContainerId(dataArg.serviceName);
          const result = await this.opsServerRef.oneboxRef.docker.execInContainer(
            containerId,
            ['test', '-e', dataArg.path],
          );
          return { exists: result.exitCode === 0 };
        },
      ),
    );

    // Execute a command in the container (non-interactive)
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_WorkspaceExec>(
        'workspaceExec',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const containerId = await this.resolveContainerId(dataArg.serviceName);
          const cmd = dataArg.args
            ? [dataArg.command, ...dataArg.args]
            : [dataArg.command];
          const result = await this.opsServerRef.oneboxRef.docker.execInContainer(
            containerId,
            cmd,
          );
          return {
            stdout: result.stdout,
            stderr: result.stderr,
            exitCode: result.exitCode,
          };
        },
      ),
    );

    logger.info('Workspace handler registered');
  }
}
