import "server-only";

import { cookies } from "next/headers";
import { Query } from "node-appwrite";

import { createAdminClient, getCurrentUser } from "./server";
import { DATABASE_ID, TABLES, type TenantKind, type TenantSettings } from "./schema";
import { ACTIVE_TENANT_COOKIE } from "./tenant-types";
import { getActiveTenantId, getUserTeams } from "./tenant";

export type TenantRole = "owner" | "admin" | "member";

export type TenantContext = {
  user: { id: string; name: string; email: string };
  tenantId: string;
  role: TenantRole;
  kind: TenantKind | null;
  settings: TenantSettings | null;
};

function pickHighestRole(roles: string[]): TenantRole | null {
  if (roles.includes("owner")) return "owner";
  if (roles.includes("admin")) return "admin";
  if (roles.includes("member")) return "member";
  return null;
}

export async function requireTenant(): Promise<TenantContext> {
  const user = await getCurrentUser();
  if (!user) throw new Error("UNAUTHENTICATED");

  let tenantId = await getActiveTenantId();
  if (!tenantId) {
    // Fallback: pick the user's first team (handles invite acceptees and
    // sessions where the active-tenant cookie/prefs weren't set yet).
    const userTeams = await getUserTeams();
    tenantId = userTeams?.teams[0]?.$id ?? null;
    if (tenantId) {
      try {
        (await cookies()).set(ACTIVE_TENANT_COOKIE, tenantId, {
          path: "/",
          httpOnly: true,
          sameSite: "strict",
          secure: process.env.NODE_ENV === "production",
          maxAge: 60 * 60 * 24 * 365,
        });
      } catch {
        /* setting cookie can fail in some Server Component paths; ignore */
      }
    }
  }
  if (!tenantId) throw new Error("NO_TENANT");

  const { tablesDB, teams } = createAdminClient();

  const memberships = await teams.listMemberships(tenantId);
  const membership = memberships.memberships.find((m) => m.userId === user.$id);
  if (!membership) throw new Error("NOT_A_MEMBER");

  const role = pickHighestRole(membership.roles) ?? "member";

  let settings: TenantSettings | null = null;
  try {
    const result = await tablesDB.listRows({
      databaseId: DATABASE_ID,
      tableId: TABLES.tenantSettings,
      queries: [Query.equal("tenantId", tenantId), Query.limit(1)],
    });
    settings = (result.rows[0] as unknown as TenantSettings) ?? null;
  } catch {
    settings = null;
  }

  return {
    user: { id: user.$id, name: user.name, email: user.email },
    tenantId,
    role,
    kind: settings?.kind ?? null,
    settings,
  };
}

export function requireRole(ctx: TenantContext, allowed: TenantRole[]): void {
  if (!allowed.includes(ctx.role)) {
    throw new Error("FORBIDDEN");
  }
}

export function requireTenantKind(
  ctx: TenantContext,
  allowed: TenantKind[],
): void {
  if (!ctx.kind || !allowed.includes(ctx.kind)) {
    throw new Error("FORBIDDEN_KIND");
  }
}
