import { empty } from "./utils";

/**
 * @typedef {Object} WireframeSDK - Wireframe SDK
 * @property {string} _baseurl - Base URL
 * @property {string} _project_id - Project ID
 * @property {string} _secret - Secret
 * @property {string} _table - Table
 * @property {function(): Object} getHeader - Get header
 * @property {function(): string} baseUrl - Base URL
 * @property {function(): string} getProjectId - Get project ID
 * @property {function(): string} wireBaseUrl - Wire base URL
 * @property {function(string, Object=): Promise<Object>} makeFolder - Make folder
 * @property {function(string, Object=): Promise<Object>} makeWireframeRow - Make wireframe row
 * @property {function(string, Object=): Promise<Object>} makeRequirement - Make requirement
 * @property {function(string, string=): Promise<Object>} saveWireframeConfig - Save wireframe config
 * @property {function(string, string=): Promise<Object>} saveDBConfig - Save DB config
 * @property {function(string, string, string=): Promise<Object>} getWireframeConfig - Get wireframe config
 * @property {function(string, string=): Promise<Object>} saveApiConfig - Save API config
 * @property {function(string, string=): Promise<Object>} saveIosConfig - Save iOS config
 * @property {function(string, string=): Promise<Object>} saveAndroidConfig - Save Android config
 * @property {function(string, string, boolean=, string): Promise<Object>} commitFileToRepo - Commit file to repo
 * @property {function(string, string, Array, boolean=, string=): Promise<Object>} commitFilesToRepo - Commit files to repo
 * @property {function(string, Object=): Promise<Object>} askGpt - Ask GPT
 * @property {function(string, Object=): Promise<Object>} askGptCustom - Ask GPT custom
 * @property {function(Object, string, string): Promise<Object>} customAICompletion - Custom AI completion
 * @property {function(string, Object=): Promise<Object>} getProjectDeployment - Get project deployment
 * @property {function(string, Object=): Promise<Object>} initializeProjectDeployment - Initialize project deployment
 * @property {function(string, string, Object=): Promise<Object>} createRepo - Create repo
 * @property {function(string, Object=): Promise<Object>} createDomainRecord - Create domain record
 * @property {function(string, Object=): Promise<Object>} deleteDomainRecord - Delete domain record
 * @property {function(string, string, string=, Object=): Promise<Object>} createJenkinsJob - Create Jenkins job
 * @property {function(string, string, string=, Object=): Promise<Object>} provisionProject - Provision project
 * @property {function(string, string, Object=): Promise<Object>} getRepoBranches - Get repo branches
 * @property {function(string, Object=): Promise<Object>} getPaginate - Get paginate
 */
