import { setupHorizontalScrollViaWheel } from "../../widget_utils";
import ViewMode, { ViewModeArgs } from "../view_mode";
import attributeService from "../../../services/attributes";
import noteCreateService from "../../../services/note_create";
import appContext, { EventData } from "../../../components/app_context";
import { BoardData } from "./config";
import SpacedUpdate from "../../../services/spaced_update";
import { setupContextMenu } from "./context_menu";
import BoardApi from "./api";
import { BoardDragHandler, DragContext } from "./drag_handler";
import { DifferentialBoardRenderer } from "./differential_renderer";
const TPL = /*html*/`
`;
export default class BoardView extends ViewMode {
    private $root: JQuery;
    private $container: JQuery;
    private spacedUpdate: SpacedUpdate;
    private dragContext: DragContext;
    private persistentData: BoardData;
    private api?: BoardApi;
    private dragHandler?: BoardDragHandler;
    private renderer?: DifferentialBoardRenderer;
    constructor(args: ViewModeArgs) {
        super(args, "board");
        this.$root = $(TPL);
        setupHorizontalScrollViaWheel(this.$root);
        this.$container = this.$root.find(".board-view-container");
        this.spacedUpdate = new SpacedUpdate(() => this.onSave(), 5_000);
        this.persistentData = {
            columns: []
        };
        this.dragContext = {
            draggedNote: null,
            draggedBranch: null,
            draggedNoteElement: null
        };
        args.$parent.append(this.$root);
    }
    async renderList(): Promise | undefined> {
        if (!this.renderer) {
            // First time setup
            this.$container.empty();
            await this.initializeRenderer();
        }
        await this.renderer!.renderBoard();
        return this.$root;
    }
    private async initializeRenderer() {
        this.api = await BoardApi.build(this.parentNote, this.viewStorage);
        this.dragHandler = new BoardDragHandler(
            this.$container,
            this.api,
            this.dragContext,
            async () => { await this.renderList(); }
        );
        this.renderer = new DifferentialBoardRenderer(
            this.$container,
            this.api,
            this.dragHandler,
            (column: string) => this.createNewItem(column),
            this.parentNote,
            this.viewStorage
        );
        setupContextMenu({
            $container: this.$container,
            api: this.api,
            boardView: this
        });
        // Setup column title editing and add column functionality
        this.setupBoardInteractions();
    }
    private setupBoardInteractions() {
        // Handle column title editing
        this.$container.on('click', 'h3[data-column-value]', (e) => {
            e.stopPropagation();
            const $titleEl = $(e.currentTarget);
            const columnValue = $titleEl.attr('data-column-value');
            if (columnValue) {
                const columnItems = this.api?.getColumn(columnValue) || [];
                this.startEditingColumnTitle($titleEl, columnValue, columnItems);
            }
        });
        // Handle add column button
        this.$container.on('click', '.board-add-column', (e) => {
            e.stopPropagation();
            this.startCreatingNewColumn($(e.currentTarget));
        });
    }
    private createTitleStructure(title: string): { $titleText: JQuery; $editIcon: JQuery } {
        const $titleText = $("").text(title);
        const $editIcon = $("")
            .addClass("edit-icon icon bx bx-edit-alt")
            .attr("title", "Click to edit column title");
        return { $titleText, $editIcon };
    }
    private startEditingColumnTitle($titleEl: JQuery, columnValue: string, columnItems: { branch: any; note: any; }[]) {
        if ($titleEl.hasClass("editing")) {
            return; // Already editing
        }
        const $titleText = $titleEl.find("span").first();
        const currentTitle = $titleText.text();
        $titleEl.addClass("editing");
        const $input = $("")
            .attr("type", "text")
            .val(currentTitle)
            .attr("placeholder", "Column title");
        $titleEl.empty().append($input);
        $input.focus().select();
        const finishEdit = async (save: boolean = true) => {
            if (!$titleEl.hasClass("editing")) {
                return; // Already finished
            }
            $titleEl.removeClass("editing");
            let finalTitle = currentTitle;
            if (save) {
                const newTitle = $input.val() as string;
                if (newTitle.trim() && newTitle !== currentTitle) {
                    await this.renameColumn(columnValue, newTitle.trim(), columnItems);
                    finalTitle = newTitle.trim();
                }
            }
            // Recreate the title structure
            const { $titleText, $editIcon } = this.createTitleStructure(finalTitle);
            $titleEl.empty().append($titleText, $editIcon);
        };
        $input.on("blur", () => finishEdit(true));
        $input.on("keydown", (e) => {
            if (e.key === "Enter") {
                e.preventDefault();
                finishEdit(true);
            } else if (e.key === "Escape") {
                e.preventDefault();
                finishEdit(false);
            }
        });
    }
    private async renameColumn(oldValue: string, newValue: string, columnItems: { branch: any; note: any; }[]) {
        try {
            // Get all note IDs in this column
            const noteIds = columnItems.map(item => item.note.noteId);
            // Use the API to rename the column (update all notes)
            await this.api?.renameColumn(oldValue, newValue, noteIds);
            // Refresh the board to reflect the changes
            await this.renderList();
        } catch (error) {
            console.error("Failed to rename column:", error);
        }
    }
    private async createNewItem(column: string) {
        try {
            // Get the parent note path
            const parentNotePath = this.parentNote.noteId;
            // Create a new note as a child of the parent note
            const { note: newNote } = await noteCreateService.createNote(parentNotePath, {
                activate: false,
                title: "New item"
            });
            if (newNote) {
                // Set the status label to place it in the correct column
                await attributeService.setLabel(newNote.noteId, "status", column);
                // Refresh the board to show the new item
                await this.renderList();
                // Start inline editing of the newly created card
                this.startInlineEditingCard(newNote.noteId);
            }
        } catch (error) {
            console.error("Failed to create new item:", error);
        }
    }
    async insertItemAtPosition(column: string, relativeToBranchId: string, direction: "before" | "after"): Promise {
        try {
            // Create the note without opening it
            const newNote = await this.api?.insertRowAtPosition(column, relativeToBranchId, direction, false);
            
            if (newNote) {
                // Refresh the board to show the new item
                await this.renderList();
                // Start inline editing of the newly created card
                this.startInlineEditingCard(newNote.noteId);
            }
        } catch (error) {
            console.error("Failed to insert new item:", error);
        }
    }
    private startInlineEditingCard(noteId: string) {
        this.renderer?.startInlineEditing(noteId);
    }
    forceFullRefresh() {
        this.renderer?.forceFullRender();
        return this.renderList();
    }
    private startCreatingNewColumn($addColumnEl: JQuery) {
        if ($addColumnEl.hasClass("editing")) {
            return; // Already editing
        }
        $addColumnEl.addClass("editing");
        const $input = $("")
            .attr("type", "text")
            .attr("placeholder", "Enter column name...")
            .css({
                background: "var(--main-background-color)",
                border: "1px solid var(--main-text-color)",
                borderRadius: "4px",
                padding: "0.5em",
                color: "var(--main-text-color)",
                fontFamily: "inherit",
                fontSize: "inherit",
                width: "100%",
                textAlign: "center"
            });
        $addColumnEl.empty().append($input);
        $input.focus();
        const finishEdit = async (save: boolean = true) => {
            if (!$addColumnEl.hasClass("editing")) {
                return; // Already finished
            }
            $addColumnEl.removeClass("editing");
            if (save) {
                const columnName = $input.val() as string;
                if (columnName.trim()) {
                    await this.createNewColumn(columnName.trim());
                }
            }
            // Restore the add button
            $addColumnEl.html('Add Column');
        };
        $input.on("blur", () => finishEdit(true));
        $input.on("keydown", (e) => {
            if (e.key === "Enter") {
                e.preventDefault();
                finishEdit(true);
            } else if (e.key === "Escape") {
                e.preventDefault();
                finishEdit(false);
            }
        });
    }
    private async createNewColumn(columnName: string) {
        try {
            // Check if column already exists
            if (this.api?.columns.includes(columnName)) {
                console.warn("A column with this name already exists.");
                return;
            }
            // Create the new column
            await this.api?.createColumn(columnName);
            // Refresh the board to show the new column
            await this.renderList();
        } catch (error) {
            console.error("Failed to create new column:", error);
        }
    }
    async onEntitiesReloaded({ loadResults }: EventData<"entitiesReloaded">) {
        // Check if any changes affect our board
        const hasRelevantChanges =
            // React to changes in "status" attribute for notes in this board
            loadResults.getAttributeRows().some(attr => attr.name === "status" && this.noteIds.includes(attr.noteId!)) ||
            // React to changes in note title
            loadResults.getNoteIds().some(noteId => this.noteIds.includes(noteId)) ||
            // React to changes in branches for subchildren (e.g., moved, added, or removed notes)
            loadResults.getBranchRows().some(branch => this.noteIds.includes(branch.noteId!)) ||
            // React to attachment change
            loadResults.getAttachmentRows().some(att => att.ownerId === this.parentNote.noteId && att.title === "board.json");
        if (hasRelevantChanges && this.renderer) {
            // Use differential rendering with API refresh
            await this.renderer.renderBoard(true);
        }
        // Don't trigger full view refresh - let differential renderer handle it
        return false;
    }
    private onSave() {
        this.viewStorage.store(this.persistentData);
    }
}