/**
 * Per-skill reputation scoring with time-based decay.
 *
 * Score formula: baseScore × decayFactor
 *   baseScore = (avgRating / 5) × 80 + (min(completedHours, 50) / 50) × 20
 *   decayFactor = e^(-λ × daysSinceLastRating)  where λ = 0.005
 *
 * Score range: 0-100. New members start at 50 (neutral).
 */

import type { Skill } from './schemas';
import type { ReputationDoc, ReputationEntry, ReputationRating } from './schemas-intent';

// ── Constants ──

/** Default score for members with no reputation */
export const DEFAULT_REPUTATION = 50;

/** Decay rate (per day). At λ=0.005, score halves after ~139 days of inactivity */
const DECAY_LAMBDA = 0.005;

/** Rating weight (out of 100) */
const RATING_WEIGHT = 80;

/** Hours weight (out of 100) */
const HOURS_WEIGHT = 20;

/** Hours cap for scoring (diminishing returns beyond this) */
const HOURS_CAP = 50;

const MS_PER_DAY = 86_400_000;

// ── Scoring ──

/**
 * Calculate the reputation score for a member-skill pair.
 */
export function calculateScore(entry: ReputationEntry, now: number = Date.now()): number {
	if (entry.ratings.length === 0) return DEFAULT_REPUTATION;

	// Average rating (1-5 scale)
	const avgRating = entry.ratings.reduce((sum, r) => sum + r.score, 0) / entry.ratings.length;

	// Base score: rating component + hours component
	const ratingComponent = (avgRating / 5) * RATING_WEIGHT;
	const hoursComponent = (Math.min(entry.completedHours, HOURS_CAP) / HOURS_CAP) * HOURS_WEIGHT;
	const baseScore = ratingComponent + hoursComponent;

	// Decay based on time since most recent rating
	const lastRating = entry.ratings.reduce((latest, r) => Math.max(latest, r.timestamp), 0);
	const daysSince = (now - lastRating) / MS_PER_DAY;
	const decayFactor = Math.exp(-DECAY_LAMBDA * Math.max(0, daysSince));

	return Math.round(baseScore * decayFactor);
}

/**
 * Get a member's reputation for a specific skill, or DEFAULT_REPUTATION if none.
 */
export function getMemberSkillReputation(
	memberId: string,
	skill: Skill,
	reputationDoc: ReputationDoc,
	now?: number,
): number {
	const key = `${memberId}:${skill}`;
	const entry = reputationDoc.entries[key];
	if (!entry) return DEFAULT_REPUTATION;
	return calculateScore(entry, now);
}

/**
 * Get a member's average reputation across all skills they have entries for.
 */
export function getMemberOverallReputation(
	memberId: string,
	reputationDoc: ReputationDoc,
	now?: number,
): number {
	const entries = Object.values(reputationDoc.entries)
		.filter(e => e.memberId === memberId);

	if (entries.length === 0) return DEFAULT_REPUTATION;

	const total = entries.reduce((sum, e) => sum + calculateScore(e, now), 0);
	return Math.round(total / entries.length);
}

/**
 * Build a reputation entry key from memberId and skill.
 */
export function reputationKey(memberId: string, skill: Skill): string {
	return `${memberId}:${skill}`;
}

/**
 * Create a new rating to add to a reputation entry.
 */
export function createRating(fromMemberId: string, score: number): ReputationRating {
	return {
		from: fromMemberId,
		score: Math.max(1, Math.min(5, Math.round(score))),
		timestamp: Date.now(),
	};
}
