import { UserSession } from '#models/UserSession';
import RedisService from '#services/storage/redisService';
import Logger from '#utils/logger';

/**
 * Device Trust Score Service
 *
 * Calculates and maintains trust scores for devices based on multiple factors:
 * - Device age
 * - Success/failure login history
 * - Network consistency (ASN, geo region)
 * - Biometric verification status
 */

export interface DeviceTrustScore {
	deviceId: string;
	userId: string;
	score: number; // 0-100
	factors: {
		age: number; // Device age in days
		successLogins: number;
		failedLogins: number;
		consistentASN: boolean;
		consistentGeoRegion: boolean;
		biometricVerified: boolean;
	};
	lastUpdated: Date;
}

export interface DeviceTrustFactors {
	deviceId: string;
	userId: string;
	currentASN?: string;
	currentGeoRegion?: string;
	biometricVerified?: boolean;
}

const TRUST_CACHE_TTL = 3600; // 1 hour
const TRUST_CACHE_KEY = (userId: string, deviceId: string) => `trust:${userId}:${deviceId}`;

export class DeviceTrustService {
	private static redis = RedisService.getInstance();

	/**
	 * Calculate trust score for a device
	 */
	static async calculateTrustScore(factors: DeviceTrustFactors): Promise<DeviceTrustScore> {
		const {
			deviceId,
			userId,
			currentASN: _currentASN,
			currentGeoRegion: _currentGeoRegion,
			biometricVerified,
		} = factors;

		// Get device history from sessions
		const sessions = await UserSession.findAll({
			where: {
				user_id: userId,
				device_id: deviceId,
			},
			order: [['created_at', 'DESC']],
			limit: 50,
		});

		if (sessions.length === 0) {
			// New device - base score
			return {
				deviceId,
				userId,
				score: 50,
				factors: {
					age: 0,
					successLogins: 0,
					failedLogins: 0,
					consistentASN: true,
					consistentGeoRegion: true,
					biometricVerified: biometricVerified || false,
				},
				lastUpdated: new Date(),
			};
		}

		// Calculate factors
		const firstSession = sessions[sessions.length - 1];
		if (!firstSession) {
			// Should not happen, but handle gracefully
			return {
				deviceId,
				userId,
				score: 50,
				factors: {
					age: 0,
					successLogins: 0,
					failedLogins: 0,
					consistentASN: true,
					consistentGeoRegion: true,
					biometricVerified: biometricVerified || false,
				},
				lastUpdated: new Date(),
			};
		}

		const deviceAge = Math.floor(
			(Date.now() - (firstSession.created_at?.getTime() || Date.now())) / (1000 * 60 * 60 * 24),
		);
		const successLogins = sessions.filter((s) => s.status === 'active').length;
		const failedLogins = sessions.length - successLogins;

		// Network consistency
		const asns = sessions.map((s) => s.ip_address).filter(Boolean);
		const uniqueASNs = new Set(asns);
		const consistentASN = uniqueASNs.size <= 2; // Allow 1-2 different ASNs (carrier changes)

		// Geo region consistency (simplified - would need IP geolocation service)
		const consistentGeoRegion = true; // Placeholder - implement with actual geo lookup

		// Calculate score
		let score = 50; // Base score

		// Device age bonus
		if (deviceAge > 30) score += 20;
		else if (deviceAge > 7) score += 10;
		else if (deviceAge > 1) score += 5;

		// Success login history bonus
		score += Math.min(successLogins * 2, 20);

		// Failed login penalty
		score -= failedLogins * 5;

		// Network consistency bonus
		if (consistentASN) score += 10;
		if (consistentGeoRegion) score += 10;

		// Biometric verification bonus
		if (biometricVerified) score += 15;

		// Clamp to 0-100
		score = Math.max(0, Math.min(100, score));

		const trustScore: DeviceTrustScore = {
			deviceId,
			userId,
			score,
			factors: {
				age: deviceAge,
				successLogins,
				failedLogins,
				consistentASN,
				consistentGeoRegion,
				biometricVerified: biometricVerified || false,
			},
			lastUpdated: new Date(),
		};

		// Cache trust score (fire-and-forget)
		await this.redis.set(TRUST_CACHE_KEY(userId, deviceId), trustScore, TRUST_CACHE_TTL).catch((error) => {
			Logger.error('Failed to cache device trust score', error);
		});

		return trustScore;
	}

	/**
	 * Get cached trust score
	 */
	static async getCachedTrustScore(userId: string, deviceId: string): Promise<DeviceTrustScore | null> {
		return this.redis.get<DeviceTrustScore>(TRUST_CACHE_KEY(userId, deviceId));
	}

	/**
	 * Invalidate trust score cache
	 */
	static async invalidateTrustScore(userId: string, deviceId: string): Promise<void> {
		// Invalidate cache (fire-and-forget)
		await this.redis.delete(TRUST_CACHE_KEY(userId, deviceId)).catch((error) => {
			Logger.error('Failed to delete device trust score cache', error);
		});
	}

	/**
	 * Get trust level based on score
	 */
	static getTrustLevel(score: number): 'trusted' | 'medium' | 'low' {
		if (score >= 80) return 'trusted';
		if (score >= 50) return 'medium';
		return 'low';
	}

	/**
	 * Check if additional verification is required based on trust level
	 */
	static requiresAdditionalVerification(score: number): boolean {
		return score < 50;
	}

	/**
	 * Update trust score after successful login
	 */
	static async recordSuccessfulLogin(userId: string, deviceId: string): Promise<void> {
		await this.invalidateTrustScore(userId, deviceId);
	}

	/**
	 * Update trust score after failed login
	 */
	static async recordFailedLogin(userId: string, deviceId: string): Promise<void> {
		await this.invalidateTrustScore(userId, deviceId);
	}
}
