import { getUserById } from 'officerdb';
import { getHomeDir, getOwnerHomeDir } from './data-path';

// Whose home does a session run in.
//
// This is the seam the whole per-user story turns on, and it replaces `getOwnerHomeDir(email)` at every
// point where a REQUEST decides which directory it may touch. That function takes an email and discards it
// whenever `HOME_DIR` is set — which is always, on a real install — so every caller resolved to the owner's
// login home regardless of who was asking. Harmless while the surfaces around it were owner-only. Not
// harmless the moment a member can open a file browser.
//
// ── Why a member with no Linux account is REFUSED, not defaulted ──
//
// The tempting fallback is "no `osUser`? use the managed home under DATA_PATH anyway." It would work, and
// it would be wrong in the one direction that matters: a member's files would sit in a directory the
// platform's own service user owns and every other part of the system can read, while the UI showed them a
// private-looking home. Worse, an error in the resolution order — an owner check that stopped matching, say
// — would land a member in the OWNER'S home with write access, silently.
//
// So the rule is: a non-owner reaches an execution surface only if the kernel is enforcing the boundary.
// No Linux account means no confinement means no access, and the refusal names the fix.

export type HomeResolution =
  | {
      ok: true;
      home: string;
      isOwner: boolean;
      /**
       * The Linux account whose identity reaches this home, or `null` for the owner — who IS this process's
       * uid, so there is nobody to become.
       *
       * Carried because resolving the home is not enough to READ inside it: a member's files are theirs and
       * `claude` writes transcripts at mode 600, which clamps the platform's ACL entry to nothing. See
       * `read-as-user.ts`. Reported here rather than looked up again at each call site so that "whose home"
       * and "whose identity" cannot drift apart — they are one answer from one row.
       */
      osUser: string | null;
    }
  | { ok: false; reason: string; needsOsAccount: boolean };

/**
 * The directory this account's file browser, terminal and (eventually) agents are confined to.
 *
 * Reads the row rather than trusting the token, for the same reason `authorize.ts` re-reads `role`: a claim
 * minted at sign-in would keep working for thirty days after the account it describes changed. Enabling OS
 * users, or provisioning one for an existing member, must take effect on the next request.
 */
export async function resolveHomeDir(userId: number): Promise<HomeResolution> {
  let user;
  try {
    user = await getUserById(userId);
  } catch {
    // Same posture as every catch in authorize.ts: a database error denies. There is no safe guess about
    // whose home to open.
    return { ok: false, reason: 'could not resolve your account', needsOsAccount: false };
  }
  if (!user) return { ok: false, reason: 'account not found', needsOsAccount: false };

  // The owner runs in their real login home — the whole point of HOME_DIR, and what makes platform
  // terminals share config and credentials with the shell they use outside Officer.
  if (user.role === 'Super Admin') {
    return { ok: true, home: getOwnerHomeDir(user.email), isOwner: true, osUser: null };
  }

  if (!user.osUser) {
    return {
      ok: false,
      reason: 'this account has no Linux user on this machine, so there is nothing to confine it to',
      needsOsAccount: true,
    };
  }

  // `getHomeDir` and `osUserHome` are deliberately the same path: DATA_PATH/<email>/home is both the
  // managed home the platform provisions and the real passwd home of the Linux account. If those ever
  // diverge, a member's shell and their file browser would show different directories.
  return { ok: true, home: getHomeDir(user.email), isOwner: false, osUser: user.osUser };
}
