import { defineBackendPlugin, createEndpoint } from '@btst/stack/plugins/api';
import { kanbanSchema } from '../db.mjs';
import { slugify } from '../utils.mjs';
import { BoardListQuerySchema, createBoardSchema, updateBoardSchema, createColumnSchema, updateColumnSchema, reorderColumnsSchema, createTaskSchema, updateTaskSchema, moveTaskSchema, reorderTasksSchema } from '../schemas.mjs';

const kanbanBackendPlugin = (hooks) => defineBackendPlugin({
  name: "kanban",
  dbPlugin: kanbanSchema,
  routes: (adapter) => {
    const listBoards = createEndpoint(
      "/boards",
      {
        method: "GET",
        query: BoardListQuerySchema
      },
      async (ctx) => {
        const { query, headers } = ctx;
        const context = { query, headers };
        try {
          if (hooks?.onBeforeListBoards) {
            const canList = await hooks.onBeforeListBoards(query, context);
            if (!canList) {
              throw ctx.error(403, {
                message: "Unauthorized: Cannot list boards"
              });
            }
          }
          const whereConditions = [];
          if (query.slug) {
            whereConditions.push({
              field: "slug",
              value: query.slug,
              operator: "eq"
            });
          }
          if (query.ownerId) {
            whereConditions.push({
              field: "ownerId",
              value: query.ownerId,
              operator: "eq"
            });
          }
          if (query.organizationId) {
            whereConditions.push({
              field: "organizationId",
              value: query.organizationId,
              operator: "eq"
            });
          }
          const boards = await adapter.findMany({
            model: "kanbanBoard",
            limit: query.limit ?? 50,
            offset: query.offset ?? 0,
            where: whereConditions,
            sortBy: {
              field: "createdAt",
              direction: "desc"
            },
            join: {
              kanbanColumn: true
            }
          });
          const columnIds = [];
          for (const board of boards) {
            if (board.column) {
              for (const col of board.column) {
                columnIds.push(col.id);
              }
            }
          }
          const tasksByColumn = /* @__PURE__ */ new Map();
          if (columnIds.length > 0) {
            const taskQueries = columnIds.map(
              (columnId) => adapter.findMany({
                model: "kanbanTask",
                where: [
                  {
                    field: "columnId",
                    value: columnId,
                    operator: "eq"
                  }
                ],
                sortBy: { field: "order", direction: "asc" }
              })
            );
            const taskResults = await Promise.all(taskQueries);
            for (let i = 0; i < columnIds.length; i++) {
              const columnId = columnIds[i];
              const tasks = taskResults[i];
              if (columnId && tasks) {
                tasksByColumn.set(columnId, tasks);
              }
            }
          }
          const result = boards.map((board) => {
            const columns = (board.column || []).sort((a, b) => a.order - b.order).map((col) => ({
              ...col,
              tasks: tasksByColumn.get(col.id) || []
            }));
            const { column: _, ...boardWithoutJoin } = board;
            return {
              ...boardWithoutJoin,
              columns
            };
          });
          if (hooks?.onBoardsRead) {
            await hooks.onBoardsRead(result, query, context);
          }
          return result;
        } catch (error) {
          if (hooks?.onListBoardsError) {
            await hooks.onListBoardsError(error, context);
          }
          throw error;
        }
      }
    );
    const getBoard = createEndpoint(
      "/boards/:id",
      {
        method: "GET"
      },
      async (ctx) => {
        const { params, headers } = ctx;
        const context = { params, headers };
        try {
          if (hooks?.onBeforeReadBoard) {
            const canRead = await hooks.onBeforeReadBoard(params.id, context);
            if (!canRead) {
              throw ctx.error(403, {
                message: "Unauthorized: Cannot read board"
              });
            }
          }
          const board = await adapter.findOne({
            model: "kanbanBoard",
            where: [
              { field: "id", value: params.id, operator: "eq" }
            ],
            join: {
              kanbanColumn: true
            }
          });
          if (!board) {
            throw ctx.error(404, { message: "Board not found" });
          }
          const columnIds = (board.column || []).map((c) => c.id);
          const tasksByColumn = /* @__PURE__ */ new Map();
          if (columnIds.length > 0) {
            const taskQueries = columnIds.map(
              (columnId) => adapter.findMany({
                model: "kanbanTask",
                where: [
                  {
                    field: "columnId",
                    value: columnId,
                    operator: "eq"
                  }
                ],
                sortBy: { field: "order", direction: "asc" }
              })
            );
            const taskResults = await Promise.all(taskQueries);
            for (let i = 0; i < columnIds.length; i++) {
              const columnId = columnIds[i];
              const tasks = taskResults[i];
              if (columnId && tasks) {
                tasksByColumn.set(columnId, tasks);
              }
            }
          }
          const columns = (board.column || []).sort((a, b) => a.order - b.order).map((col) => ({
            ...col,
            tasks: tasksByColumn.get(col.id) || []
          }));
          const { column: _, ...boardWithoutJoin } = board;
          const result = {
            ...boardWithoutJoin,
            columns
          };
          if (hooks?.onBoardRead) {
            await hooks.onBoardRead(result, context);
          }
          return result;
        } catch (error) {
          if (hooks?.onReadBoardError) {
            await hooks.onReadBoardError(error, context);
          }
          throw error;
        }
      }
    );
    const createBoard = createEndpoint(
      "/boards",
      {
        method: "POST",
        body: createBoardSchema
      },
      async (ctx) => {
        const context = {
          body: ctx.body,
          headers: ctx.headers
        };
        try {
          if (hooks?.onBeforeCreateBoard) {
            const canCreate = await hooks.onBeforeCreateBoard(
              ctx.body,
              context
            );
            if (!canCreate) {
              throw ctx.error(403, {
                message: "Unauthorized: Cannot create board"
              });
            }
          }
          const { ...boardData } = ctx.body;
          const slug = slugify(boardData.slug || boardData.name);
          if (!slug) {
            throw ctx.error(400, {
              message: "Invalid slug: must contain at least one alphanumeric character"
            });
          }
          let newBoard;
          const createdColumns = [];
          await adapter.transaction(async (tx) => {
            newBoard = await tx.create({
              model: "kanbanBoard",
              data: {
                ...boardData,
                slug,
                createdAt: /* @__PURE__ */ new Date(),
                updatedAt: /* @__PURE__ */ new Date()
              }
            });
            const defaultColumns = [
              { title: "To Do", order: 0, boardId: newBoard.id },
              { title: "In Progress", order: 1, boardId: newBoard.id },
              { title: "Done", order: 2, boardId: newBoard.id }
            ];
            for (const colData of defaultColumns) {
              const col = await tx.create({
                model: "kanbanColumn",
                data: {
                  ...colData,
                  createdAt: /* @__PURE__ */ new Date(),
                  updatedAt: /* @__PURE__ */ new Date()
                }
              });
              createdColumns.push({ ...col, tasks: [] });
            }
          });
          if (!newBoard) {
            throw ctx.error(500, {
              message: "Failed to create board"
            });
          }
          const result = { ...newBoard, columns: createdColumns };
          if (hooks?.onBoardCreated) {
            await hooks.onBoardCreated(result, context);
          }
          return result;
        } catch (error) {
          if (hooks?.onCreateBoardError) {
            await hooks.onCreateBoardError(error, context);
          }
          throw error;
        }
      }
    );
    const updateBoard = createEndpoint(
      "/boards/:id",
      {
        method: "PUT",
        body: updateBoardSchema.omit({ id: true })
      },
      async (ctx) => {
        const context = {
          body: ctx.body,
          params: ctx.params,
          headers: ctx.headers
        };
        try {
          if (hooks?.onBeforeUpdateBoard) {
            const canUpdate = await hooks.onBeforeUpdateBoard(
              ctx.params.id,
              { ...ctx.body, id: ctx.params.id },
              context
            );
            if (!canUpdate) {
              throw ctx.error(403, {
                message: "Unauthorized: Cannot update board"
              });
            }
          }
          const { slug: rawSlug, ...restBoardData } = ctx.body;
          const slugified = rawSlug ? slugify(rawSlug) : void 0;
          if (rawSlug && !slugified) {
            throw ctx.error(400, {
              message: "Invalid slug: must contain at least one alphanumeric character"
            });
          }
          const boardData = {
            ...restBoardData,
            ...slugified ? { slug: slugified } : {}
          };
          const updated = await adapter.update({
            model: "kanbanBoard",
            where: [{ field: "id", value: ctx.params.id }],
            update: {
              ...boardData,
              updatedAt: /* @__PURE__ */ new Date()
            }
          });
          if (!updated) {
            throw ctx.error(404, { message: "Board not found" });
          }
          if (hooks?.onBoardUpdated) {
            await hooks.onBoardUpdated(updated, context);
          }
          return updated;
        } catch (error) {
          if (hooks?.onUpdateBoardError) {
            await hooks.onUpdateBoardError(error, context);
          }
          throw error;
        }
      }
    );
    const deleteBoard = createEndpoint(
      "/boards/:id",
      {
        method: "DELETE"
      },
      async (ctx) => {
        const context = {
          params: ctx.params,
          headers: ctx.headers
        };
        try {
          const existingBoard = await adapter.findOne({
            model: "kanbanBoard",
            where: [
              { field: "id", value: ctx.params.id, operator: "eq" }
            ]
          });
          if (!existingBoard) {
            throw ctx.error(404, { message: "Board not found" });
          }
          if (hooks?.onBeforeDeleteBoard) {
            const canDelete = await hooks.onBeforeDeleteBoard(
              ctx.params.id,
              context
            );
            if (!canDelete) {
              throw ctx.error(403, {
                message: "Unauthorized: Cannot delete board"
              });
            }
          }
          await adapter.delete({
            model: "kanbanBoard",
            where: [{ field: "id", value: ctx.params.id }]
          });
          if (hooks?.onBoardDeleted) {
            await hooks.onBoardDeleted(ctx.params.id, context);
          }
          return { success: true };
        } catch (error) {
          if (hooks?.onDeleteBoardError) {
            await hooks.onDeleteBoardError(error, context);
          }
          throw error;
        }
      }
    );
    const createColumn = createEndpoint(
      "/columns",
      {
        method: "POST",
        body: createColumnSchema
      },
      async (ctx) => {
        const context = {
          body: ctx.body,
          headers: ctx.headers
        };
        try {
          if (hooks?.onBeforeCreateColumn) {
            const canCreate = await hooks.onBeforeCreateColumn(
              ctx.body,
              context
            );
            if (!canCreate) {
              throw ctx.error(403, {
                message: "Unauthorized: Cannot create column"
              });
            }
          }
          const existingColumns = await adapter.findMany({
            model: "kanbanColumn",
            where: [
              {
                field: "boardId",
                value: ctx.body.boardId,
                operator: "eq"
              }
            ]
          });
          const nextOrder = existingColumns.length > 0 ? Math.max(...existingColumns.map((c) => c.order)) + 1 : 0;
          const newColumn = await adapter.create({
            model: "kanbanColumn",
            data: {
              ...ctx.body,
              order: ctx.body.order ?? nextOrder,
              createdAt: /* @__PURE__ */ new Date(),
              updatedAt: /* @__PURE__ */ new Date()
            }
          });
          if (hooks?.onColumnCreated) {
            await hooks.onColumnCreated(newColumn, context);
          }
          return newColumn;
        } catch (error) {
          throw error;
        }
      }
    );
    const updateColumn = createEndpoint(
      "/columns/:id",
      {
        method: "PUT",
        body: updateColumnSchema.omit({ id: true })
      },
      async (ctx) => {
        const context = {
          body: ctx.body,
          params: ctx.params,
          headers: ctx.headers
        };
        try {
          if (hooks?.onBeforeUpdateColumn) {
            const canUpdate = await hooks.onBeforeUpdateColumn(
              ctx.params.id,
              { ...ctx.body, id: ctx.params.id },
              context
            );
            if (!canUpdate) {
              throw ctx.error(403, {
                message: "Unauthorized: Cannot update column"
              });
            }
          }
          const updated = await adapter.update({
            model: "kanbanColumn",
            where: [{ field: "id", value: ctx.params.id }],
            update: {
              ...ctx.body,
              updatedAt: /* @__PURE__ */ new Date()
            }
          });
          if (!updated) {
            throw ctx.error(404, { message: "Column not found" });
          }
          if (hooks?.onColumnUpdated) {
            await hooks.onColumnUpdated(updated, context);
          }
          return updated;
        } catch (error) {
          throw error;
        }
      }
    );
    const deleteColumn = createEndpoint(
      "/columns/:id",
      {
        method: "DELETE"
      },
      async (ctx) => {
        const context = {
          params: ctx.params,
          headers: ctx.headers
        };
        try {
          const existingColumn = await adapter.findOne({
            model: "kanbanColumn",
            where: [
              { field: "id", value: ctx.params.id, operator: "eq" }
            ]
          });
          if (!existingColumn) {
            throw ctx.error(404, { message: "Column not found" });
          }
          if (hooks?.onBeforeDeleteColumn) {
            const canDelete = await hooks.onBeforeDeleteColumn(
              ctx.params.id,
              context
            );
            if (!canDelete) {
              throw ctx.error(403, {
                message: "Unauthorized: Cannot delete column"
              });
            }
          }
          await adapter.delete({
            model: "kanbanColumn",
            where: [{ field: "id", value: ctx.params.id }]
          });
          if (hooks?.onColumnDeleted) {
            await hooks.onColumnDeleted(ctx.params.id, context);
          }
          return { success: true };
        } catch (error) {
          throw error;
        }
      }
    );
    const reorderColumns = createEndpoint(
      "/columns/reorder",
      {
        method: "POST",
        body: reorderColumnsSchema
      },
      async (ctx) => {
        const { boardId, columnIds } = ctx.body;
        const context = {
          body: ctx.body,
          headers: ctx.headers
        };
        if (hooks?.onBeforeUpdateColumn) {
          for (let i = 0; i < columnIds.length; i++) {
            const columnId = columnIds[i];
            if (!columnId) continue;
            const canUpdate = await hooks.onBeforeUpdateColumn(
              columnId,
              { id: columnId, order: i },
              context
            );
            if (!canUpdate) {
              throw ctx.error(403, {
                message: "Unauthorized: Cannot reorder columns"
              });
            }
          }
        }
        const updatedColumns = [];
        await adapter.transaction(async (tx) => {
          for (let i = 0; i < columnIds.length; i++) {
            const columnId = columnIds[i];
            if (!columnId) continue;
            const updated = await tx.update({
              model: "kanbanColumn",
              where: [
                { field: "id", value: columnId },
                { field: "boardId", value: boardId, operator: "eq" }
              ],
              update: { order: i, updatedAt: /* @__PURE__ */ new Date() }
            });
            if (updated) {
              updatedColumns.push(updated);
            }
          }
        });
        if (hooks?.onColumnUpdated) {
          for (const column of updatedColumns) {
            await hooks.onColumnUpdated(column, context);
          }
        }
        return { success: true };
      }
    );
    const createTask = createEndpoint(
      "/tasks",
      {
        method: "POST",
        body: createTaskSchema
      },
      async (ctx) => {
        const context = {
          body: ctx.body,
          headers: ctx.headers
        };
        try {
          if (hooks?.onBeforeCreateTask) {
            const canCreate = await hooks.onBeforeCreateTask(
              ctx.body,
              context
            );
            if (!canCreate) {
              throw ctx.error(403, {
                message: "Unauthorized: Cannot create task"
              });
            }
          }
          const existingTasks = await adapter.findMany({
            model: "kanbanTask",
            where: [
              {
                field: "columnId",
                value: ctx.body.columnId,
                operator: "eq"
              }
            ]
          });
          const nextOrder = existingTasks.length > 0 ? Math.max(...existingTasks.map((t) => t.order)) + 1 : 0;
          const taskData = {
            title: ctx.body.title,
            columnId: ctx.body.columnId,
            description: ctx.body.description,
            priority: ctx.body.priority || "MEDIUM",
            order: ctx.body.order ?? nextOrder,
            assigneeId: ctx.body.assigneeId ?? void 0,
            isArchived: ctx.body.isArchived ?? false,
            createdAt: /* @__PURE__ */ new Date(),
            updatedAt: /* @__PURE__ */ new Date()
          };
          const newTask = await adapter.create({
            model: "kanbanTask",
            data: taskData
          });
          if (hooks?.onTaskCreated) {
            await hooks.onTaskCreated(newTask, context);
          }
          return newTask;
        } catch (error) {
          throw error;
        }
      }
    );
    const updateTask = createEndpoint(
      "/tasks/:id",
      {
        method: "PUT",
        body: updateTaskSchema.omit({ id: true })
      },
      async (ctx) => {
        const context = {
          body: ctx.body,
          params: ctx.params,
          headers: ctx.headers
        };
        try {
          if (hooks?.onBeforeUpdateTask) {
            const canUpdate = await hooks.onBeforeUpdateTask(
              ctx.params.id,
              { ...ctx.body, id: ctx.params.id },
              context
            );
            if (!canUpdate) {
              throw ctx.error(403, {
                message: "Unauthorized: Cannot update task"
              });
            }
          }
          const updated = await adapter.update({
            model: "kanbanTask",
            where: [{ field: "id", value: ctx.params.id }],
            update: {
              ...ctx.body,
              updatedAt: /* @__PURE__ */ new Date()
            }
          });
          if (!updated) {
            throw ctx.error(404, { message: "Task not found" });
          }
          if (hooks?.onTaskUpdated) {
            await hooks.onTaskUpdated(updated, context);
          }
          return updated;
        } catch (error) {
          throw error;
        }
      }
    );
    const deleteTask = createEndpoint(
      "/tasks/:id",
      {
        method: "DELETE"
      },
      async (ctx) => {
        const context = {
          params: ctx.params,
          headers: ctx.headers
        };
        try {
          const existingTask = await adapter.findOne({
            model: "kanbanTask",
            where: [
              { field: "id", value: ctx.params.id, operator: "eq" }
            ]
          });
          if (!existingTask) {
            throw ctx.error(404, { message: "Task not found" });
          }
          if (hooks?.onBeforeDeleteTask) {
            const canDelete = await hooks.onBeforeDeleteTask(
              ctx.params.id,
              context
            );
            if (!canDelete) {
              throw ctx.error(403, {
                message: "Unauthorized: Cannot delete task"
              });
            }
          }
          await adapter.delete({
            model: "kanbanTask",
            where: [{ field: "id", value: ctx.params.id }]
          });
          if (hooks?.onTaskDeleted) {
            await hooks.onTaskDeleted(ctx.params.id, context);
          }
          return { success: true };
        } catch (error) {
          throw error;
        }
      }
    );
    const moveTask = createEndpoint(
      "/tasks/move",
      {
        method: "POST",
        body: moveTaskSchema
      },
      async (ctx) => {
        const { taskId, targetColumnId, targetOrder } = ctx.body;
        const context = {
          body: ctx.body,
          headers: ctx.headers
        };
        const task = await adapter.findOne({
          model: "kanbanTask",
          where: [{ field: "id", value: taskId, operator: "eq" }]
        });
        if (!task) {
          throw ctx.error(404, { message: "Task not found" });
        }
        if (hooks?.onBeforeUpdateTask) {
          const canUpdate = await hooks.onBeforeUpdateTask(
            taskId,
            { id: taskId, columnId: targetColumnId, order: targetOrder },
            context
          );
          if (!canUpdate) {
            throw ctx.error(403, {
              message: "Unauthorized: Cannot move task"
            });
          }
        }
        const updated = await adapter.update({
          model: "kanbanTask",
          where: [{ field: "id", value: taskId }],
          update: {
            columnId: targetColumnId,
            order: targetOrder,
            updatedAt: /* @__PURE__ */ new Date()
          }
        });
        if (!updated) {
          throw ctx.error(404, { message: "Task not found" });
        }
        if (hooks?.onTaskUpdated) {
          await hooks.onTaskUpdated(updated, context);
        }
        return updated;
      }
    );
    const reorderTasks = createEndpoint(
      "/tasks/reorder",
      {
        method: "POST",
        body: reorderTasksSchema
      },
      async (ctx) => {
        const { columnId, taskIds } = ctx.body;
        const context = {
          body: ctx.body,
          headers: ctx.headers
        };
        if (hooks?.onBeforeUpdateTask) {
          for (let i = 0; i < taskIds.length; i++) {
            const taskId = taskIds[i];
            if (!taskId) continue;
            const canUpdate = await hooks.onBeforeUpdateTask(
              taskId,
              { id: taskId, order: i },
              context
            );
            if (!canUpdate) {
              throw ctx.error(403, {
                message: "Unauthorized: Cannot reorder tasks"
              });
            }
          }
        }
        const updatedTasks = [];
        await adapter.transaction(async (tx) => {
          for (let i = 0; i < taskIds.length; i++) {
            const taskId = taskIds[i];
            if (!taskId) continue;
            const updated = await tx.update({
              model: "kanbanTask",
              where: [
                { field: "id", value: taskId },
                {
                  field: "columnId",
                  value: columnId,
                  operator: "eq"
                }
              ],
              update: { order: i, updatedAt: /* @__PURE__ */ new Date() }
            });
            if (updated) {
              updatedTasks.push(updated);
            }
          }
        });
        if (hooks?.onTaskUpdated) {
          for (const task of updatedTasks) {
            await hooks.onTaskUpdated(task, context);
          }
        }
        return { success: true };
      }
    );
    return {
      listBoards,
      getBoard,
      createBoard,
      updateBoard,
      deleteBoard,
      createColumn,
      updateColumn,
      deleteColumn,
      reorderColumns,
      createTask,
      updateTask,
      deleteTask,
      moveTask,
      reorderTasks
    };
  }
});

export { kanbanBackendPlugin };
