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 UserRecord = {
  id: string;
  name: string | null;
  systemRole?: SystemRole | string;
};

type UserAdminDeps = {
  createUserCaller: (ctx: TRPCContext) => {
    list: () => Promise<unknown[]>;
    create: (params: {
      email: string;
      name: string;
      systemRole?: SystemRole;
      password: string;
    }) => Promise<UserRecord>;
    setPassword: (params: { userId: string; password: string }) => Promise<Record<string, unknown>>;
    updateRole: (params: { id: string; systemRole: SystemRole }) => Promise<UserRecord>;
    updateName: (params: { id: string; name: string }) => Promise<UserRecord>;
    linkResource: (
      params: { userId: string; resourceId: string | null },
    ) => Promise<Record<string, unknown>>;
    autoLinkAllByEmail: () => Promise<Record<string, unknown> & { linked: number }>;
    setPermissions: (params: {
      userId: string;
      overrides: {
        granted?: string[];
        denied?: string[];
        chapterIds?: string[];
      } | null;
    }) => Promise<UserRecord>;
    resetPermissions: (params: { userId: string }) => Promise<UserRecord>;
    getEffectivePermissions: (params: { userId: string }) => Promise<unknown>;
    disableTotp: (params: { userId: string }) => Promise<Record<string, unknown>>;
  };
  createScopedCallerContext: (ctx: ToolContext) => TRPCContext;
  toAssistantUserMutationError: (
    error: unknown,
    mode?: "create" | "password",
  ) => AssistantToolErrorResult | null;
  toAssistantUserResourceLinkError: (
    error: unknown,
  ) => AssistantToolErrorResult | null;
};

export const userAdminToolDefinitions: ToolDef[] = withToolAccess([
  {
    type: "function",
    function: {
      name: "list_users",
      description: "List all system users via the admin user router, including role and MFA state. Admin role required.",
      parameters: {
        type: "object",
        properties: {
          limit: { type: "integer", description: "Max results. Default: 50" },
        },
      },
    },
  },
  {
    type: "function",
    function: {
      name: "create_user",
      description: "Create a new system user and auto-link a matching resource by email when possible. Admin role required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          email: { type: "string", description: "User email address." },
          name: { type: "string", description: "Display name." },
          systemRole: { type: "string", enum: ["ADMIN", "MANAGER", "CONTROLLER", "USER", "VIEWER"], description: "Initial system role." },
          password: { type: "string", description: "Initial password, minimum 8 characters." },
        },
        required: ["email", "name", "password"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "set_user_password",
      description: "Reset a user's password. Admin role required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          userId: { type: "string", description: "User ID." },
          password: { type: "string", description: "New password, minimum 8 characters." },
        },
        required: ["userId", "password"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "update_user_role",
      description: "Change a user's system role. Admin role required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          id: { type: "string", description: "User ID." },
          systemRole: { type: "string", enum: ["ADMIN", "MANAGER", "CONTROLLER", "USER", "VIEWER"] },
        },
        required: ["id", "systemRole"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "update_user_name",
      description: "Rename a user. Admin role required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          id: { type: "string", description: "User ID." },
          name: { type: "string", description: "New display name." },
        },
        required: ["id", "name"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "link_user_resource",
      description: "Link or unlink a user to a resource. Admin role required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          userId: { type: "string", description: "User ID." },
          resourceId: { type: ["string", "null"], description: "Resource ID or null to unlink." },
        },
        required: ["userId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "auto_link_users_by_email",
      description: "Auto-link all users without a resource to matching resources by email. Admin role required. Always confirm first.",
      parameters: { type: "object", properties: {} },
    },
  },
  {
    type: "function",
    function: {
      name: "set_user_permissions",
      description: "Set explicit permission overrides for a user. Admin role required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          userId: { type: "string", description: "User ID." },
          overrides: {
            type: ["object", "null"],
            properties: {
              granted: { type: "array", items: { type: "string" } },
              denied: { type: "array", items: { type: "string" } },
              chapterIds: { type: "array", items: { type: "string" } },
            },
            description: "Permission override object or null to clear.",
          },
        },
        required: ["userId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "reset_user_permissions",
      description: "Reset a user's permission overrides back to role defaults. Admin role required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          userId: { type: "string", description: "User ID." },
        },
        required: ["userId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "get_effective_user_permissions",
      description: "Get a user's resolved permissions, role, and explicit overrides. Admin role required.",
      parameters: {
        type: "object",
        properties: {
          userId: { type: "string", description: "User ID." },
        },
        required: ["userId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "disable_user_totp",
      description: "Disable MFA TOTP for a user as an admin override. Admin role required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          userId: { type: "string", description: "User ID." },
        },
        required: ["userId"],
      },
    },
  },
], {
  list_users: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
  create_user: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
  set_user_password: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
  update_user_role: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
  update_user_name: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
  link_user_resource: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
  auto_link_users_by_email: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
  set_user_permissions: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
  reset_user_permissions: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
  get_effective_user_permissions: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
  disable_user_totp: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
});

