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

// ──────────────────────────────────────────────────────────────────────────
// Zugriffsberechnung für den Cloud-Speicher.
//
// Regeln:
//  • Eigene Ordner/Dateien sind immer zugänglich.
//  • Team-Ordner (mit teamId) gehören allen Mitgliedern des Teams — dort
//    darf jedes Mitglied lesen und schreiben.
//  • Eine Ordner-Freigabe gilt für den Ordner UND alle Unterordner samt
//    deren Dateien — sonst müsste man jede Ebene einzeln freigeben.
//  • Einzelne Dateien können zusätzlich direkt freigegeben werden.
//  • Bearbeiten darf, wem der Ordner gehört oder wer eine Freigabe mit
//    Bearbeitungsrecht hat (ebenfalls vererbt).
// ──────────────────────────────────────────────────────────────────────────

export type CloudScope = {
  userId: string;
  /** Ordner, die gelesen werden dürfen. */
  readFolders: Set<string>;
  /** Ordner, in denen geändert werden darf. */
  writeFolders: Set<string>;
  /** Direkt freigegebene Dateien (lesen). */
  readFiles: Set<string>;
  /** Direkt freigegebene Dateien (schreiben). */
  writeFiles: Set<string>;
};

type FolderRow = {
  id: string;
  parentId: string | null;
  ownerId: string;
  teamId: string | null;
  shares: { canEdit: boolean; userId: string | null; teamId: string | null; roleId: string | null }[];
};

/** Team- und Rollen-IDs des Nutzers. */
async function viewerGroups(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: new Set(teams.map((t) => t.teamId)),
    roleIds: new Set(roles.map((r) => r.roleId)),
  };
}

/** Berechnet alles, was der Nutzer sehen und ändern darf. */
export async function getCloudScope(userId: string): Promise<CloudScope> {
  const [{ teamIds, roleIds }, folders, fileShares] = await Promise.all([
    viewerGroups(userId),
    prisma.cloudFolder.findMany({
      select: {
        id: true,
        parentId: true,
        ownerId: true,
        teamId: true,
        shares: { select: { canEdit: true, userId: true, teamId: true, roleId: true } },
      },
    }),
    prisma.cloudShare.findMany({
      where: { fileId: { not: null } },
      select: { fileId: true, canEdit: true, userId: true, teamId: true, roleId: true },
    }),
  ]);

  const matches = (s: { userId: string | null; teamId: string | null; roleId: string | null }) =>
    (s.userId !== null && s.userId === userId) ||
    (s.teamId !== null && teamIds.has(s.teamId)) ||
    (s.roleId !== null && roleIds.has(s.roleId));

  // 1. Direkter Zugriff je Ordner.
  const read = new Set<string>();
  const write = new Set<string>();
  for (const f of folders as FolderRow[]) {
    // Team-Ordner: Mitgliedschaft entscheidet, Freigaben spielen keine Rolle.
    if (f.teamId !== null) {
      if (teamIds.has(f.teamId)) {
        read.add(f.id);
        write.add(f.id);
      }
      continue;
    }
    if (f.ownerId === userId) {
      read.add(f.id);
      write.add(f.id);
      continue;
    }
    const mine = f.shares.filter(matches);
    if (mine.length === 0) continue;
    read.add(f.id);
    if (mine.some((s) => s.canEdit)) write.add(f.id);
  }

  // 2. Vererbung an alle Unterordner (iterativ, verträgt beliebige Tiefe).
  const childrenOf = new Map<string, string[]>();
  for (const f of folders) {
    if (!f.parentId) continue;
    childrenOf.set(f.parentId, [...(childrenOf.get(f.parentId) ?? []), f.id]);
  }
  const spread = (target: Set<string>) => {
    const queue = [...target];
    while (queue.length > 0) {
      const current = queue.pop() as string;
      for (const child of childrenOf.get(current) ?? []) {
        if (!target.has(child)) {
          target.add(child);
          queue.push(child);
        }
      }
    }
  };
  spread(read);
  spread(write);

  // 3. Einzeln freigegebene Dateien.
  const readFiles = new Set<string>();
  const writeFiles = new Set<string>();
  for (const s of fileShares) {
    if (!s.fileId || !matches(s)) continue;
    readFiles.add(s.fileId);
    if (s.canEdit) writeFiles.add(s.fileId);
  }

  return { userId, readFolders: read, writeFolders: write, readFiles, writeFiles };
}

export function canReadFolder(scope: CloudScope, folderId: string | null): boolean {
  // Die Wurzel ist die persönliche Ablage — immer zugänglich.
  return folderId === null || scope.readFolders.has(folderId);
}

export function canWriteFolder(scope: CloudScope, folderId: string | null): boolean {
  return folderId === null || scope.writeFolders.has(folderId);
}

export function canReadFile(
  scope: CloudScope,
  file: { id: string; folderId: string | null; ownerId: string },
): boolean {
  if (file.ownerId === scope.userId) return true;
  if (scope.readFiles.has(file.id)) return true;
  return file.folderId !== null && scope.readFolders.has(file.folderId);
}

export function canWriteFile(
  scope: CloudScope,
  file: { id: string; folderId: string | null; ownerId: string },
): boolean {
  if (file.ownerId === scope.userId) return true;
  if (scope.writeFiles.has(file.id)) return true;
  return file.folderId !== null && scope.writeFolders.has(file.folderId);
}
