import "server-only";
import { prisma } from "@/lib/prisma";

// ──────────────────────────────────────────────────────────────────────────
// Lesezugriffe des Passwortmanagers. Geheimnisse verlassen diese Schicht
// niemals — entschlüsselt wird ausschließlich in `revealSecretAction`.
// ──────────────────────────────────────────────────────────────────────────

export type VaultTarget = { id: string; name: string; color?: string | null };

export type VaultEntryDTO = {
  id: string;
  title: string;
  username: string | null;
  url: string | null;
  category: string | null;
  notes: string | null;
  pinProtected: boolean;
  pinLength: number;
  pinHint: string | null;
  locked: boolean;
  owner: VaultTarget;
  isOwner: boolean;
  canEdit: boolean;
  canDelete: boolean;
  canShare: boolean;
  shareEdit: boolean;
  users: VaultTarget[];
  teams: VaultTarget[];
  roles: VaultTarget[];
  updatedAt: string;
  rotatedAt: string;
};

const dn = (u: { name: string; displayName: string | null }) => u.displayName ?? u.name;

const entrySelect = {
  id: true,
  title: true,
  username: true,
  url: true,
  category: true,
  notes: true,
  pinProtected: true,
  pinLength: true,
  pinHint: true,
  pinLockedUntil: true,
  ownerId: true,
  owner: { select: { id: true, name: true, displayName: true } },
  updatedAt: true,
  rotatedAt: true,
  shares: {
    select: {
      canEdit: true,
      user: { select: { id: true, name: true, displayName: true } },
      team: { select: { id: true, name: true, color: true } },
      role: { select: { id: true, name: true } },
    },
  },
} as const;

/** Team- und Rollen-IDs des Nutzers — Basis für Freigabe-Treffer. */
export async function getViewerScope(userId: string) {
  const [teams, roles] = await Promise.all([
    prisma.teamMember.findMany({ where: { userId }, select: { teamId: true } }),
    prisma.userRole.findMany({ where: { userId }, select: { roleId: true } }),
  ]);
  return {
    teamIds: teams.map((t) => t.teamId),
    roleIds: roles.map((r) => r.roleId),
  };
}

type Scope = { teamIds: string[]; roleIds: string[] };

/** Prisma-Filter: alle Einträge, die der Nutzer sehen darf. */
export function visibleWhere(userId: string, scope: Scope, viewAll: boolean) {
  if (viewAll) return {};
  return {
    OR: [
      { ownerId: userId },
      { shares: { some: { userId } } },
      ...(scope.teamIds.length ? [{ shares: { some: { teamId: { in: scope.teamIds } } } }] : []),
      ...(scope.roleIds.length ? [{ shares: { some: { roleId: { in: scope.roleIds } } } }] : []),
    ],
  };
}

type RawEntry = {
  id: string;
  title: string;
  username: string | null;
  url: string | null;
  category: string | null;
  notes: string | null;
  pinProtected: boolean;
  pinLength: number | null;
  pinHint: string | null;
  pinLockedUntil: Date | null;
  ownerId: string;
  owner: { id: string; name: string; displayName: string | null };
  updatedAt: Date;
  rotatedAt: Date;
  shares: {
    canEdit: boolean;
    user: { id: string; name: string; displayName: string | null } | null;
    team: { id: string; name: string; color: string | null } | null;
    role: { id: string; name: string } | null;
  }[];
};

function toDTO(
  e: RawEntry,
  userId: string,
  scope: Scope,
  perms: { manage: boolean; share: boolean; viewAll: boolean },
): VaultEntryDTO {
  const isOwner = e.ownerId === userId;

  // Trifft eine Freigabe auf mich zu — direkt, über ein Team oder eine Rolle?
  const mine = e.shares.filter(
    (s) =>
      s.user?.id === userId ||
      (s.team && scope.teamIds.includes(s.team.id)) ||
      (s.role && scope.roleIds.includes(s.role.id)),
  );
  const sharedEdit = mine.some((s) => s.canEdit);

  return {
    id: e.id,
    title: e.title,
    username: e.username,
    url: e.url,
    category: e.category,
    notes: e.notes,
    pinProtected: e.pinProtected,
    pinLength: e.pinLength ?? 4,
    pinHint: e.pinHint,
    locked: Boolean(e.pinLockedUntil && e.pinLockedUntil > new Date()),
    owner: { id: e.owner.id, name: dn(e.owner) },
    isOwner,
    canEdit: perms.manage && (isOwner || sharedEdit),
    canDelete: isOwner || perms.viewAll,
    canShare: perms.share && isOwner,
    shareEdit: e.shares.some((s) => s.canEdit),
    users: e.shares
      .filter((s) => s.user)
      .map((s) => ({ id: s.user!.id, name: dn(s.user!) })),
    teams: e.shares
      .filter((s) => s.team)
      .map((s) => ({ id: s.team!.id, name: s.team!.name, color: s.team!.color })),
    roles: e.shares.filter((s) => s.role).map((s) => ({ id: s.role!.id, name: s.role!.name })),
    updatedAt: e.updatedAt.toISOString(),
    rotatedAt: e.rotatedAt.toISOString(),
  };
}

/** Alle für den Nutzer sichtbaren Einträge. */
export async function getVaultEntries(
  userId: string,
  permissions: readonly string[] | undefined,
): Promise<VaultEntryDTO[]> {
  const perms = {
    manage: Boolean(permissions?.includes("vault.manage")),
    share: Boolean(permissions?.includes("vault.share")),
    viewAll: Boolean(permissions?.includes("vault.view_all")),
  };
  const scope = await getViewerScope(userId);

  const rows = await prisma.vaultEntry.findMany({
    where: visibleWhere(userId, scope, perms.viewAll),
    orderBy: [{ title: "asc" }],
    select: entrySelect,
  });

  return rows.map((r) => toDTO(r, userId, scope, perms));
}

/** Auswahllisten für Freigaben (Personen, Teams, Rollen). */
export async function getShareTargets() {
  const [users, teams, roles] = await Promise.all([
    prisma.user.findMany({
      where: { status: { not: "SUSPENDED" } },
      orderBy: { name: "asc" },
      select: { id: true, name: true, displayName: true, avatarUrl: true },
    }),
    prisma.team.findMany({
      orderBy: { name: "asc" },
      select: { id: true, name: true, color: true },
    }),
    prisma.role.findMany({
      orderBy: { rank: "asc" },
      select: { id: true, name: true, key: true },
    }),
  ]);

  return {
    users: users.map((u) => ({ id: u.id, name: dn(u), avatarUrl: u.avatarUrl })),
    teams,
    roles,
  };
}

export type ShareTargets = Awaited<ReturnType<typeof getShareTargets>>;
