import { readFile, stat } from "node:fs/promises";
import path from "node:path";
import { logger } from "@infra/logger";
import { getJobOwnerProfileId } from "@infra/request-context";
import { DEFAULT_JOB_OWNER_PROFILE_ID } from "@server/infra/job-owner-context";
import { getProfileById } from "@server/repositories/profiles";
import { getSetting } from "@server/repositories/settings";
import type { ResumeProfile } from "@shared/types";

/** Cache key is absolute path + file mtime (ms). */
let cachedLocalSourceKey: string | null = null;
let cachedLocalProfile: ResumeProfile | null = null;

/**
 * Prefer `JOBOPS_LOCAL_RESUME_PATH`, then `localResumeProfilePath` setting.
 * Relative paths resolve against `process.cwd()` (orchestrator working directory).
 */
export async function resolveLocalResumeFilePath(): Promise<string | null> {
  const envPath = process.env.JOBOPS_LOCAL_RESUME_PATH?.trim();
  if (envPath) {
    return path.isAbsolute(envPath)
      ? envPath
      : path.resolve(process.cwd(), envPath);
  }

  const ownerId = getJobOwnerProfileId();
  if (
    ownerId &&
    ownerId !== DEFAULT_JOB_OWNER_PROFILE_ID &&
    ownerId !== "__unmapped__"
  ) {
    const row = await getProfileById(ownerId);
    const tenantPath = row?.data?.resumeLocalPath?.trim();
    if (tenantPath) {
      return path.isAbsolute(tenantPath)
        ? tenantPath
        : path.resolve(process.cwd(), tenantPath);
    }
  }

  const raw = (await getSetting("localResumeProfilePath"))?.trim();
  if (!raw) return null;
  return path.isAbsolute(raw) ? raw : path.resolve(process.cwd(), raw);
}

async function loadProfileFromLocalFile(
  absolutePath: string,
  forceRefresh: boolean,
): Promise<ResumeProfile> {
  let mtimeKey = "unknown";
  try {
    const st = await stat(absolutePath);
    mtimeKey = String(st.mtimeMs);
  } catch {
    mtimeKey = "missing";
  }
  const cacheKey = `${absolutePath}:${mtimeKey}`;

  if (
    !forceRefresh &&
    cacheKey === cachedLocalSourceKey &&
    cachedLocalProfile !== null
  ) {
    return cachedLocalProfile;
  }

  let raw: string;
  try {
    raw = await readFile(absolutePath, "utf8");
  } catch (error) {
    const detail = error instanceof Error ? error.message : "Unknown error";
    throw new Error(
      `Cannot read local resume file at ${absolutePath}. ${detail}`,
    );
  }

  let parsed: unknown;
  try {
    parsed = JSON.parse(raw);
  } catch {
    throw new Error(
      `Local resume file at ${absolutePath} is not valid JSON (resume export shape expected).`,
    );
  }

  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
    throw new Error(
      `Local resume file at ${absolutePath} must be a single JSON object.`,
    );
  }

  cachedLocalSourceKey = cacheKey;
  cachedLocalProfile = parsed as ResumeProfile;
  logger.info("Profile loaded from local JSON file", {
    path: absolutePath,
  });
  return cachedLocalProfile;
}

/**
 * Get the base resume profile from a local JSON file when configured.
 *
 * Local sources (in order): `JOBOPS_LOCAL_RESUME_PATH` env, then `localResumeProfilePath` setting.
 *
 * Results are cached until clearProfileCache() is called.
 *
 * @param forceRefresh Force reload from disk or API.
 */
export async function getProfile(forceRefresh = false): Promise<ResumeProfile> {
  const localPath = await resolveLocalResumeFilePath();
  if (localPath) {
    return loadProfileFromLocalFile(localPath, forceRefresh);
  }

  throw new Error(
    "Base resume not configured. Set JOBOPS_LOCAL_RESUME_PATH or a local resume path in Settings.",
  );
}

/**
 * Get the person's name from the profile.
 */
export async function getPersonName(): Promise<string> {
  const profile = await getProfile();
  return profile?.basics?.name || "Resume";
}

/**
 * Clear the profile cache.
 */
export function clearProfileCache(): void {
  cachedLocalSourceKey = null;
  cachedLocalProfile = null;
}
