// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
// @author Roman Rezinkin roman.rezinkin@hotmail.com
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })

import "cypress-file-upload";
import { isXcdb, isPostgres } from "./page_objects/projectConstants";

require("@4tw/cypress-drag-drop");

// for waiting until page load
Cypress.Commands.add("waitForSpinners", () => {
    cy.visit("http://localhost:3000", {
        retryOnNetworkFailure: true,
        timeout: 1200000,
        headers: {
            "Accept-Encoding": "gzip, deflate",
        },
    });
    cy.get("#nuxt-loading", { timeout: 10_0000 }).should("have.length", 0);
});

Cypress.Commands.add("signinOrSignup", (_args) => {
    const args = Object.assign(
        { username: "user@nocodb.com", password: "Password123." },
        _args
    );

    cy.wait(1000);

    // signin/signup
    cy.get("body").then(($body) => {
        // cy.wait(1000)
        cy.url().then((url) => {
            if (!url.includes("/projects")) {
                // handle initial load
                if ($body.find(".welcome-page").length > 0) {
                    cy.wait(8000);
                    cy.get("body").trigger("mousemove");
                    cy.snip("LetsBegin");
                    cy.contains("Let's Begin").click();
                    cy.get('input[type="text"]', { timeout: 12000 }).type(
                        args.username
                    );
                    cy.get('input[type="password"]').type(args.password);
                    cy.snip("SignUp");
                    cy.get('button:contains("SIGN UP")').click();

                    // handle signin
                } else {
                    cy.get('input[type="text"]', { timeout: 12000 }).type(
                        args.username
                    );
                    cy.get('input[type="password"]').type(args.password);
                    cy.snip("SignIn");
                    cy.get('button:contains("SIGN IN")').click();
                }
            } else if (url.includes("/signin")) {
                cy.get('input[type="text"]', { timeout: 12000 }).type(
                    args.username
                );
                cy.get('input[type="password"]').type(args.password);
                cy.snip("SignIn");
                cy.get('button:contains("SIGN IN")').click();
            }
        });
    });

    // indicates page-load complete
    cy.get(".nc-noco-brand-icon", { timeout: 12000 }).should("exist");
});

// for opening/creating a rest project
Cypress.Commands.add("openOrCreateRestProject", (_args) => {
    const args = Object.assign({ new: false }, _args);

    // signin/signup
    cy.signinOrSignup();
    cy.get(".nc-new-project-menu").should("exist");
    cy.snip("ProjectPage");
    cy.get("body").then(($body) => {
        const filter = args.meta
            ? ".nc-meta-project-row"
            : ":not(.nc-meta-project-row)";
        // if project exist open
        if (
            $body.find(".nc-rest-project-row").filter(filter).length &&
            !args.new
        ) {
            cy.get(".nc-rest-project-row").filter(filter).first().click();
        } else {
            cy.contains("New Project")
                .trigger("onmouseover")
                .trigger("mouseenter");
            if (args.meta) {
                cy.get(".nc-create-xc-db-project").click();
                cy.url({ timeout: 6000 }).should("contain", "#/project/xcdb");
                cy.get(".nc-metadb-project-name").type(
                    "test_proj" + Date.now()
                );
                cy.contains("button", "Create", { timeout: 3000 }).click();
            } else {
                cy.get(".nc-create-external-db-project").click();
                cy.url({ timeout: 6000 }).should("contain", "#/project");
                cy.get(".database-field input").click().clear().type("sakila");
                cy.contains("Test Database Connection").click();
                cy.contains("Ok & Save Project", { timeout: 3000 }).click();
            }
        }
    });
    cy.url({ timeout: 20000 }).should("contain", "#/nc/");
});

Cypress.Commands.add("refreshTableTab", () => {
    cy.task("log", `[refreshTableTab]`);

    cy.get(".nc-project-tree")
        .find(".v-list-item__title:contains(Tables)", { timeout: 10000 })
        .should("exist")
        .first()
        .rightclick({ force: true });

    cy.getActiveMenu()
        .find('[role="menuitem"]')
        .contains("Tables Refresh")
        .should("exist")
        .click({ force: true });

    cy.toastWait("Tables refreshed");
});

