class GitController{

  static gitApi = "https://vc.dennis.systems";

  static ADD_SUCCESS = "gitlab.branch.add.success"
  static ADD_FAIL = "gitlab.branch.add.fail";
  static DELETE_SUCCESS = "gitlab.branch.delete.success";
  static DELETE_FAIL = "gitlab.branch.delete.fail";
  static MERGED_SUCCESS = "gitlab.branch.merged.success"
  static MERGED_FAIL = "gitlab.branch.merged.fail";
  static ADD_PROJECT_SUCCESS = "gitlab.config.project.add.success";
  static ADD_PROJECT_FAIL = "gitlab.config.project.add.fail";
  static DELETE_PROJECT_SUCCESS = "gitlab.config.project.delete.success";
  static DELETE_PROJECT_FAIL = "gitlab.config.project.delete.fail";

  static addProject(projectId, payload, callback, error = (e) => {}){

    Executor.runPostWithPayload(Environment.dataApi + "/api/v2/flaw/plugins/version_control/git_lab/project/add/" + projectId, (data) =>{

      callback(data)

    }, payload, (e) => error(e)) 
  }

  static getProject(projectId, callback, error = (e) => {}){
    Executor.runGet(Environment.dataApi + "/api/v2/flaw/plugins/version_control/git_lab/project/" + projectId, (data) =>{

      callback(data)

    }, true, Executor.HEADERS, (e) => error(e)) 
  }

  static importProject(groupId, gitUid, callback, error = (e) => {}){
    Executor.runPost(Environment.dataApi + "/api/v2/flaw/plugins/version_control/git_lab/importer/project/" + groupId + "/" + gitUid, (data) => {
      callback(data)
    }, error(e))
  }

  static editProject(payload, callback, error = (e) => {}){
    Executor.runPostWithPayload(Environment.dataApi + "/api/v2/version_control/git_lab/config/edit/", (data) => {
      callback(data)
    }, payload, error(e))
  }


  static deleteProject(projectId, callback, error = (e) => {}){
    Executor.runDelete(Environment.dataApi + "/api/v2/flaw/plugins/version_control/git_lab/project/delete/" + projectId, (data) => {
      callback(data)
    }, (e) => error(e))
  }

  static addBranch(payload, callback, error = (e) => {}) {

    Executor.runPostWithPayload(GitController.gitApi + "/api/v2/version_control/git_lab/branch/add", (data) =>{
      callback(data);
    }, payload, (e) => error(e))
  }

  static getBranch(repoId, callback){
    Executor.runGet(GitController.gitApi + "/api/v2/version_control/git_lab/branch/repo/" + repoId, (data) => {
      callback(data)
    })
  }

  static deleteBranch(id, callback, error = (e) => {}) {

    Executor.runDelete(GitController.gitApi + "/api/v2/version_control/git_lab/branch/delete/" + id, (data) =>{
      callback(data);
    }, (e) => error(e))
  }

  static mergeBranch(sourseId, targetId, callback, error = (e) => {}) {

    Executor.runPost(GitController.gitApi + "/api/v2/version_control/git_lab/branch/merge/" + sourseId + "/" + targetId, (data) =>{
      callback(data);
    }, (e) => error(e))
  }

  static buildConnectionToProject(projectId, uid, callback, error = (e) => {}) {
    Executor.runPost(Environment.dataApi + "/api/v2/flaw/plugins/version_control/git_lab/project/link/" + projectId + "/" + uid, (data) =>{
      callback(data);
    }, (e) => error(e))
  }
}





class GitPanel{
  static CLASS_NAME = "git-panel__";

  static ERROR_INPUT_CLASS = "input__error-border";
  static ADD_BTN = "gitlab.branch.add.success";
  static ADD_PROJECT = "gitlab.project.connection.add";
  static REMOVE_PROJECT = "gitlab.project.connection.remove";
  static ADD_PROJECT_ATTENTION = "gitlab.project.connection.no_project";
  static CONNECT_NOW = "gitlab.project.connection.connect.now.question";

  #data;

  #projectChooser;

  #gitConfigId

  #form;

  #mainBranch;

  #redrawIssue;