export default function WireframeSDK() {
  this._baseurl = "https://wireframe.mkdlabs.com";
  // this._baseurl = "http://localhost:3048";
  this._project_id = "wireframe";
  this._secret = "3c3w0u0scvmcv39q0brods1iv11q6n5lf";
  //   this._baseurl = "http://localhost:3048";
  this._table = "";

  const raw = this._project_id + ":" + this._secret;
  let base64Encode = btoa(raw);

  const joinsPropertyHasValue = (options) => {
    let properties = Object.keys(options);
    let joins = "";
    properties.forEach((prop) => {
      if (options[prop]?.length) {
        joins += `&${prop}=${options[prop]}`;
      }
    })
    return { joins };

  }

  this.getHeader = function () {
    return {
      Authorization: "Bearer " + localStorage.getItem("token"),
      "x-project": base64Encode,
    };
  };

  this.baseUrl = function () {
    return this._baseurl;
  };

  this.getProjectId = function () {
    return this._project_id;
  };

  this.wireBaseUrl = function () {
    return this._baseurl + "/v5/api/deployments";
  };

  this.makeFolder = async function (id, options = {}) {
    if (empty(id)) throw new Error("id is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/sow/make-folder/${id}`,
      {
        method: "POST",
        headers: header,
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  this.makeWireframeRow = async function (id, options = {}) {
    if (empty(id)) throw new Error("id is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/sow/make-wireframe/${id}`,
      {
        method: "POST",
        headers: header,
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  this.makeRequirement = async function (id, options = {}) {
    if (empty(id)) throw new Error("id is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/sow/make-requirement/${id}`,
      {
        method: "POST",
        headers: header,
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  this.saveWireframeConfig = async function (id, config = "") {
    if (empty(id)) throw new Error("id is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/wireframe/save-wireframe-config/${id}`,
      {
        method: "PUT",
        headers: header,
        body: JSON.stringify({ config }),
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  this.saveDBConfig = async function (id, config = "") {
    if (empty(id)) throw new Error("id is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/wireframe/save-db-config/${id}`,
      {
        method: "PUT",
        headers: header,
        body: JSON.stringify({ config }),
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  this.getWireframeConfig = async function (table, id, config="") {
    if (empty(id)) throw new Error("id is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/wireframe/${table}/${id}?to_get=${config}`,
      {
        method: "GET",
        headers: header,
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  //v5/api/deployments/wireframe/deployment/populate-mock-data
  this.populateMockData = async function (bodyInputs) {
    if (empty(bodyInputs)) throw new Error("bodyInputs is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/wireframe/deployment/populate-mock-data`,
      {
        method: "POST",
        headers: header,
        body: JSON.stringify(bodyInputs),
      }
    );

    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }
    return json;
  }

  this.saveApiConfig = async function (id, config = "") {
    if (empty(id)) throw new Error("id is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/wireframe/save-api-config/${id}`,
      {
        method: "PUT",
        headers: header,
        body: JSON.stringify({ config }),
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  this.saveIosConfig = async function (id, config = "") {
    if (empty(id)) throw new Error("id is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/wireframe/save-ios-config/${id}`,
      {
        method: "PUT",
        headers: header,
        body: JSON.stringify({ config }),
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  this.saveAndroidConfig = async function (id, config = "") {
    if (empty(id)) throw new Error("id is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/wireframe/save-android-config/${id}`,
      {
        method: "PUT",
        headers: header,
        body: JSON.stringify({ config }),
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  /**
   * Clear S3 images
   * @param {string} project - Project ID
   * @returns {Promise<Object>} - Clear S3 images when project moves to production
   */
  this.clearS3Images = async function (project) {
    if (empty(project)) throw new Error("project id is required.");

    const header = this.getHeader();

    const getResult = await fetch(
      this.wireBaseUrl() + `/wireframe/clear-s3-images/${project}`,
      {
        method: "POST",
        headers: header,
      }
    );
    const json = await getResult.json();

    return json;
  }


  /**
   * Commit file to repo
   * @param {string} project - Project
   * @param {string} repo - Repo
   * @param {boolean} is_api - Is API
   * @param {string} content - Content
   * @returns {Promise<Object>} - Commit file to repo
   */
  this.commitFileToRepo = async function (
    project,
    repo,
    is_api = false,
    content
  ) {
    if (empty(project)) throw new Error("project is required.");
    if (empty(repo)) throw new Error("repo is required.");
    if (empty(content)) throw new Error("content is required.");

    content = btoa(content);

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/wireframe/commit-file`,
      {
        method: "POST",
        headers: header,
        body: JSON.stringify({ project, repo, content, is_api }),
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  this.commitFilesToRepo = async function (
    project,
    repo,
    files,
    is_api = false,
    branch = "master"
  ) {
    if (empty(project)) throw new Error("project is required.");
    if (empty(repo)) throw new Error("repo is required.");
    if (empty(files)) throw new Error("files is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/wireframe/commit-files`,
      {
        method: "POST",
        headers: header,
        body: JSON.stringify({ project, repo, files, is_api, branch }),
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  /**
   * Ask GPT
   * @param {string} id - ID
   * @param {Object} options - Options
   * @returns {Promise<Object>} - Ask GPT
   */
  this.askGpt = async function (id, options = {}) {
    if (empty(id)) throw new Error("id is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(this.wireBaseUrl() + `/sow/ask-gpt/${id}`, {
      method: "POST",
      headers: header,
      body: JSON.stringify(options),
    });
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  this.askGptCustom = async function (prompt, options = {}) {
    if (empty(prompt)) throw new Error("id is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/wireframe/ask-gpt-custom`,
      {
        method: "POST",
        headers: header,
        body: JSON.stringify({ prompt, temperature: 0 }),
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  this.customAICompletion = async function (api_structure, comments, project_name) {
    if (empty(prompt)) throw new Error("id is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/wireframe/deployment/custom-completion/`,
      {
        method: "POST",
        headers: header,
        body: JSON.stringify({ project_name: project_name, api_structure, comments }),
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  this.getProjectDeployment = async function (project, options = {}) {
    if (empty(project)) throw new Error("project is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/wireframe/deployment/${project}`,
      {
        method: "GET",
        headers: header,
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  this.initializeProjectDeployment = async function (project, options = {}) {
    if (empty(project)) throw new Error("project is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/wireframe/deployment/initialize/${project}`,
      {
        method: "POST",
        headers: header,
        body: JSON.stringify({}),
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  this.createRepo = async function (project, platform, options = {}) {
    if (empty(project)) throw new Error("project is required.");
    if (empty(platform)) throw new Error("platform is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/wireframe/deployment/repository/${project}`,
      {
        method: "POST",
        headers: header,
        body: JSON.stringify({ platform }),
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  this.createDomainRecord = async function (project, options = {}) {
    if (empty(project)) throw new Error("project is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/wireframe/deployment/domain/${project}`,
      {
        method: "POST",
        headers: header,
        body: JSON.stringify({}),
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  this.deleteDomainRecord = async function (project, options = {}) {
    if (empty(project)) throw new Error("project is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/wireframe/deployment/domain/${project}`,
      {
        method: "DELETE",
        headers: header,
        body: JSON.stringify({}),
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  this.createJenkinsJob = async function (
    project,
    platform,
    branch = "master",
    options = {}
  ) {
    if (empty(project)) throw new Error("project is required.");
    if (empty(platform)) throw new Error("platform is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/wireframe/deployment/jenkins/${project}`,
      {
        method: "POST",
        headers: header,
        body: JSON.stringify({ platform, branch }),
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  this.provisionProject = async function (
    project,
    platform,
    branch = "master",
    options = {}
  ) {
    if (empty(project)) throw new Error("project is required.");
    if (empty(platform)) throw new Error("platform is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() + `/wireframe/deployment/provision/${project}`,
      {
        method: "POST",
        headers: header,
        body: JSON.stringify({ platform, branch }),
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  /**
   * Get repo branches
   * @param {string} project - Project
   * @param {string} platform - Platform (frontend, backend, android, ios)
   * @returns {Promise<Object>} - Get repo branches list
   */
  this.getRepoBranches = async function (project, platform, options = {}) {
    if (empty(project)) throw new Error("project is required.");
    if (empty(platform)) throw new Error("platform is required.");

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };

    const getResult = await fetch(
      this.wireBaseUrl() +
      `/wireframe/deployment/repository/branches/${project}/${platform}`,
      {
        method: "GET",
        headers: header,
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };

  /**
   * Get paginate
   * @param {string} table - Table
   * @param {Object} options - Options
   * @returns {Promise<Object>} - Get paginate
   */
  this.getPaginate = async function (table, options = {}) {
    if (empty(table)) throw new Error("table and id is required.");
    const { joins } = joinsPropertyHasValue(options);

    const header = {
      "Content-Type": "application/json",
      "x-project": base64Encode,
      Authorization: "Bearer " + localStorage.getItem("token"),
    };
    const getResult = await fetch(
      this.wireBaseUrl() + `/${table}/get?${joins}`,
      {
        method: "get",
        headers: header,
      }
    );
    const json = await getResult.json();

    if (getResult.status === 401) {
      throw new Error(json.message);
    }

    if (getResult.status === 403) {
      throw new Error(json.message);
    }
    return json;
  };


  return this;
}
