import { SystemRole } from "@capakraken/shared";
import type { TRPCContext } from "../../trpc.js";
import { withToolAccess, type ToolContext, type ToolDef, type ToolExecutor } from "./shared.js";

type AssistantToolErrorResult = { error: string };

type ColumnPreferenceView =
  | "resources"
  | "projects"
  | "allocations"
  | "vacations"
  | "roles"
  | "users"
  | "blueprints";

type UserSelfServiceDeps = {
  createUserCaller: (ctx: TRPCContext) => {
    listAssignable: () => Promise<unknown>;
    me: () => Promise<unknown>;
    getDashboardLayout: () => Promise<unknown>;
    saveDashboardLayout: (params: { layout: unknown[] }) => Promise<Record<string, unknown>>;
    getFavoriteProjectIds: () => Promise<unknown>;
    toggleFavoriteProject: (
      params: { projectId: string },
    ) => Promise<Record<string, unknown> & { added: boolean }>;
    getColumnPreferences: () => Promise<unknown>;
    setColumnPreferences: (params: {
      view: ColumnPreferenceView;
      visible?: string[];
      sort?: { field: string; dir: "asc" | "desc" } | null;
      rowOrder?: string[] | null;
    }) => Promise<Record<string, unknown>>;
    generateTotpSecret: () => Promise<Record<string, unknown>>;
    verifyAndEnableTotp: (params: { token: string }) => Promise<Record<string, unknown>>;
    getMfaStatus: () => Promise<unknown>;
    activeCount: () => Promise<unknown>;
  };
  createScopedCallerContext: (ctx: ToolContext) => TRPCContext;
  toAssistantCurrentUserError: (
    error: unknown,
  ) => AssistantToolErrorResult | null;
  toAssistantTotpEnableError: (
    error: unknown,
  ) => AssistantToolErrorResult | null;
};

export const userSelfServiceToolDefinitions: ToolDef[] = withToolAccess([
  {
    type: "function",
    function: {
      name: "list_assignable_users",
      description: "List lightweight users available for assignment workflows. Manager or admin role required.",
      parameters: { type: "object", properties: {} },
    },
  },
  {
    type: "function",
    function: {
      name: "get_current_user",
      description: "Get the authenticated user's own profile, role, and permission overrides.",
      parameters: { type: "object", properties: {} },
    },
  },
  {
    type: "function",
    function: {
      name: "get_dashboard_layout",
      description: "Get the authenticated user's saved dashboard widget layout and last update timestamp.",
      parameters: { type: "object", properties: {} },
    },
  },
  {
    type: "function",
    function: {
      name: "save_dashboard_layout",
      description: "Save the authenticated user's dashboard layout. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          layout: {
            type: "array",
            description: "Dashboard layout items as stored by the user router.",
            items: { type: "object" },
          },
        },
        required: ["layout"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "get_favorite_project_ids",
      description: "Get the authenticated user's favorite project IDs.",
      parameters: { type: "object", properties: {} },
    },
  },
  {
    type: "function",
    function: {
      name: "toggle_favorite_project",
      description: "Add or remove a project from the authenticated user's favorites. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          projectId: { type: "string", description: "Project ID." },
        },
        required: ["projectId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "get_column_preferences",
      description: "Get the authenticated user's saved table column preferences for all supported views.",
      parameters: { type: "object", properties: {} },
    },
  },
  {
    type: "function",
    function: {
      name: "set_column_preferences",
      description: "Update the authenticated user's table column preferences for one view. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          view: {
            type: "string",
            enum: ["resources", "projects", "allocations", "vacations", "roles", "users", "blueprints"],
            description: "View key to update.",
          },
          visible: {
            type: "array",
            items: { type: "string" },
            description: "Visible column IDs.",
          },
          sort: {
            type: ["object", "null"],
            properties: {
              field: { type: "string" },
              dir: { type: "string", enum: ["asc", "desc"] },
            },
            description: "Sort state. Use null to clear it.",
          },
          rowOrder: {
            type: ["array", "null"],
            items: { type: "string" },
            description: "Optional row order. Use null to clear it.",
          },
        },
        required: ["view"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "generate_totp_secret",
      description: "Generate a new MFA TOTP secret and provisioning URI for the authenticated user. Always confirm first. The secret is sensitive.",
      parameters: { type: "object", properties: {} },
    },
  },
  {
    type: "function",
    function: {
      name: "verify_and_enable_totp",
      description: "Verify a 6-digit MFA TOTP token and enable MFA for the authenticated user. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          token: { type: "string", description: "6-digit TOTP token." },
        },
        required: ["token"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "get_mfa_status",
      description: "Get the authenticated user's MFA status.",
      parameters: { type: "object", properties: {} },
    },
  },
  {
    type: "function",
    function: {
      name: "get_active_user_count",
      description: "Get the number of users active in the last five minutes. Admin role required.",
      parameters: { type: "object", properties: {} },
    },
  },
], {
  list_assignable_users: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER],
  },
  get_active_user_count: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
});