  #container = h.div(GitPanel.CLASS_NAME + "container");
  #header = h.div(GitPanel.CLASS_NAME + "header").appendTo(this.#container);
  #projectBtn = h.div(GitPanel.CLASS_NAME + "project-btn").pointer().appendTo(this.#header);
  #wrapper = h.div(GitPanel.CLASS_NAME + "wrapper");
  #addBtn = h.div(GitPanel.CLASS_NAME + "add").text("global.app.list.search.add").appendTo(this.#wrapper);
  #input = h.textElement(GitPanel.CLASS_NAME + "input").appendTo(this.#wrapper);
  #branches = h.div(GitPanel.CLASS_NAME + "branches-container").appendTo(this.#wrapper);
  #chooseProjectPanel = h.div(GitPanel.CLASS_NAME + "choose-project").appendTo(this.#container);

  constructor(data, redrawIssue){
    this.#data = data;

    this.#redrawIssue = redrawIssue;

    this.init();
  }

  init() {
    if(this.checkProject()){

      this.buldBranchName();

      this.drawBranches();
  
      this.#addBtn.pointer().click(this.addBranch);
    }
  }

  checkProject() {
    let project = this.#data.project;

    if(!project.value && !project.key) {
      this.initProjectChooser();

      return false;
    }
    return true;
  }

  initProjectChooser() {
    let header = {
      getData() {
          return {
              searchName: 'project',
              searchField: 'name',
          };
      },
      getField() {
          return 'name';
      },
      getType() {
          return EditFlow.OBJECT_CHOOSER;
      },
    };

    let remoteChooserDiv = h.div('remote_selection');

    let remoteChooserInput = h.input(dom.HIDDEN);

    this.#projectChooser = new MagicRemoteChooser(remoteChooserDiv, {}, remoteChooserInput, 'dataType', header, this );

    this.#chooseProjectPanel.add(h.span(GitPanel.CLASS_NAME + "attention").text(GitPanel.ADD_PROJECT_ATTENTION))
      .add(h.span(GitPanel.CLASS_NAME + "choose-project-btn").pointer().text(GitPanel.CONNECT_NOW).click(() => this.addFlowProject()));
  }

