import { eq, inArray } from "drizzle-orm";
import { getDb } from "../config/database.js";
import {
  users,
  roles,
  userRoles,
  permissions,
  rolePermissions,
  refreshTokens,
  roleViewports,
} from "./iam.schema.js";
import type { User, Role, Permission, RoleViewport } from "./iam.schema.js";
import { DatabaseError } from "../shared/errors/application-error.js";

export class IamRepository {
  async createUser(data: {
    id: string;
    email: string;
    passwordHash: string;
    name: string;
  }): Promise<User> {
    const db = getDb();
    await db.insert(users).values(data);
    const [result] = await db.select().from(users).where(eq(users.id, data.id));
    if (!result) {
      throw new DatabaseError("Failed to create user");
    }
    return result;
  }

  async findUserByEmail(email: string): Promise<User | undefined> {
    const db = getDb();
    const [result] = await db
      .select()
      .from(users)
      .where(eq(users.email, email));
    return result;
  }

  async findUserById(id: string): Promise<User | undefined> {
    const db = getDb();
    const [result] = await db.select().from(users).where(eq(users.id, id));
    return result;
  }

  async getUserRoles(userId: string): Promise<Role[]> {
    const db = getDb();
    const result = await db
      .select()
      .from(roles)
      .innerJoin(userRoles, eq(roles.id, userRoles.roleId))
      .where(eq(userRoles.userId, userId));
    return result.map((r) => r.iam_roles);
  }

  async getUserPermissions(userId: string): Promise<Permission[]> {
    const db = getDb();
    const userRoleRows = await db
      .select()
      .from(userRoles)
      .where(eq(userRoles.userId, userId));
    const roleIds = userRoleRows.map((r) => r.roleId);
    if (roleIds.length === 0) return [];
    const result = await db
      .select()
      .from(permissions)
      .innerJoin(
        rolePermissions,
        eq(permissions.id, rolePermissions.permissionId),
      )
      .where(inArray(rolePermissions.roleId, roleIds));
    return result.map((r) => r.iam_permissions);
  }

  async getUserViewports(userId: string): Promise<RoleViewport[]> {
    const db = getDb();
    const userRoleRows = await db
      .select()
      .from(userRoles)
      .where(eq(userRoles.userId, userId));
    const roleIds = userRoleRows.map((r) => r.roleId);
    if (roleIds.length === 0) return [];
    return db
      .select()
      .from(roleViewports)
      .where(inArray(roleViewports.roleId, roleIds));
  }

  async getUserDataScope(userId: string): Promise<string> {
    const db = getDb();
    const [user] = await db
      .select({ dataScope: users.dataScope })
      .from(users)
      .where(eq(users.id, userId));
    return user?.dataScope ?? "self";
  }

  async getAllRoles(): Promise<Role[]> {
    const db = getDb();
    return db.select().from(roles);
  }

  async getAllPermissions(): Promise<Permission[]> {
    const db = getDb();
    return db.select().from(permissions);
  }

  async assignRole(userId: string, roleId: string): Promise<void> {
    const db = getDb();
    await db.insert(userRoles).values({ userId, roleId });
  }

  async createRefreshToken(data: {
    id: string;
    userId: string;
    tokenHash: string;
    expiresAt: Date;
  }): Promise<void> {
    const db = getDb();
    await db.insert(refreshTokens).values(data);
  }

  async revokeRefreshToken(id: string): Promise<void> {
    const db = getDb();
    await db
      .update(refreshTokens)
      .set({ revokedAt: new Date() })
      .where(eq(refreshTokens.id, id));
  }
}
