import fs from "node:fs";
import os from "node:os";
import path from "node:path";

import JSON5 from "json5";
import type { MsgContext } from "../auto-reply/templating.js";
import { CONFIG_DIR, normalizeE164 } from "../utils.js";

export type SessionScope = "per-sender" | "global";

export type SessionEntry = {
  sessionId: string;
  updatedAt: number;
  systemSent?: boolean;
  abortedLastRun?: boolean;
  thinkingLevel?: string;
  verboseLevel?: string;
  inputTokens?: number;
  outputTokens?: number;
  totalTokens?: number;
  model?: string;
  contextTokens?: number;
  // Optional flag to mirror Mac app UI and future sync states.
  syncing?: boolean | string;
};

export const SESSION_STORE_DEFAULT = path.join(
  CONFIG_DIR,
  "sessions",
  "sessions.json",
);
export const DEFAULT_RESET_TRIGGER = "/new";
export const DEFAULT_IDLE_MINUTES = 60;

export function resolveStorePath(store?: string) {
  if (!store) return SESSION_STORE_DEFAULT;
  if (store.startsWith("~"))
    return path.resolve(store.replace("~", os.homedir()));
  return path.resolve(store);
}

export function loadSessionStore(
  storePath: string,
): Record<string, SessionEntry> {
  try {
    const raw = fs.readFileSync(storePath, "utf-8");
    const parsed = JSON5.parse(raw);
    if (parsed && typeof parsed === "object") {
      return parsed as Record<string, SessionEntry>;
    }
  } catch {
    // ignore missing/invalid store; we'll recreate it
  }
  return {};
}

export async function saveSessionStore(
  storePath: string,
  store: Record<string, SessionEntry>,
) {
  await fs.promises.mkdir(path.dirname(storePath), { recursive: true });
  await fs.promises.writeFile(
    storePath,
    JSON.stringify(store, null, 2),
    "utf-8",
  );
}

// Decide which session bucket to use (per-sender vs global).
export function deriveSessionKey(scope: SessionScope, ctx: MsgContext) {
  if (scope === "global") return "global";
  const from = ctx.From ? normalizeE164(ctx.From) : "";
  // Preserve group conversations as distinct buckets
  if (typeof ctx.From === "string" && ctx.From.includes("@g.us")) {
    return `group:${ctx.From}`;
  }
  if (typeof ctx.From === "string" && ctx.From.startsWith("group:")) {
    return ctx.From;
  }
  return from || "unknown";
}

/**
 * Resolve the session key with a canonical direct-chat bucket (default: "main").
 * All non-group direct chats collapse to this bucket; groups stay isolated.
 */
export function resolveSessionKey(
  scope: SessionScope,
  ctx: MsgContext,
  mainKey?: string,
) {
  const raw = deriveSessionKey(scope, ctx);
  if (scope === "global") return raw;
  // Default to a single shared direct-chat session called "main"; groups stay isolated.
  const canonical = (mainKey ?? "main").trim() || "main";
  const isGroup = raw.startsWith("group:") || raw.includes("@g.us");
  if (!isGroup) return canonical;
  return raw;
}