// tn: table name
// rc: row count. validate row count if rc!=0
Cypress.Commands.add("openTableTab", (tn, rc) => {
    cy.task("log", `[openTableTab] ${tn} ${rc}`);

    cy.get(".nc-project-tree")
        .find(".v-list-item__title:contains(Tables)", { timeout: 10000 })
        .should("exist")
        .first()
        .click({ force: true });

    cy.get(".nc-project-tree")
        .contains(tn)
        .should('exist')
        .first()
        .click({ force: true });

    cy.get(`.project-tab`).contains(tn).should("exist");

    cy.get(".nc-project-tree")
        .find(".v-list-item__title:contains(Tables)")
        .should('exist')
        .first()
        .click({ force: true });

    // wait for page rendering to complete
    if (rc != 0) {
        cy.get(".nc-grid-row").should("have.length", rc);
    }

    // kludge: add delay to skip flicker
    cy.wait(3000)
    cy.snip(`GridView_${tn}`);
});

Cypress.Commands.add("closeTableTab", (tn) => {
    cy.task("log", `[closeTableTab] ${tn}`);
    cy.get(`.project-tab`).contains(tn).should("exist");
    cy.get(`[href="#table||||${tn}"]`).find("button.mdi-close").click();
});

Cypress.Commands.add("openOrCreateGqlProject", (_args) => {
    const args = Object.assign({ new: false, meta: false }, _args);

    cy.signinOrSignup();

    cy.get(".nc-new-project-menu").should("exist");
    cy.get("body").then(($body) => {
        const filter = args.meta
            ? ".nc-meta-project-row"
            : ":not(.nc-meta-project-row)";
        // if project exist open
        if (
            $body.find(".nc-graphql-project-row").filter(filter).length &&
            !args.new
        ) {
            cy.get(".nc-graphql-project-row").filter(filter).first().click();
        } else {
            cy.contains("New Project")
                .trigger("onmouseover")
                .trigger("mouseenter");
            if (args.meta) {
                cy.get(".nc-create-xc-db-project").click();
                cy.url({ timeout: 6000 }).should("contain", "#/project/xcdb");
                cy.contains("GRAPHQL APIs").closest("label").click();
                cy.get(".nc-metadb-project-name").type(
                    "test_proj" + Date.now()
                );
                cy.contains("button", "Create", { timeout: 3000 }).click();
            } else {
                cy.get(".nc-create-external-db-project").click();
                cy.url({ timeout: 6000 }).should("contain", "#/project");
                cy.contains("GRAPHQL APIs").closest("label").click();
                cy.get(".database-field input").click().clear().type("sakila");
                cy.contains("Test Database Connection").click();
                cy.contains("Ok & Save Project").should('exist').click();
            }
        }
    });
    cy.url({ timeout: 20000 }).should("contain", "#/nc/");
});

let LOCAL_STORAGE_MEMORY = {};

Cypress.Commands.add("saveLocalStorage", () => {
    Object.keys(localStorage).forEach((key) => {
        LOCAL_STORAGE_MEMORY[key] = localStorage[key];
    });
});

Cypress.Commands.add("restoreLocalStorage", () => {
    Object.keys(LOCAL_STORAGE_MEMORY).forEach((key) => {
        localStorage.setItem(key, LOCAL_STORAGE_MEMORY[key]);
    });
});

Cypress.Commands.add("getActiveModal", () => {
    return cy.get(".v-dialog.v-dialog--active").last();
});

Cypress.Commands.add("getActiveMenu", () => {
    return cy.get(".menuable__content__active").last();
});

Cypress.Commands.add("getActiveContentModal", () => {
    return cy.get(".v-dialog__content--active").last();
});

Cypress.Commands.add("createTable", (name) => {
    cy.get(".add-btn").click();
    cy.getActiveMenu().contains("Add new table").should('exist').click()
    cy.get('.nc-create-table-card .nc-table-name input[type="text"]')
        .first()
        .click()
        .clear()
        .type(name);

    // cy.log(isXcdb());
    // if (!isXcdb()) {
    //   cy.get('.nc-create-table-card .nc-table-name-alias input[type="text"]')
    //     .first()
    //     .should("have.value", name.toLowerCase());
    // }

    cy.snip("CreateTable");
    cy.snipActiveModal("Modal_CreateTable");

    cy.get(".nc-create-table-card .nc-create-table-submit").first().click();
    cy.toastWait(`Create table successful`);
    cy.get(`.project-tab:contains(${name})`).should("exist");
    cy.url().should("contain", `name=${name}`);
});

