// DSL SKIP
import { CallContextEvent, CCE, dedent, getRuntime, PostfixReturn, dslIfaceGuard } from "ts-dsl";
import { DockerAPIRequest, DockerAPIResponse, DockerContainer, DockerImage, DockerPullStatus, UnixSocketGet, UnixSocketPost } from "./docker.model";

// ====================================================
//  Facade
// ====================================================

class dockerModel {

  static ps(): PostfixReturn<DockerContainer[]>;
  static ps(...args) { dslIfaceGuard('lugger:docker:docker.ps', __filename); return null; }

  static images(): PostfixReturn<DockerImage[]>;
  static images(...args) { dslIfaceGuard('lugger:docker:docker.images', __filename); return null; }

  static pull(image: string): PostfixReturn<'downloaded' | 'cached'>;
  static pull(...args) { dslIfaceGuard('lugger:docker:docker.pull', __filename); return null; }

  static login(registry: string, username: string, password: string): PostfixReturn<void>;
  static login(...args) { dslIfaceGuard('lugger:docker:docker.login', __filename); return null; }
}

export const docker = dockerModel as (typeof dockerModel & ((req: DockerAPIRequest) => PostfixReturn<DockerAPIResponse>));


// ====================================================
//  Implemenation
// ====================================================

function dockerModel__(cce: CallContextEvent, ...args) {
  return getRuntime().scopedExec(
    cce, 'lugger:docker:docker', { data: { } },
    async (resolve, reject, scopeContext) => {
      resolve(true);
    });
}
(dockerModel__ as any).ps = (cce: CallContextEvent, ...args) => {
  return getRuntime().scopedExec(
    cce, 'lugger:docker:docker.ps', { data: { } },
    async (resolve, reject, scopeContext) => {
      try { resolve(JSON.parse(await UnixSocketGet('/var/run/docker.sock', '/containers/json?all=true&size=true'))); } catch (e) { reject(e); }
    });
}
(dockerModel__ as any).images = (cce: CallContextEvent, ...args) => {
  // const { url, config } = getHttpRequestArgs(args);
  return getRuntime().scopedExec(
    cce, 'lugger:docker:docker.images', { data: { } },
    async (resolve, reject, scopeContext) => {
      try { resolve(JSON.parse(await UnixSocketGet('/var/run/docker.sock', '/images/json'))); } catch (e) { reject(e); }
    });
}
(dockerModel__ as any).pull = (cce: CallContextEvent, ...args) => {
  const image = args[0];
  return getRuntime().scopedExec(
    cce, 'lugger:docker:docker.pull', { data: { } },
    async (resolve, reject, scopeContext) => {
      try {
        let registryEndpoint: string = 'docker.io';
        const headers = { 'X-Stream-Match': [
          `Status: Image is up to date for :: cached`,
          `Status: Downloaded :: downloaded`,
          `unknown: Not Found :: not_found`,
          `unauthorized: :: unauthorized`,
        ].join(' || ') };
        if (image.indexOf('.') >= 0) {
          registryEndpoint = image.split('/')[0];
          if (scopeContext.parent.data.dockerLogin?.[registryEndpoint]) {
            headers['X-Registry-Auth'] = scopeContext.parent.data.dockerLogin[registryEndpoint];
          }
        }
        const res = await UnixSocketPost('/var/run/docker.sock', `/images/create?fromImage=${image}`, '', headers) as DockerPullStatus;
        if (!res) {
          return reject(new Error(`unknown error while pulling ${image}`));
        } else if (res === 'not_found') {
          return reject(new Error(`image not found: ${image}`));
        } else if (res === 'unauthorized') {
          return reject(new Error(`unauthorized: no valid permission for ${registryEndpoint}`));
        }
        return resolve(res);
      } catch (e) { return reject(e); }
    });
}

(dockerModel__ as any).login = (cce: CallContextEvent, ...args) => {
  let [ registry, username, password ] = args as [ string, string, string ];
  return getRuntime().scopedExec(
    cce, 'lugger:docker:docker.login', { data: { } },
    async (resolve, reject, scopeContext) => {
      try {
        if (!scopeContext.parent.data.dockerLogin) { scopeContext.parent.data.dockerLogin = {}; }
        scopeContext.parent.data.dockerLogin[registry] = Buffer.from(JSON.stringify({
          serveraddress: registry, username, password, email: '',
        })).toString('base64');
        resolve(void 0);
      } catch (e) { reject(e); }
    });
}

export const docker__ = dockerModel__ as any as (typeof docker);