export function createUserAdminExecutors(
  deps: UserAdminDeps,
): Record<string, ToolExecutor> {
  return {
    async list_users(params: { limit?: number }, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      const users = await caller.list();
      return users.slice(0, Math.min(params.limit ?? 50, 100));
    },

    async create_user(params: {
      email: string;
      name: string;
      systemRole?: SystemRole;
      password: string;
    }, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      let user;
      try {
        user = await caller.create({
          email: params.email,
          name: params.name,
          password: params.password,
          ...(params.systemRole !== undefined ? { systemRole: params.systemRole } : {}),
        });
      } catch (error) {
        const mapped = deps.toAssistantUserMutationError(error, "create");
        if (mapped) {
          return mapped;
        }
        throw error;
      }
      return {
        __action: "invalidate" as const,
        scope: ["user", "resource"],
        success: true,
        user,
        userId: user.id,
        message: `Created user ${user.name}.`,
      };
    },

    async set_user_password(params: { userId: string; password: string }, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      let result;
      try {
        result = await caller.setPassword(params);
      } catch (error) {
        const mapped = deps.toAssistantUserMutationError(error, "password");
        if (mapped) {
          return mapped;
        }
        throw error;
      }
      return {
        __action: "invalidate" as const,
        scope: ["user"],
        ...result,
        message: `Reset password for user ${params.userId}.`,
      };
    },

    async update_user_role(params: { id: string; systemRole: SystemRole }, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      let user;
      try {
        user = await caller.updateRole(params);
      } catch (error) {
        const mapped = deps.toAssistantUserMutationError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }
      return {
        __action: "invalidate" as const,
        scope: ["user"],
        success: true,
        user,
        userId: user.id,
        message: `Updated role for ${user.name} to ${user.systemRole}.`,
      };
    },

    async update_user_name(params: { id: string; name: string }, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      let user;
      try {
        user = await caller.updateName(params);
      } catch (error) {
        const mapped = deps.toAssistantUserMutationError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }
      return {
        __action: "invalidate" as const,
        scope: ["user"],
        success: true,
        user,
        userId: user.id,
        message: `Updated user name to ${user.name}.`,
      };
    },

    async link_user_resource(params: { userId: string; resourceId?: string | null }, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      let result;
      try {
        result = await caller.linkResource({
          userId: params.userId,
          resourceId: params.resourceId ?? null,
        });
      } catch (error) {
        const mapped = deps.toAssistantUserResourceLinkError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }
      return {
        __action: "invalidate" as const,
        scope: ["user", "resource"],
        ...result,
        message: params.resourceId ? "Linked user to resource." : "Unlinked user resource.",
      };
    },

    async auto_link_users_by_email(_params: Record<string, never>, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      const result = await caller.autoLinkAllByEmail();
      return {
        __action: "invalidate" as const,
        scope: ["user", "resource"],
        success: true,
        ...result,
        message: `Auto-linked ${result.linked} user(s) by email.`,
      };
    },

    async set_user_permissions(params: {
      userId: string;
      overrides?: {
        granted?: string[];
        denied?: string[];
        chapterIds?: string[];
      } | null;
    }, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      let user;
      try {
        user = await caller.setPermissions({
          userId: params.userId,
          overrides: params.overrides ?? null,
        });
      } catch (error) {
        const mapped = deps.toAssistantUserMutationError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }
      return {
        __action: "invalidate" as const,
        scope: ["user"],
        success: true,
        user,
        userId: user.id,
        message: params.overrides ? "Updated user permission overrides." : "Cleared user permission overrides.",
      };
    },

    async reset_user_permissions(params: { userId: string }, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      let user;
      try {
        user = await caller.resetPermissions(params);
      } catch (error) {
        const mapped = deps.toAssistantUserMutationError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }
      return {
        __action: "invalidate" as const,
        scope: ["user"],
        success: true,
        user,
        userId: user.id,
        message: "Reset user permissions to role defaults.",
      };
    },

    async get_effective_user_permissions(params: { userId: string }, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      try {
        return await caller.getEffectivePermissions(params);
      } catch (error) {
        const mapped = deps.toAssistantUserMutationError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }
    },

    async disable_user_totp(params: { userId: string }, ctx: ToolContext) {
      const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
      let result;
      try {
        result = await caller.disableTotp(params);
      } catch (error) {
        const mapped = deps.toAssistantUserMutationError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }
      return {
        __action: "invalidate" as const,
        scope: ["user"],
        success: true,
        ...result,
        message: `Disabled TOTP for user ${params.userId}.`,
      };
    },
  };
}
