import { exec } from "node:child_process";
import { promisify } from "node:util";
import type { PullImageInput } from "../schemas/images";
import { getMsbPath } from "../lib/msb";

const execAsync = promisify(exec);

export type ImageRecord = {
  ref: string;
  sourceType: string;
  cached: boolean;
  size?: number;
  createdAt?: string;
  lastUsedAt?: string;
};

type ImageBindings = {
  list(): Promise<ImageRecord[]>;
  pull(ref: string): Promise<ImageRecord>;
  inspect(ref: string): Promise<ImageRecord>;
  remove(ref: string): Promise<void>;
};

type CliImageInfo = {
  architecture: string;
  created_at: string;
  digest: string;
  layer_count: number;
  os: string;
  reference: string;
  size_bytes: number;
};

class MicrosandboxImageBindings implements ImageBindings {
  private get msb(): string {
    return getMsbPath();
  }

  async list(): Promise<ImageRecord[]> {
    const { stdout } = await execAsync(`${this.msb} image ls --format json`);
    const images: CliImageInfo[] = JSON.parse(stdout);
    return images.map((img) => ({
      ref: img.reference,
      sourceType: "oci",
      cached: true,
      size: img.size_bytes,
      createdAt: img.created_at,
    }));
  }

  async pull(ref: string): Promise<ImageRecord> {
    await execAsync(`${this.msb} image pull ${ref}`);
    return this.inspect(ref);
  }

  async inspect(ref: string): Promise<ImageRecord> {
    const { stdout } = await execAsync(`${this.msb} image inspect ${ref} --format json`);
    const img: CliImageInfo = JSON.parse(stdout);
    return {
      ref: img.reference,
      sourceType: "oci",
      cached: true,
      size: img.size_bytes,
      createdAt: img.created_at,
    };
  }

  async remove(ref: string): Promise<void> {
    await execAsync(`${this.msb} image rm ${ref}`);
  }
}

export class ImageService {
  constructor(
    private readonly deps: {
      bindings?: ImageBindings;
    } = {}
  ) {}

  private get bindings(): ImageBindings {
    return this.deps.bindings ?? new MicrosandboxImageBindings();
  }

  async listImages(): Promise<{ items: ImageRecord[]; total: number }> {
    const items = await this.bindings.list();
    return { items, total: items.length };
  }

  async pullImage(input: PullImageInput): Promise<ImageRecord> {
    return await this.bindings.pull(input.ref);
  }

  async getImage(ref: string): Promise<ImageRecord> {
    return await this.bindings.inspect(ref);
  }

  async deleteImage(ref: string): Promise<{ ref: string }> {
    await this.bindings.remove(ref);
    return { ref };
  }
}