  getFormFieldListener(){
    return (event) =>{
      if(_e(event.type, MagicRemoteChooser.EVENT_ITEM_SELECTED)){

        let payload = transformIssue(this.#data)

        payload.project = event.additional.id;

        Executor.runPutWithPayload(Environment.dataApi + "/api/v2/flaw/issue/edit", (data) => {
          if(this.#redrawIssue) {
            this.#redrawIssue(data)
          }

          this.drawBranches();

          this.#chooseProjectPanel.text(null)
        }, payload )
      }
    }
  }

  addFlowProject() {
    this.#projectChooser.showForm();
  }

  getContainer(){
    return this.#container
  }

  buldBranchName(){

    let d = this.#data;

    let issueFlow = d.issueFlow.value;

    let name = transliterate(d.name.replace(/\s+/g, ' ').toLowerCase());

    let str = (d.id + "_" + issueFlow + "_" + name).toLowerCase()
    
    str = this.filterStr(str);

    this.#input.setVal(str)
  }

  filterStr(str){
    str = transliterate(str)

    str = str
              .replace(/\s+/g, ' ')
              .toLowerCase().replace(/ /g, "_")
              .replace(/[^a-zа-яё0-9\s]/gi, '_');

    str = removeLastSymbols("_", str);

    str = removeIdenticallySubling(str, "_");

    return str;
  }

  addBranch = () => {

    let branchName = this.#input.val();

    branchName = this.filterStr(branchName)

    if(!branchName.includes(this.#data.id)){
      branchName = this.#data.id + "_" + branchName;
    }

    const payload = {
      name: branchName,
      sourceBranch: 0,
      gitConfig: this.#gitConfigId
    }

    GitController.addBranch(payload, (data) => {
      this.drawBranch(data);

      this.#input.text(null).rcl(GitPanel.ERROR_INPUT_CLASS);

      dom.toast(MagicPage.translate(GitController.ADD_SUCCESS) )

    }, (e) => {

      this.#input.cl(GitPanel.ERROR_INPUT_CLASS);
      
      dom.toast(MagicPage.translate(GitController.ADD_FAIL) )
    });
  }

  loadAddForm = () => {

    let dataType = 'version_control/git_lab/config';

    let magicFormOptions = new MagicFormOptions( dataType);  

    magicFormOptions.setPath("https://vc.dennis.systems/");

    magicFormOptions.setFormListener((event) => this.formListener(event));
    
    this.#form = new MagicForm(magicFormOptions);

    this.#form.load();
  }
  
  formListener = (event) => {
    if(_e(event.type, MagicFormEvent.FORM_SAVED)) {

      let uid = event.additional.uid;

      let projectId = this.#data.project.key;

      GitController.buildConnectionToProject(projectId, uid, (data) => {

        GitController.getBranch(event.additional.id, (data) => {

          this.#gitConfigId = event.additional.id;
          
          if(data && data.content && data.content.length) {

            GitController.getProject(this.#data.project.key, (project) => {
              this.#projectBtn.text(GitPanel.REMOVE_PROJECT).click(() => this.removeProjectGit(project[0].id))
            })
            
            this.redrawContainer(data);

            this.buldBranchName()

            dom.toast(MagicPage.translate(GitController.ADD_PROJECT_SUCCESS))
          }
        })
      
      },  (error) => {dom.toast(MagicPage.translate(GitController.ADD_PROJECT_FAIL))})
    }
  }

  redrawContainer(data){
    this.#mainBranch = data.content[0];

    this.#wrapper.appendTo(this.#container);

    this.#addBtn.appendTo(this.#wrapper);

    this.#input.appendTo(this.#wrapper);

    this.#branches.appendTo(this.#wrapper);

    _.each(data.content, (item) => {

      this.drawBranch(item)
    })
  }

  removeProjectGit = (id) => {
    GitController.deleteProject(id, (data) => {

      this.#projectBtn.text(GitPanel.ADD_PROJECT).click(() => this.loadAddForm(id));

      this.#wrapper.text(null);

      this.#branches.text(null);

      dom.toast(MagicPage.translate(GitController.DELETE_PROJECT_SUCCESS))

    }, (e) => { dom.toast(MagicPage.translate(GitController.DELETE_PROJECT_FAIL)) })
  }

  drawBranches(){
    GitController.getProject(this.#data.project.key, (project) => {
      
      this.#projectBtn.text(GitPanel.ADD_PROJECT).click(() => this.loadAddForm());

      if(project && project[0]){

        this.#gitConfigId = project[0].gitConfig.id;
        
        h.div(GitPanel.CLASS_NAME + 'project-name').text(project[0].gitConfig.projectName, false).prependTo(this.#header)
        
        GitController.getBranch(project[0].gitConfig.id, (data) => {
          
          if(data && data.content && data.content.length) {

            this.#projectBtn.text(GitPanel.REMOVE_PROJECT).click(() => this.removeProjectGit(project[0].id))
  
            this.#wrapper.appendTo(this.#container)
  
            this.#mainBranch = data.content[0]
  
            _.each(data.content, (item) => {
              this.drawBranch(item)
            })
          }
        })
      }
    }, (e) => {
      this.#projectBtn.text(GitPanel.ADD_PROJECT).click(() => this.loadAddForm());
    })
  }

  drawBranch(data){
    let item = h.div(GitPanel.CLASS_NAME + "branch").appendTo(this.#branches)

    item.add(h.span(GitPanel.CLASS_NAME + "branch-name").text(data.name, false))

    item.add(h.img('rm.png', Environment.defaultIconSize).pointer().click(() => this.removeBranch(item, data)));

    item.add(h.span(GitPanel.CLASS_NAME + "merge").pointer().text("merge", false).click(() => this.mergeBranch(data)))
  }

  mergeBranch(branch){
    if(!_e(branch.id, this.#mainBranch.id)) {

      GitController.mergeBranch(branch.id, this.#mainBranch.id, (data) => {
        dom.toast(MagicPage.translate(GitController.MERGED_SUCCESS))
        
      },(er) => { 
        dom.toast(MagicPage.translate(GitController.MERGED_FAIL))
      })

    }
  }

  removeBranch(divH, branch){
    GitController.deleteBranch(branch.id, (data) => {

      dom.toast(MagicPage.translate(GitController.DELETE_SUCCESS));

      divH.remove();
    }, (e) => {
      dom.toast(MagicPage.translate(GitController.DELETE_FAIL));
    })
  }
}

function removeIdenticallySubling(str, symb) {
  let isIdentically = false;
  
  let arrFromStr = Array.from(str);

  _.each(arrFromStr, (l, i) => {

    if(arrFromStr[i - 1] && _e(arrFromStr[i -1], symb) && _e(l, symb)){

      arrFromStr.splice(i, 1);
      
      isIdentically = true;
    }
  })

  let newStr = arrFromStr.join("");

  if(!isIdentically) return newStr

  return removeIdenticallySubling(newStr, symb)
}

function removeLastSymbols(symbol, str) {
  if(_e(_.last(str), symbol)){

      str = str.substring(0, str.length - 1);
    } else {
      return str;
    }
  
  return removeLastSymbols(symbol, str);
}


let transliterate = function(text) {

    text = text
    .replace(/\u0451/g, 'yo')
    .replace(/\u0439/g, 'i')
    .replace(/\u0446/g, 'ts')
    .replace(/\u0443/g, 'u')
    .replace(/\u043A/g, 'k')
    .replace(/\u0435/g, 'e')
    .replace(/\u043D/g, 'n')
    .replace(/\u0433/g, 'g')
    .replace(/\u0448/g, 'sh')
    .replace(/\u0449/g, 'sch')
    .replace(/\u0437/g, 'z')
    .replace(/\u0445/g, 'h')
    .replace(/\u044A/g, "")
    .replace(/\u0410/g, 'a')
    .replace(/\u0444/g, 'f')
    .replace(/\u044B/g, 'i')
    .replace(/\u0432/g, 'v')
    .replace(/\u0430/g, 'a')
    .replace(/\u043F/g, 'p')
    .replace(/\u0440/g, 'r')
    .replace(/\u043E/g, 'o')
    .replace(/\u043B/g, 'l')
    .replace(/\u0434/g, 'd')
    .replace(/\u0436/g, 'zh')
    .replace(/\u044D/g, 'e')
    .replace(/\u044F/g, 'ya')
    .replace(/\u0447/g, 'ch')
    .replace(/\u0441/g, 's')
    .replace(/\u043C/g, 'm')
    .replace(/\u0438/g, 'i')
    .replace(/\u0442/g, 't')
    .replace(/\u044C/g, "")
    .replace(/\u0431/g, 'b')
    .replace(/\u044E/g, 'yu');

  return text;

};

function transformIssue(data) {
  let issue = {...data};

  _.each(Object.keys(issue), key => {

    if(issue[key]){
      if(_e(issue[key].key, 0) || issue[key].key) {

        issue[key] = issue[key].key;
      }
  }
  })

  return issue;
}

class ProjectWithGit{
  static MENU_ACTIONS = [
    {
      name: 'delete',
      allowOnMultiSelect: false 
    },
    { 
      name: 'edit', 
      allowOnMultiSelect: false 
    }
  ]

  #container = h.div(GitProjectList.CLASS_NAME + "project");
  #projectInfo = h.div(GitProjectList.CLASS_NAME + "project-info").appendTo(this.#container);
  #gitProject = h.div(GitProjectList.CLASS_NAME + "git-projects").appendTo(this.#container);
  #addProjectBtn = h.div(GitPanel.CLASS_NAME + "project-btn").text("gitlab.project.connection.add").pointer().appendTo(this.#gitProject).hide();

  #project;
  #data;
  #parent;
  #connect;

  constructor(data, project, parent) {
    this.#data = data;

    this.#project = project;

    this.#parent = parent;

    this.initMenu();

    this.drawProjectInfo();

    this.drawConnections();
  }

  drawProjectInfo(){
    this.#projectInfo.text(null)
      .addIf(this.#project.icon, h.img(this.#project.icon, Environment.defaultIconSize))
      .add(h.div(GitProjectList.CLASS_NAME + "project-name").text(this.#project.name, false))
      .add(h.div(GitProjectList.CLASS_NAME + "project-id").text("ID " + this.#project.id, false));
  }

  formListener = (event) => {
    if(_e(event.type, MagicFormEvent.FORM_SAVED)) {
      this.#gitProject.text(null);

      this.#data[0].gitConfig = event.additional;

      this.buildGitProject(this.#data[0])
    }
  }

  initMenu() {
    let options = new MagicMenuOptions(this.#container, '', this, GitProjectList.CLASS_NAME + "project").setActions(IssueToIssue.actionArray);

    options.setListner(this.menuListener);

    new MagicMenu(options);
  }

  menuListener = (event) => {
    if(_e(event.eventType, "delete")) {
      this.delete();
    }

    if(_e(event.eventType, "edit")) {
      GitProjectList.loadForm(this.formListener, this.#data[0].gitConfig.id);
    }
  }

  delete() {
    GitController.deleteProject(this.#data[0].id, () => {
      this.#gitProject.text(null);

      this.#addProjectBtn.appendTo(this.#gitProject).show().click(() => GitProjectList.loadForm(this.addFormListener))
    }, (e) => {})
  }

  drawConnections(){

    if(this.#data.length) {
      _.each(this.#data, (connect) => {
      
        this.buildGitProject(connect);
      })
    } else {
      this.#addProjectBtn.show().click(() => GitProjectList.loadForm(this.addFormListener))
    }
  }

  addFormListener = (event) => {
    if(_e(event.type, MagicFormEvent.FORM_SAVED)) {


      let uid = event.additional.uid;

      let projectId = this.#project.id;

      GitController.buildConnectionToProject(projectId, uid, () => {
        GitController.getProject(projectId, (data) => {

          this.#data = data;

          this.#gitProject.text(null);
          this.buildGitProject(data[0])
        }, (e) =>  {})
      
      },  (error) => {dom.toast(MagicPage.translate(GitController.ADD_PROJECT_FAIL))})
    }
  }

  buildGitProject(connect) {
    this.#connect = connect;
    let git = connect.gitConfig;

      this.#gitProject.add(this.getRow('id', "ID", connect.id))
                       .add(this.getRow('name', 'name', git.projectName))
                       .add(this.getRow('remotePath', "remotePath", git.remotePath, true))
                       .add(this.getRow('main_branch', "mainBranch", git.mainBranch))
  }

  getContainer() {
    return this.#container;
  }

  getRow(label, field, value, isLink = false){
    let row = h.div(GitProjectList.GIT_CLASS + 'row');

    row.add(h.span(GitProjectList.GIT_CLASS + "label").text(GitProjectList.LABEL + label))

    if(isLink) {
      row.add(h.a(value, value, false, true).cl(GitProjectList.GIT_CLASS + field));

      return row;
    }

    row.add(h.span(GitProjectList.GIT_CLASS + field).text(value, false));

    return row;
  }
}

class GitProjectList {
  static CLASS_NAME = "git-projects__";
  static GIT_CLASS = "git-projects__config-"
  static LABEL = "gitlab.config.label."
  
  #content = h.div(GitProjectList.CLASS_NAME + 'content');
  
  #projects = [];

  #projectsWithGit = [];
  
  constructor(){
    this.draw();
  }

  draw(){
    this.#content.text(null);
    this.getProjects();
  }
  
  getProjects() {
    let payload = new MagicRequest();

    let id = MagicPage.getPageParam(Group.GROUP_PARAM);

    payload.query.push({
        'field': "group",
        "value": parseInt(id),
        "searchName": "group",
        "dataType": "object_chooser",
        "type": "="
    })

    Executor.runPostWithPayload(Environment.dataApi + "/api/v2/flaw/project/root/fetch/data", (data) => {
      if(data && data.content && data.content.length) {
        this.#projects = data.content;

        this.getProjectsConnection();
      }

    }, payload)
  }

  getProjectsConnection() {
    _.each(this.#projects, (project) => {
      GitController.getProject(project.id, (data) => {

        let projectWithGit = new ProjectWithGit(data, project, this);

        this.#projectsWithGit.push(projectWithGit);

        this.#content.add(projectWithGit.getContainer())
      }, (e) =>  {})
    })
  }

  getContent(){
    return this.#content;
  }

  static loadForm(listener, id = null){
    let dataType = 'version_control/git_lab/config';

    let magicFormOptions = new MagicFormOptions(dataType);  

    magicFormOptions.setPath("https://vc.dennis.systems/");

    magicFormOptions.setFormListener((event) => listener(event));
    
    MagicForm.Instance(dataType, magicFormOptions).load(id);
  }
}