/**
 * rCrowdSurf Automerge document schemas.
 *
 * Stores collaborative activity proposals ("prompts") with swipe-based
 * commitment tracking, contribution tagging, and threshold triggers.
 *
 * DocId format: {space}:crowdsurf:prompts
 */

import type { DocSchema } from '../../shared/local-first/document';

// ── Contribution types ──

export type ContributionCategory = 'skill' | 'space' | 'equipment' | 'food' | 'other';

export interface Contribution {
	bringing: string[];
	needed: string[];
	tags: string[];
	value: number;
}

// ── Swipe record ──

export interface PromptSwipe {
	direction: 'right' | 'left';
	timestamp: number;
	contribution?: Contribution;
}

// ── Activity prompt ──

export interface CrowdSurfPrompt {
	id: string;
	text: string;
	location: string;
	/** Number of right-swipes needed to trigger */
	threshold: number;
	/** Hours until prompt expires */
	duration: number;
	/** Human-readable activity duration (e.g. "1 hour", "all day") */
	activityDuration: string;
	createdAt: number;
	createdBy: string | null;
	triggered: boolean;
	expired: boolean;
	/** Keyed by participant DID */
	swipes: Record<string, PromptSwipe>;
	/** Elo rating for pairwise ranking (sortition) */
	elo: number;
	/** Number of pairwise comparisons this prompt appeared in */
	comparisons: number;
	/** Number of pairwise wins */
	wins: number;
}

// ── Document root ──

export interface CrowdSurfDoc {
	meta: {
		module: string;
		collection: string;
		version: number;
		spaceSlug: string;
		createdAt: number;
	};
	prompts: Record<string, CrowdSurfPrompt>;
}

// ── Schema registration ──

export const crowdsurfSchema: DocSchema<CrowdSurfDoc> = {
	module: 'crowdsurf',
	collection: 'prompts',
	version: 1,
	init: (): CrowdSurfDoc => ({
		meta: {
			module: 'crowdsurf',
			collection: 'prompts',
			version: 1,
			spaceSlug: '',
			createdAt: Date.now(),
		},
		prompts: {},
	}),
	migrate: (doc: any, _fromVersion: number) => {
		if (!doc.prompts) doc.prompts = {};
		// Backfill Elo defaults on existing prompts
		for (const p of Object.values(doc.prompts) as any[]) {
			if (p.elo === undefined) { p.elo = ELO_DEFAULT; p.comparisons = 0; p.wins = 0; }
		}
		doc.meta.version = 1;
		return doc;
	},
};

// ── Elo constants ──

export const ELO_K = 32;
export const ELO_DEFAULT = 1500;

export function computeElo(winnerElo: number, loserElo: number): { winner: number; loser: number } {
	const expected = 1 / (1 + Math.pow(10, (loserElo - winnerElo) / 400));
	const delta = Math.round(ELO_K * (1 - expected));
	return { winner: winnerElo + delta, loser: loserElo - delta };
}

// ── Helpers ──

export function crowdsurfDocId(space: string) {
	return `${space}:crowdsurf:prompts` as const;
}

/** Calculate decay progress (0-1) based on creation time and duration */
export function getDecayProgress(prompt: CrowdSurfPrompt): number {
	const age = Date.now() - prompt.createdAt;
	const durationMs = prompt.duration * 60 * 60 * 1000;
	return Math.min(age / durationMs, 1);
}

/** Get human-readable time remaining */
export function getTimeRemaining(prompt: CrowdSurfPrompt): string {
	const remaining = prompt.duration * 60 * 60 * 1000 - (Date.now() - prompt.createdAt);
	if (remaining <= 0) return 'Expired';
	const hours = Math.floor(remaining / (60 * 60 * 1000));
	const minutes = Math.floor((remaining % (60 * 60 * 1000)) / (60 * 1000));
	if (hours > 0) return `${hours}h ${minutes}m left`;
	return `${minutes}m left`;
}

/** Count right-swipes */
export function getRightSwipeCount(prompt: CrowdSurfPrompt): number {
	return Object.values(prompt.swipes).filter(s => s.direction === 'right').length;
}

/** Check if prompt has met its threshold */
export function isReadyToTrigger(prompt: CrowdSurfPrompt): boolean {
	return getRightSwipeCount(prompt) >= prompt.threshold;
}

/** Get urgency level based on time decay */
export function getUrgency(prompt: CrowdSurfPrompt): 'low' | 'medium' | 'high' {
	const decay = getDecayProgress(prompt);
	if (decay > 0.7) return 'high';
	if (decay > 0.4) return 'medium';
	return 'low';
}

/** Parse free-text contribution input into tags and categories */
export function parseContributions(bringing: string, needed: string): Contribution {
	const parseItems = (text: string): string[] =>
		text.split(/[,\n]/).map(s => s.trim()).filter(s => s.length > 0);

	const bringingItems = parseItems(bringing);
	const neededItems = parseItems(needed);
	const allItems = [...bringingItems, ...neededItems];
	const tags = new Set<string>();

	const categoryKeywords: Record<string, string[]> = {
		food: ['cook', 'food', 'eat', 'meal', 'kitchen', 'bake', 'grill', 'ingredients'],
		music: ['music', 'guitar', 'drum', 'sing', 'band', 'dj', 'speaker', 'mic'],
		learning: ['teach', 'learn', 'skill', 'knowledge', 'workshop', 'lecture'],
		tech: ['code', 'laptop', 'hack', 'build', 'dev', 'tech', 'wifi'],
		art: ['art', 'paint', 'draw', 'craft', 'design', 'photo', 'camera'],
	};

	for (const item of allItems) {
		const lower = item.toLowerCase();
		for (const [category, keywords] of Object.entries(categoryKeywords)) {
			if (keywords.some(kw => lower.includes(kw))) {
				tags.add(category);
			}
		}
	}

	// Base value: 5 per item brought, 2 per item needed, +5 bonus for skill keywords
	const skillWords = ['skill', 'experience', 'professional', 'advanced', 'expert'];
	const value = bringingItems.reduce((sum, item) => {
		const hasSkill = skillWords.some(sw => item.toLowerCase().includes(sw));
		return sum + (hasSkill ? 10 : 5);
	}, 0) + neededItems.length * 2;

	return {
		bringing: bringingItems,
		needed: neededItems,
		tags: Array.from(tags),
		value,
	};
}
