import { defineStore } from "pinia";
import {
  createLayout,
  scrubFieldNames,
  sectionBoilerplate,
} from "./utils";
import { computed, nextTick, ref } from "vue";
import { useDebouncedRefHistory, onKeyDown, useActiveElement } from "@vueuse/core";
import { t } from '@/shared/i18n';

// Field types supported by the form builder
export const ALL_FIELDTYPES = [
  "Autocomplete",
  "Attach",
  "Attach Image",
  "Barcode",
  "Button",
  "Check",
  "Code",
  "Color",
  "Content",
  "Cron Editor",
  "Currency",
  "Data",
  "Date",
  "Datetime",
  "Duration",
  "Dynamic Link",
  "Float",
  "Geolocation",
  "Heading",
  "HTML",
  "HTML Editor",
  "Icon",
  "Image",
  "Int",
  "Jeditor",
  "JSON",
  "Link",
  "Long Text",
  "Markdown Editor",
  "Password",
  "Percent",
  "Phone",
  "Rating",
  "Read Only",
  "Select",
  "Signature",
  "Small Text",
  "Table",
  "Table MultiSelect",
  "Text",
  "Text Editor",
  "Time",
];

// Layout fields (not actual data fields)
export const LAYOUT_FIELDS = ["Tab Break", "Section Break", "Column Break"];

// Fields that don't hold values
export const NO_VALUE_TYPES = [
  "Button",
  "Column Break",
  "Heading",
  "HTML",
  "Image",
  "Section Break",
  "Tab Break",
];

// Table field types
export const TABLE_FIELDS = ["Table", "Table MultiSelect"];

// Restricted field names
export const RESTRICTED_FIELDS = [
  "name",
  "creation",
  "modified",
  "modified_by",
  "owner",
  "pagestatus",
  "idx",
  "parent",
  "parenttype",
  "parentfield",
];