Cypress.Commands.add("deleteTable", (name, dbType) => {
    cy.get(".nc-project-tree")
        .find(".v-list-item__title:contains(Tables)")
        .should('exist')
        .first()
        .click();
    cy.get(".nc-project-tree")
        .contains(name)
        .should('exist')
        .first()
        .click({ force: true });
    cy.get(`.project-tab:contains(${name}):visible`).should("exist");
    cy.get(".nc-table-delete-btn:visible").click();
    cy.snipActiveModal("Modal_DeleteTable");
    cy.get("button:contains(Submit)").click();

    // only for postgre project
    if (dbType === "postgres") cy.toastWait(`Delete trigger successful`);

    cy.toastWait(`Delete table successful`);
});

Cypress.Commands.add("renameTable", (oldName, newName) => {
    // expand project tree
    cy.get(".nc-project-tree")
        .find(".v-list-item__title:contains(Tables)")
        .should('exist')
        .first()
        .click();

    // right click on project table name
    cy.get(".nc-project-tree")
        .contains(oldName)
        .should('exist')
        .first()
        .rightclick();

    // choose rename option from menu
    cy.getActiveMenu()
        .find('[role="menuitem"]')
        .contains("Table Rename")
        .click({ force: true });

    // feed new name
    cy.getActiveContentModal().find("input").clear().type(newName);

    cy.snipActiveModal("Modal_RenameTable");
    // submit
    cy.getActiveContentModal().find("button").contains("Submit").click();

    cy.toastWait("Table renamed successfully");

    // close expanded project tree
    cy.get(".nc-project-tree")
        .find(".v-list-item__title:contains(Tables)")
        .should('exist')
        .first()
        .click();

    cy.wait(8000)
});

Cypress.Commands.add("createColumn", (table, columnName) => {
    cy.get(".nc-project-tree")
        .find(".v-list-item__title:contains(Tables)")
        .should('exist')
        .first()
        .click();
    cy.get(".nc-project-tree")
        .contains(table)
        .should('exist')
        .first()
        .click({ force: true });
    cy.get(`.project-tab:contains(${table}):visible`).should("exist");
    cy.get(".v-window-item--active .nc-grid  tr > th:last button").click({
        force: true,
    });
    cy.get(".nc-column-name-input input").clear().type(columnName);
    cy.getActiveMenu("Menu_CreateColumn");
    cy.get(".nc-col-create-or-edit-card").contains("Save").click();
    cy.get("th:contains(new_column)").should("exist");
});

Cypress.Commands.add("toastWait", (msg) => {
    // cy.get(".toasted:visible", { timout: 12000 }).contains(msg).should("exist");
    // cy.get(".toasted:visible", { timout: 12000 })
    //     .contains(msg)
    //     .should("not.exist");
    cy.wait(3000);
});

// vn: view name
// rc: expected row count. validate row count if rc!=0
Cypress.Commands.add("openViewsTab", (vn, rc) => {
    cy.task("log", `[openViewsTab] ${vn} ${rc}`);

    cy.get(".nc-project-tree")
        .find(".v-list-item__title:contains(Tables)")
        .should("exist")
        .first()
        .click({ force: true });

    cy.get(".nc-project-tree")
        .contains(vn, { timeout: 6000 })
        .first()
        .click({ force: true });

    cy.get(`.project-tab`).contains(vn).should("exist");

    cy.get(".nc-project-tree")
        .find(".v-list-item__title:contains(Tables)")
        .should('exist')
        .first()
        .click({ force: true });

    // wait for page rendering to complete
    if (rc != 0) {
        cy.get(".nc-grid-row").should("have.length", rc);
    }
});

