/**
 * session-loader.ts
 *
 * Shared helper for decrypting a browser session storageState blob.
 * Used by both:
 *   - src/engine/tools/user-folder.ts (RunUserScript tool)
 *   - src/bridge/user-folder-api.ts  (POST /scripts/:name/run endpoint)
 */

import type { BrowserSessionRepo } from '../db/browser-session-repo.js';
import { decryptProfileState } from '../crypto/profile-dek.js';
import { logger } from '../logger.js';

interface SessionLoaderDeps {
  sessRepo: BrowserSessionRepo;
  masterKeyPath: string;
}

export type SessionLoadError =
  | { kind: 'profile_not_found'; message: string }
  | { kind: 'profile_not_active'; message: string }
  | { kind: 'dek_not_found'; message: string }
  | { kind: 'decrypt_error'; message: string };

export type SessionLoadResult =
  | { ok: true; storageState: object }
  | { ok: false; error: SessionLoadError };

/**
 * Load and decrypt the storageState for the given session profile.
 *
 * Returns `{ ok: true, storageState }` on success.
 * Returns `{ ok: false, error }` when the profile is missing, wrong owner,
 * not active, or decryption fails.
 * Never throws — all errors are surfaced via the returned error shape.
 */
export async function loadSessionStateForUser(
  deps: SessionLoaderDeps,
  ownerId: string,
  sessionProfileId: number,
): Promise<SessionLoadResult> {
  const { sessRepo, masterKeyPath } = deps;

  // Owner-scoped lookup — ensures profile belongs to ownerId
  const profile = sessRepo.getProfileById(sessionProfileId, ownerId);
  if (!profile) {
    return {
      ok: false,
      error: {
        kind: 'profile_not_found',
        message: `session profile ${sessionProfileId} not found or does not belong to this user`,
      },
    };
  }

  if (profile.status !== 'active' || !profile.encryptedStateBlob) {
    return {
      ok: false,
      error: {
        kind: 'profile_not_active',
        message: `session profile ${sessionProfileId} is not active (status=${profile.status})`,
      },
    };
  }

  // No key material at all to decrypt this profile (space DEK for a space-owned
  // profile, or the owner's user DEK) → dek_not_found (distinct from a corrupt
  // blob). Space-as-Principal P2: a space profile may have only the space DEK.
  const hasDek =
    (profile.spaceId != null && sessRepo.getSpaceDek(profile.spaceId) != null) ||
    sessRepo.getUserDek(ownerId) != null;
  if (!hasDek) {
    return {
      ok: false,
      error: {
        kind: 'dek_not_found',
        message: 'DEK not found — the session profile has not been saved yet',
      },
    };
  }

  try {
    // Space-as-Principal P2: space-owned profile → space DEK (owner-DEK fallback
    // for pre-migration blobs); personal → owner DEK.
    const stateJson = decryptProfileState({ masterKeyPath, sessRepo }, {
      ownerId,
      spaceId: profile.spaceId,
      encryptedStateBlob: profile.encryptedStateBlob,
    });
    const storageState = JSON.parse(stateJson) as object;
    return { ok: true, storageState };
  } catch (err) {
    logger.error(`[session-loader] storageState decrypt failed: ${(err as Error).message}`);
    return {
      ok: false,
      error: {
        kind: 'decrypt_error',
        message: `failed to decrypt session storageState: ${(err as Error).message}`,
      },
    };
  }
}