// Default PageField properties for the properties panel
// Order follows jingrow's PageField.field_order
export const DEFAULT_PAGEFIELD_PROPERTIES = [
  // Label and Type section
  { fieldname: "label", label: t("Label"), fieldtype: "Data" },
  { fieldname: "fieldtype", label: t("Field Type"), fieldtype: "Select", options: [...ALL_FIELDTYPES, ...LAYOUT_FIELDS].join("\n") },
  { fieldname: "fieldname", label: t("Fieldname"), fieldtype: "Data" },
  { fieldname: "precision", label: t("Precision"), fieldtype: "Select", options: "\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9", depends_on: "eval:in_list(['Float', 'Currency', 'Percent'], pg.fieldtype)" },
  { fieldname: "length", label: t("Length"), fieldtype: "Int", depends_on: "eval:in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image', 'Int'], pg.fieldtype)" },
  { fieldname: "non_negative", label: t("Non Negative"), fieldtype: "Check", depends_on: "eval:in_list(['Int', 'Float', 'Currency'], pg.fieldtype)" },
  { fieldname: "hide_days", label: t("Hide Days"), fieldtype: "Check", depends_on: "eval:pg.fieldtype=='Duration'" },
  { fieldname: "hide_seconds", label: t("Hide Seconds"), fieldtype: "Check", depends_on: "eval:pg.fieldtype=='Duration'" },
  { fieldname: "reqd", label: t("Mandatory"), fieldtype: "Check" },
  { fieldname: "is_virtual", label: t("Virtual"), fieldtype: "Check" },
  { fieldname: "search_index", label: t("Index"), fieldtype: "Check" },
  
  // Column Break
  { fieldname: "column_break_18", fieldtype: "Column Break" },
  
  { fieldname: "options", label: t("Options"), fieldtype: "Small Text" },
  { fieldname: "sort_options", label: t("Sort Options"), fieldtype: "Check", depends_on: "eval:pg.fieldtype=='Select'" },
  { fieldname: "show_dashboard", label: t("Show Dashboard"), fieldtype: "Check", depends_on: "eval:pg.fieldtype=='Tab Break'" },
  { fieldname: "link_filters", label: t("Link Filters"), fieldtype: "Code", options: "JSON", depends_on: "eval:pg.fieldtype=='Link'" },
  
  // Defaults Section
  { fieldname: "defaults_section", fieldtype: "Section Break", label: t("Defaults") },
  { fieldname: "default", label: t("Default"), fieldtype: "Small Text" },
  { fieldname: "column_break_6", fieldtype: "Column Break" },
  { fieldname: "fetch_from", label: t("Fetch From"), fieldtype: "Small Text" },
  { fieldname: "fetch_if_empty", label: t("Fetch If Empty"), fieldtype: "Check" },
  
  // Visibility Section
  { fieldname: "visibility_section", fieldtype: "Section Break", label: t("Visibility") },
  { fieldname: "hidden", label: t("Hidden"), fieldtype: "Check" },
  { fieldname: "show_on_timeline", label: t("Show on Timeline"), fieldtype: "Check", depends_on: "eval:pg.hidden" },
  { fieldname: "bold", label: t("Bold"), fieldtype: "Check" },
  { fieldname: "allow_in_quick_entry", label: t("Allow in Quick Entry"), fieldtype: "Check" },
  { fieldname: "translatable", label: t("Translatable"), fieldtype: "Check", depends_on: "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(pg.fieldtype)" },
  { fieldname: "print_hide", label: t("Print Hide"), fieldtype: "Check" },
  { fieldname: "print_hide_if_no_value", label: t("Print Hide If No Value"), fieldtype: "Check", depends_on: "eval:['Int', 'Float', 'Currency', 'Percent'].indexOf(pg.fieldtype)!==-1" },
  { fieldname: "report_hide", label: t("Report Hide"), fieldtype: "Check" },
  { fieldname: "column_break_28", fieldtype: "Column Break" },
  { fieldname: "depends_on", label: t("Display Depends On (JS)"), fieldtype: "Code", options: "JS" },
  { fieldname: "collapsible", label: t("Collapsible"), fieldtype: "Check", depends_on: "eval:pg.fieldtype=='Section Break'" },
  { fieldname: "collapsible_depends_on", label: t("Collapsible Depends On (JS)"), fieldtype: "Code", options: "JS", depends_on: "eval:pg.collapsible" },
  { fieldname: "hide_border", label: t("Hide Border"), fieldtype: "Check", depends_on: "eval:pg.fieldtype=='Section Break'" },
  
  // List / Search Settings Section
  { fieldname: "list__search_settings_section", fieldtype: "Section Break", label: t("List / Search Settings") },
  { fieldname: "in_list_view", label: t("In List View"), fieldtype: "Check" },
  { fieldname: "in_standard_filter", label: t("In List Filter"), fieldtype: "Check" },
  { fieldname: "in_preview", label: t("In Preview"), fieldtype: "Check", depends_on: "eval:!in_list(['Table', 'Table MultiSelect'], pg.fieldtype)" },
  { fieldname: "column_break_35", fieldtype: "Column Break" },
  { fieldname: "in_filter", label: t("In Filter"), fieldtype: "Check" },
  { fieldname: "in_global_search", label: t("In Global Search"), fieldtype: "Check", depends_on: "eval:['Data', 'Select', 'Table', 'Text', 'Text Editor', 'Link', 'Small Text', 'Long Text', 'Read Only', 'Heading', 'Dynamic Link'].indexOf(pg.fieldtype)!==-1" },
  
  // Permissions Section
  { fieldname: "permissions", fieldtype: "Section Break", label: t("Permissions") },
  { fieldname: "read_only", label: t("Read Only"), fieldtype: "Check" },
  { fieldname: "allow_on_submit", label: t("Allow on Submit"), fieldtype: "Check", depends_on: "eval:parent.is_submittable" },
  { fieldname: "ignore_user_permissions", label: t("Ignore User Permissions"), fieldtype: "Check" },
  { fieldname: "allow_bulk_edit", label: t("Allow Bulk Edit"), fieldtype: "Check", depends_on: "eval:pg.fieldtype=='Table'" },
  { fieldname: "make_attachment_public", label: t("Make Attachment Public (by default)"), fieldtype: "Check", depends_on: "eval:['Attach', 'Attach Image'].includes(pg.fieldtype)" },
  { fieldname: "column_break_13", fieldtype: "Column Break" },
  { fieldname: "permlevel", label: t("Permission Level"), fieldtype: "Int" },
  { fieldname: "ignore_xss_filter", label: t("Ignore XSS Filter"), fieldtype: "Check" },
  
  // Constraints Section
  { fieldname: "constraints_section", fieldtype: "Section Break", label: t("Constraints") },
  { fieldname: "unique", label: t("Unique"), fieldtype: "Check" },
  { fieldname: "no_copy", label: t("No Copy"), fieldtype: "Check" },
  { fieldname: "set_only_once", label: t("Set Only Once"), fieldtype: "Check" },
  { fieldname: "remember_last_selected_value", label: t("Remember Last Selected Value"), fieldtype: "Check", depends_on: "eval:pg.fieldtype=='Link'" },
  { fieldname: "column_break_38", fieldtype: "Column Break" },
  { fieldname: "mandatory_depends_on", label: t("Mandatory Depends On (JS)"), fieldtype: "Code", options: "JS" },
  { fieldname: "read_only_depends_on", label: t("Read Only Depends On (JS)"), fieldtype: "Code", options: "JS" },
  
  // Display Section
  { fieldname: "display", fieldtype: "Section Break", label: t("Display") },
  { fieldname: "print_width", label: t("Print Width"), fieldtype: "Data" },
  { fieldname: "width", label: t("Width"), fieldtype: "Data" },
  { fieldname: "max_height", label: t("Max Height"), fieldtype: "Data" },
  { fieldname: "columns", label: t("Columns"), fieldtype: "Int" },
  { fieldname: "column_break_22", fieldtype: "Column Break" },
  { fieldname: "description", label: t("Description"), fieldtype: "Small Text" },
  { fieldname: "documentation_url", label: t("Documentation URL"), fieldtype: "Data", options: "URL", depends_on: "eval:!in_list(['Tab Break', 'Section Break', 'Column Break', 'Button', 'HTML'], pg.fieldtype)" },
  { fieldname: "placeholder", label: t("Placeholder"), fieldtype: "Data" },
  { fieldname: "oldfieldname", label: t("Old Fieldname"), fieldtype: "Data", hidden: 1 },
  { fieldname: "oldfieldtype", label: t("Old Fieldtype"), fieldtype: "Data", hidden: 1 },
];