export function createUserSelfServiceExecutors(
  deps: UserSelfServiceDeps,
): Record<string, ToolExecutor> {
  async function withCurrentUserErrorMapping<T>(run: () => Promise<T>) {
    try {
      return await run();
    } catch (error) {
      const mapped = deps.toAssistantCurrentUserError(error);
      if (mapped) {
        return mapped;
      }
      throw error;
    }
  }

  return {
    async list_assignable_users(_params: Record<string, never>, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      return caller.listAssignable();
    },

    async get_current_user(_params: Record<string, never>, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      return withCurrentUserErrorMapping(() => caller.me());
    },

    async get_dashboard_layout(_params: Record<string, never>, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      return withCurrentUserErrorMapping(() => caller.getDashboardLayout());
    },

    async save_dashboard_layout(params: { layout: unknown[] }, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      const result = await withCurrentUserErrorMapping(
        () => caller.saveDashboardLayout({ layout: params.layout }),
      );
      if ("error" in result) {
        return result;
      }
      return {
        __action: "invalidate" as const,
        scope: ["dashboard"],
        success: true,
        ...result,
        message: "Saved dashboard layout.",
      };
    },

    async get_favorite_project_ids(_params: Record<string, never>, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      return withCurrentUserErrorMapping(() => caller.getFavoriteProjectIds());
    },

    async toggle_favorite_project(params: { projectId: string }, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      const result = await withCurrentUserErrorMapping(
        () => caller.toggleFavoriteProject({ projectId: params.projectId }),
      );
      if ("error" in result) {
        return result;
      }
      return {
        __action: "invalidate" as const,
        scope: ["project"],
        success: true,
        ...result,
        message: result.added ? "Added project to favorites." : "Removed project from favorites.",
      };
    },

    async get_column_preferences(_params: Record<string, never>, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      return withCurrentUserErrorMapping(() => caller.getColumnPreferences());
    },

    async set_column_preferences(params: {
      view: ColumnPreferenceView;
      visible?: string[];
      sort?: { field: string; dir: "asc" | "desc" } | null;
      rowOrder?: string[] | null;
    }, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      const result = await withCurrentUserErrorMapping(() => caller.setColumnPreferences({
        view: params.view,
        ...(params.visible !== undefined ? { visible: params.visible } : {}),
        ...(params.sort !== undefined ? { sort: params.sort } : {}),
        ...(params.rowOrder !== undefined ? { rowOrder: params.rowOrder } : {}),
      }));
      if ("error" in result) {
        return result;
      }
      return {
        __action: "invalidate" as const,
        scope: ["user"],
        success: true,
        ...result,
        message: `Updated column preferences for ${params.view}.`,
      };
    },

    async generate_totp_secret(_params: Record<string, never>, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      const result = await withCurrentUserErrorMapping(() => caller.generateTotpSecret());
      if ("error" in result) {
        return result;
      }
      return {
        __action: "invalidate" as const,
        scope: ["user"],
        success: true,
        ...result,
        message: "Generated a new MFA TOTP secret.",
      };
    },

    async verify_and_enable_totp(params: { token: string }, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      let result;
      try {
        result = await caller.verifyAndEnableTotp({ token: params.token });
      } catch (error) {
        const mapped = deps.toAssistantTotpEnableError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }
      return {
        __action: "invalidate" as const,
        scope: ["user"],
        success: true,
        ...result,
        message: "Enabled MFA TOTP.",
      };
    },

    async get_mfa_status(_params: Record<string, never>, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      return withCurrentUserErrorMapping(() => caller.getMfaStatus());
    },

    async get_active_user_count(_params: Record<string, never>, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      return caller.activeCount();
    },
  };
}
