import ViewMode, { type ViewModeArgs } from "../view_mode.js";
import attributes from "../../../services/attributes.js";
import SpacedUpdate from "../../../services/spaced_update.js";
import type { EventData } from "../../../components/app_context.js";
import {Tabulator, SortModule, FormatModule, InteractionModule, EditModule, ResizeColumnsModule, FrozenColumnsModule, PersistenceModule, MoveColumnsModule, MoveRowsModule, ColumnDefinition, DataTreeModule, Options, RowComponent, ColumnComponent} from 'tabulator-tables';
import "tabulator-tables/dist/css/tabulator.css";
import "../../../../src/stylesheets/table.css";
import { canReorderRows, configureReorderingRows } from "./dragging.js";
import buildFooter from "./footer.js";
import getAttributeDefinitionInformation, { buildRowDefinitions } from "./rows.js";
import { AttributeDefinitionInformation, buildColumnDefinitions } from "./columns.js";
import { setupContextMenu } from "./context_menu.js";
import TableColumnEditing from "./col_editing.js";
import TableRowEditing from "./row_editing.js";
const TPL = /*html*/`
`;
export interface StateInfo {
    tableData?: {
        columns?: ColumnDefinition[];
    };
}
export default class TableView extends ViewMode {
    private $root: JQuery;
    private $container: JQuery;
    private spacedUpdate: SpacedUpdate;
    private api?: Tabulator;
    private persistentData: StateInfo["tableData"];
    private colEditing?: TableColumnEditing;
    private rowEditing?: TableRowEditing;
    constructor(args: ViewModeArgs) {
        super(args, "table");
        this.$root = $(TPL);
        this.$container = this.$root.find(".table-view-container");
        this.spacedUpdate = new SpacedUpdate(() => this.onSave(), 5_000);
        this.persistentData = {};
        args.$parent.append(this.$root);
    }
    async renderList() {
        this.$container.empty();
        this.renderTable(this.$container[0]);
        return this.$root;
    }
    private async renderTable(el: HTMLElement) {
        const info = getAttributeDefinitionInformation(this.parentNote);
        const modules = [ SortModule, FormatModule, InteractionModule, EditModule, ResizeColumnsModule, FrozenColumnsModule, PersistenceModule, MoveColumnsModule, MoveRowsModule, DataTreeModule ];
        for (const module of modules) {
            Tabulator.registerModule(module);
        }
        this.initialize(el, info);
    }
    private async initialize(el: HTMLElement, info: AttributeDefinitionInformation[]) {
        const viewStorage = await this.viewStorage.restore();
        this.persistentData = viewStorage?.tableData || {};
        const { definitions: rowData, hasSubtree: hasChildren } = await buildRowDefinitions(this.parentNote, info);
        const movableRows = canReorderRows(this.parentNote) && !hasChildren;
        const columnDefs = buildColumnDefinitions(info, movableRows);
        let opts: Options = {
            layout: "fitDataFill",
            index: "branchId",
            columns: columnDefs,
            data: rowData,
            persistence: true,
            movableColumns: true,
            movableRows,
            footerElement: buildFooter(this.parentNote),
            persistenceWriterFunc: (_id, type: string, data: object) => {
                (this.persistentData as Record)[type] = data;
                this.spacedUpdate.scheduleUpdate();
            },
            persistenceReaderFunc: (_id, type: string) => this.persistentData?.[type],
        };
        if (hasChildren) {
            opts = {
                ...opts,
                dataTree: hasChildren,
                dataTreeStartExpanded: true,
                dataTreeElementColumn: "title",
                dataTreeExpandElement: ``,
                dataTreeCollapseElement: ``
            }
        }
        this.api = new Tabulator(el, opts);
        this.colEditing = new TableColumnEditing(this.args.$parent, this.args.parentNote, this.api);
        this.rowEditing = new TableRowEditing(this.api, this.args.parentNotePath!);
        if (movableRows) {
            configureReorderingRows(this.api);
        }
        setupContextMenu(this.api, this.parentNote);
    }
    private onSave() {
        this.viewStorage.store({
            tableData: this.persistentData,
        });
    }
    async onEntitiesReloaded({ loadResults }: EventData<"entitiesReloaded">) {
        if (!this.api) {
            return;
        }
        // Force a refresh if sorted is changed since we need to disable reordering.
        if (loadResults.getAttributeRows().find(a => a.name === "sorted" && attributes.isAffecting(a, this.parentNote))) {
            return true;
        }
        // Refresh if promoted attributes get changed.
        if (loadResults.getAttributeRows().find(attr =>
            attr.type === "label" &&
            (attr.name?.startsWith("label:") || attr.name?.startsWith("relation:")) &&
            attributes.isAffecting(attr, this.parentNote))) {
                console.log("Col update");
            this.#manageColumnUpdate();
        }
        if (loadResults.getBranchRows().some(branch => branch.parentNoteId === this.parentNote.noteId || this.noteIds.includes(branch.parentNoteId ?? ""))
            || loadResults.getNoteIds().some(noteId => this.noteIds.includes(noteId)
            || loadResults.getAttributeRows().some(attr => this.noteIds.includes(attr.noteId!)))) {
            return await this.#manageRowsUpdate();
        }
        return false;
    }
    #manageColumnUpdate() {
        if (!this.api) {
            return;
        }
        const info = getAttributeDefinitionInformation(this.parentNote);
        const columnDefs = buildColumnDefinitions(info, !!this.api.options.movableRows, this.persistentData?.columns, this.colEditing?.getNewAttributePosition());
        this.api.setColumns(columnDefs);
        this.colEditing?.resetNewAttributePosition();
    }
    addNewRowCommand(e) {
        this.rowEditing?.addNewRowCommand(e);
    }
    addNewTableColumnCommand(e) {
        this.colEditing?.addNewTableColumnCommand(e);
    }
    updateAttributeListCommand(e) {
        this.colEditing?.updateAttributeListCommand(e);
    }
    saveAttributesCommand() {
        this.colEditing?.saveAttributesCommand();
    }
    async #manageRowsUpdate() {
        if (!this.api) {
            return;
        }
        const info = getAttributeDefinitionInformation(this.parentNote);
        const { definitions, hasSubtree } = await buildRowDefinitions(this.parentNote, info);
        // Force a refresh if the data tree needs enabling/disabling.
        if (this.api.options.dataTree !== hasSubtree) {
            return true;
        }
        await this.api.replaceData(definitions);
        return false;
    }
}