export const useFormBuilderStore = defineStore("form-builder-store", () => {
  let pagetype = ref("");
  let pg = ref(null);
  let pagefields = ref([...DEFAULT_PAGEFIELD_PROPERTIES]);
  let form = ref({
    layout: { tabs: [] },
    active_tab: null,
    selected_field: null,
  });
  let dirty = ref(false);
  let readOnly = ref(false);
  let isCustomizeForm = ref(false);
  let preview = ref(false);
  let drag = ref(false);
  let getAnimation = "cubic-bezier(0.34, 1.56, 0.64, 1)";
  let refHistory = ref(null);

  // Getters
  let getPagefields = computed(() => {
    return pagefields.value;
  });

  let currentTab = computed(() => {
    if (!form.value.layout.tabs || form.value.layout.tabs.length === 0) {
      return null;
    }
    return form.value.layout.tabs.find((tab) => tab.df.name === form.value.active_tab);
  });

  const activeElement = useActiveElement();
  const notUsingInput = computed(
    () =>
      activeElement.value?.readOnly ||
      activeElement.value?.disabled ||
      (activeElement.value?.tagName !== "INPUT" &&
        activeElement.value?.tagName !== "TEXTAREA")
  );

  // Actions
  function selected(name) {
    return form.value.selected_field?.name === name;
  }

  function getDf(fieldtype, fieldname = "", label = "") {
    let df = {
      name: getRandomId(8),
      fieldtype: fieldtype,
      fieldname: fieldname,
      label: label,
      __islocal: 1,
      __unsaved: 1,
    };
    return df;
  }

  function getRandomId(length = 8) {
    const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
    let result = '';
    for (let i = 0; i < length; i++) {
      result += chars.charAt(Math.floor(Math.random() * chars.length));
    }
    return result;
  }

  function hasStandardField(field) {
    if (!isCustomizeForm.value) return false;
    if (!field.df.is_custom_field) return true;

    let children = {
      "Tab Break": "sections",
      "Section Break": "columns",
      "Column Break": "fields",
    }[field.df.fieldtype];

    if (!children) return false;

    return field[children]?.some((child) => {
      if (!child.df.is_custom_field) return true;
      return hasStandardField(child);
    });
  }

  function isUserGeneratedField(field) {
    return field.df.is_custom_field && !field.df.is_system_generated ? 1 : 0;
  }

  function initialize(fields = [], options = {}) {
    pg.value = options.pg || null;
    pagetype.value = options.pagetype || "";
    readOnly.value = options.readOnly || false;
    isCustomizeForm.value = options.isCustomizeForm || false;

    form.value.layout = createLayout(fields);
    
    if (form.value.layout.tabs.length > 0) {
      form.value.active_tab = form.value.layout.tabs[0].df.name;
    }
    form.value.selected_field = null;

    nextTick(() => {
      dirty.value = false;
      preview.value = false;
    });

    setupUndoRedo();
  }

  let undoRedoKeyboardEvent = onKeyDown(true, (e) => {
    if (!refHistory.value) return;
    if ((e.ctrlKey || e.metaKey)) {
      if (e.key === "z" && !e.shiftKey && refHistory.value.canUndo) {
        refHistory.value.undo();
      } else if (e.key === "z" && e.shiftKey && refHistory.value.canRedo) {
        refHistory.value.redo();
      }
    }
  });

  function setupUndoRedo() {
    refHistory.value = useDebouncedRefHistory(form, { deep: true, debounce: 100 });
    undoRedoKeyboardEvent;
  }

  function validateFields(fields, isTable) {
    fields = scrubFieldNames(fields);
    let errorMessage = "";

    let hasFields = fields.some((df) => {
      return !LAYOUT_FIELDS.includes(df.fieldtype);
    });

    if (!hasFields) {
      errorMessage = t("PageType must have at least one field");
    }

    let notAllowedInListView = ["Attach Image", ...NO_VALUE_TYPES];
    if (isTable) {
      notAllowedInListView = notAllowedInListView.filter((f) => f !== "Button");
    }

    function getFieldData(df) {
      let fieldname = `<b>${df.label} (${df.fieldname})</b>`;
      if (!df.label) {
        fieldname = `<b>${df.fieldname}</b>`;
      }
      let fieldtype = `<b>${df.fieldtype}</b>`;
      return [fieldname, fieldtype];
    }

    fields.forEach((df) => {
      // check if fieldname already exists
      let duplicate = fields.filter((f) => f.fieldname === df.fieldname);
      if (duplicate.length > 1) {
        errorMessage = t("Fieldname {0} appears multiple times").replace("{0}", getFieldData(df)[0]);
      }

      // Link & Table fields should always have options set
      if (["Link", ...TABLE_FIELDS].includes(df.fieldtype) && !df.options) {
        errorMessage = t("Options is required for field {0} of type {1}")
          .replace("{0}", getFieldData(df)[0])
          .replace("{1}", getFieldData(df)[1]);
      }

      // Do not allow if field is hidden & required but doesn't have default value
      if (df.hidden && df.reqd && !df.default) {
        errorMessage = t("{0} cannot be hidden and mandatory without any default value")
          .replace("{0}", getFieldData(df)[0]);
      }

      // In List View is not allowed for some fieldtypes
      if (df.in_list_view && notAllowedInListView.includes(df.fieldtype)) {
        errorMessage = t("'In List View' is not allowed for field {0} of type {1}")
          .replace("{0}", getFieldData(df)[0])
          .replace("{1}", getFieldData(df)[1]);
      }

      // In Global Search is not allowed for no_value_type fields
      if (df.in_global_search && NO_VALUE_TYPES.includes(df.fieldtype)) {
        errorMessage = t("'In Global Search' is not allowed for field {0} of type {1}")
          .replace("{0}", getFieldData(df)[0])
          .replace("{1}", getFieldData(df)[1]);
      }

      if (df.link_filters === "") {
        delete df.link_filters;
      }

      // check if link_filters format is correct
      if (df.link_filters) {
        try {
          JSON.parse(df.link_filters);
        } catch (e) {
          errorMessage = t("Invalid Filter Format for field {0} of type {1}")
            .replace("{0}", getFieldData(df)[0])
            .replace("{1}", getFieldData(df)[1]);
        }
      }
    });

    return errorMessage;
  }

  function getUpdatedFields() {
    let fields = [];
    let idx = 0;
    let newFieldName = "new-pagefield-";

    let layoutFields = JSON.parse(JSON.stringify(form.value.layout.tabs));

    layoutFields.forEach((tab, i) => {
      if (
        (i === 0 && isDfUpdated(tab.df, getDf("Tab Break", "", t("Details")))) ||
        i > 0
      ) {
        idx++;
        tab.df.idx = idx;
        if (tab.df.__unsaved && tab.df.__islocal) {
          tab.df.name = newFieldName + idx;
        }
        fields.push(tab.df);
      }

      tab.sections.forEach((section, j) => {
        let fieldsCopy = JSON.parse(JSON.stringify(fields));
        let oldIdx = idx;
        section.has_fields = false;

        if ((j === 0 && isDfUpdated(section.df, getDf("Section Break"))) || j > 0) {
          idx++;
          section.df.idx = idx;
          if (section.df.__unsaved && section.df.__islocal) {
            section.df.name = newFieldName + idx;
          }
          fields.push(section.df);
        }

        section.columns.forEach((column, k) => {
          if (
            (k === 0 && isDfUpdated(column.df, getDf("Column Break"))) ||
            k > 0 ||
            column.fields.length === 0
          ) {
            idx++;
            column.df.idx = idx;
            if (column.df.__unsaved && column.df.__islocal) {
              column.df.name = newFieldName + idx;
            }
            fields.push(column.df);
          }

          column.fields.forEach((field) => {
            idx++;
            field.df.idx = idx;
            if (field.df.__unsaved && field.df.__islocal) {
              field.df.name = newFieldName + idx;
            }
            fields.push(field.df);
            section.has_fields = true;
          });
        });

        if (!section.has_fields) {
          fields = fieldsCopy || [];
          idx = oldIdx;
        }
      });
    });

    return fields;
  }

  function isDfUpdated(df, newDf) {
    let dfCopy = JSON.parse(JSON.stringify(df));
    let newDfCopy = JSON.parse(JSON.stringify(newDf));
    delete dfCopy.name;
    delete newDfCopy.name;
    return JSON.stringify(dfCopy) !== JSON.stringify(newDfCopy);
  }

  function getLayout() {
    return createLayout(pg.value?.fields || []);
  }

  // Tab actions
  function addNewTab() {
    let tab = {
      df: getDf("Tab Break", "", t("Tab") + " " + (form.value.layout.tabs.length + 1)),
      sections: [sectionBoilerplate(getDf)],
    };

    form.value.layout.tabs.push(tab);
    activateTab(tab);
  }

  function activateTab(tab) {
    form.value.active_tab = tab.df.name;
    form.value.selected_field = tab.df;

    nextTick(() => {
      const activeTabElement = document.querySelector(".tabs .tab.active");
      if (activeTabElement) {
        activeTabElement.scrollIntoView({
          behavior: "smooth",
          inline: "center",
          block: "nearest",
        });
      }
    });
  }

  return {
    pagetype,
    pg,
    form,
    dirty,
    readOnly,
    isCustomizeForm,
    preview,
    drag,
    getAnimation,
    getPagefields,
    currentTab,
    notUsingInput,
    selected,
    getDf,
    getRandomId,
    hasStandardField,
    isUserGeneratedField,
    initialize,
    validateFields,
    getUpdatedFields,
    isDfUpdated,
    getLayout,
    addNewTab,
    activateTab,
  };
});
