import type { Config } from '../config/schema.js';

export type SessionResetMode = 'daily' | 'idle';
export type SessionResetType = 'direct' | 'group' | 'thread';

export type SessionResetConfig = NonNullable<Config['session']>['reset'];
export type SessionResetPolicy = {
  mode: SessionResetMode;
  atHour: number;
  idleMinutes?: number;
  configured?: boolean;
};

export type SessionFreshness = {
  fresh: boolean;
  dailyResetAt?: number;
  idleExpiresAt?: number;
};

export const DEFAULT_RESET_MODE: SessionResetMode = 'daily';
export const DEFAULT_RESET_AT_HOUR = 4;
/** Default idle window when mode is `idle` and no explicit minutes are set. */
export const DEFAULT_IDLE_MINUTES = 0;

export function resolveDailyResetAtMs(now: number, atHour: number): number {
  const normalizedAtHour = normalizeResetAtHour(atHour);
  const resetAt = new Date(now);
  resetAt.setHours(normalizedAtHour, 0, 0, 0);
  if (now < resetAt.getTime()) {
    resetAt.setDate(resetAt.getDate() - 1);
  }
  return resetAt.getTime();
}

export function resolveSessionResetPolicy(params: {
  sessionCfg?: Config['session'];
  resetType: SessionResetType;
  resetOverride?: SessionResetConfig;
}): SessionResetPolicy {
  const sessionCfg = params.sessionCfg;
  const baseReset = params.resetOverride ?? sessionCfg?.reset;
  const typeReset = params.resetOverride ? undefined : sessionCfg?.resetByType?.[params.resetType];
  const hasExplicitReset = Boolean(baseReset || sessionCfg?.resetByType);
  const legacyIdleMinutes = params.resetOverride ? undefined : sessionCfg?.idleMinutes;
  const configured = Boolean(baseReset || typeReset || legacyIdleMinutes != null);
  const mode =
    typeReset?.mode ??
    baseReset?.mode ??
    (!hasExplicitReset && legacyIdleMinutes != null ? 'idle' : DEFAULT_RESET_MODE);
  const atHour = normalizeResetAtHour(typeReset?.atHour ?? baseReset?.atHour ?? DEFAULT_RESET_AT_HOUR);
  const idleMinutesRaw = typeReset?.idleMinutes ?? baseReset?.idleMinutes ?? legacyIdleMinutes;

  let idleMinutes: number | undefined;
  if (idleMinutesRaw != null) {
    const normalized = Math.floor(idleMinutesRaw);
    if (Number.isFinite(normalized)) {
      idleMinutes = Math.max(normalized, 0);
    }
  } else if (mode === 'idle') {
    idleMinutes = DEFAULT_IDLE_MINUTES;
  }

  return { mode, atHour, idleMinutes, configured };
}

export function evaluateSessionFreshness(params: {
  updatedAt: number;
  sessionStartedAt?: number;
  lastInteractionAt?: number;
  now: number;
  policy: SessionResetPolicy;
}): SessionFreshness {
  const updatedAt = resolveTimestamp(params.updatedAt, params.now) ?? 0;
  const sessionStartedAt = resolveTimestamp(params.sessionStartedAt, params.now) ?? updatedAt;
  const lastInteractionAt =
    resolveTimestamp(params.lastInteractionAt, params.now) ?? sessionStartedAt;
  const dailyResetAt =
    params.policy.mode === 'daily'
      ? resolveDailyResetAtMs(params.now, params.policy.atHour)
      : undefined;
  const idleExpiresAt =
    params.policy.idleMinutes != null && params.policy.idleMinutes > 0
      ? lastInteractionAt + params.policy.idleMinutes * 60_000
      : undefined;
  const staleDaily = dailyResetAt != null && sessionStartedAt < dailyResetAt;
  const staleIdle = idleExpiresAt != null && params.now > idleExpiresAt;
  return {
    fresh: !(staleDaily || staleIdle),
    dailyResetAt,
    idleExpiresAt,
  };
}

function resolveTimestamp(value: number | undefined, now?: number): number | undefined {
  if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {
    return undefined;
  }
  if (typeof now === 'number' && Number.isFinite(now) && value > now) {
    return undefined;
  }
  return value;
}

function normalizeResetAtHour(value: number | undefined): number {
  if (typeof value !== 'number' || !Number.isFinite(value)) {
    return DEFAULT_RESET_AT_HOUR;
  }
  const normalized = Math.floor(value);
  if (!Number.isFinite(normalized)) {
    return DEFAULT_RESET_AT_HOUR;
  }
  if (normalized < 0) {
    return 0;
  }
  if (normalized > 23) {
    return 23;
  }
  return normalized;
}