Cypress.Commands.add("closeViewsTab", (vn) => {
    cy.task("log", `[closeViewsTab] ${vn}`);
    cy.get(`.project-tab`).contains(vn).should("exist");
    cy.get(`[href="#view||||${vn}"]`)
        .find("button.mdi-close")
        .click({ force: true });
});

// Support for screen-shots
//

let screenShotDb = [];

// snip entire screen
Cypress.Commands.add("snip", (filename) => {
    if (
        true === Cypress.env("screenshot") &&
        false === screenShotDb.includes(filename)
    ) {
        let storeName = `${screenShotDb.length}_${filename}`;
        screenShotDb.push(filename);
        cy.wait(1000);
        cy.screenshot(storeName, { overwrite: true });
    }
});

// snip current modal
Cypress.Commands.add("snipActiveModal", (filename) => {
    if (
        true === Cypress.env("screenshot") &&
        false === screenShotDb.includes(filename)
    ) {
        let storeName = `${screenShotDb.length}_${filename}`;
        screenShotDb.push(filename);
        cy.wait(1000);
        // cy.getActiveModal().screenshot(filename, {
        //     padding: 0,
        //     overwrite: true,
        // });
        cy.screenshot(storeName, { overwrite: true });
    }
});

// snip current menu
Cypress.Commands.add("snipActiveMenu", (filename) => {
    if (
        true === Cypress.env("screenshot") &&
        false === screenShotDb.includes(filename)
    ) {
        let storeName = `${screenShotDb.length}_${filename}`;
        screenShotDb.push(filename);
        cy.wait(1000);
        // cy.getActiveMenu().screenshot(filename, {
        //     padding: 0,
        //     overwrite: true,
        // });
        cy.screenshot(storeName, { overwrite: true });
    }
});


// Drag n Drop
// refer: https://stackoverflow.com/a/55409853
/*

const getCoords = ($el) => {
  const domRect = $el[0].getBoundingClientRect()
  const coords = { x: domRect.left + (domRect.width / 2 || 0), y: domRect.top + (domRect.height / 2 || 0) }

  return coords
}

const dragTo = (subject, to, opts) => {

  opts = Cypress._.defaults(opts, {
    // delay inbetween steps
    delay: 0,
    // interpolation between coords
    steps: 0,
    // >=10 steps
    smooth: false,
  })

  if (opts.smooth) {
    opts.steps = Math.max(opts.steps, 10)
  }

  const win = subject[0].ownerDocument.defaultView

  const elFromCoords = (coords) => win.document.elementFromPoint(coords.x, coords.y)
  const winMouseEvent = win.MouseEvent

  const send = (type, coords, el) => {

    el = el || elFromCoords(coords)

    el.dispatchEvent(
      new winMouseEvent(type, Object.assign({}, { clientX: coords.x, clientY: coords.y }, { bubbles: true, cancelable: true }))
    )
  }

  const toSel = to

  function drag (from, to, steps = 1) {

    const fromEl = elFromCoords(from)

    const _log = Cypress.log({
      $el: fromEl,
      name: 'drag to',
      message: toSel,
    })

    _log.snapshot('before', { next: 'after', at: 0 })

    _log.set({ coords: to })

    send('mouseover', from, fromEl)
    send('mousedown', from, fromEl)

    cy.then(() => {
      return Cypress.Promise.try(() => {

        if (steps > 0) {

          const dx = (to.x - from.x) / steps
          const dy = (to.y - from.y) / steps

          return Cypress.Promise.map(Array(steps).fill(), (v, i) => {
            i = steps - 1 - i

            let _to = {
              x: from.x + dx * (i),
              y: from.y + dy * (i),
            }

            send('mousemove', _to, fromEl)

            return Cypress.Promise.delay(opts.delay)

          }, { concurrency: 1 })
        }
      })
        .then(() => {

          send('mousemove', to, fromEl)
          send('mouseover', to)
          send('mousemove', to)
          send('mouseup', to)
          _log.snapshot('after', { at: 1 }).end()

        })

    })

  }

  const $el = subject
  const fromCoords = getCoords($el)
  const toCoords = getCoords(cy.$$(to))

  drag(fromCoords, toCoords, opts.steps)
}

Cypress.Commands.addAll(
  { prevSubject: 'element' },
  {
    dragTo,
  }
)
*/