import { CloudObject, CloudQuery } from "./ncloud";

export class RecommendationService {
    private perferQuery: CloudQuery; // 偏好查询对象

    constructor() {
        this.perferQuery = new CloudQuery('Perfer'); // 偏好数据存储在 perfer 类中
    }

    /**
     * 获取用户的物品推荐
     * @param userId 用户ID
     * @param howMany 推荐数量
     * @returns 推荐的物品ID列表
     */
    public async getItemRecommendations(userId: string, howMany: number): Promise<string[]> {
        // 获取用户的偏好数据
        const userPreferences = await this.getUserPreferences(userId);
        
        if (!userPreferences || userPreferences.length === 0) {
            return [];
        }

        // 计算物品相似度
        //console.log(userPreferences);
        const similarItems = await this.calculateItemSimilarity(userPreferences);
        // 获取推荐物品
        return this.getTopRecommendations(similarItems, howMany);
    }

    /**
     * 获取用户的偏好数据
     * @param userId 用户ID
     * @returns 用户偏好物品的列表
     */
    private async getUserPreferences(userId: string): Promise<CloudObject[]> {
        this.perferQuery.equalTo('user_id', userId); // 查询条件
        const preferences = await this.perferQuery.find(); // 获取用户的偏好数据
        console.log(preferences);
        return preferences; // 返回用户偏好物品列表
    }

    /**
     * 计算物品相似度
     * @param userPreferences 用户偏好物品
     * @returns 物品相似度的映射
     */
    private async calculateItemSimilarity(userPreferences: CloudObject[]): Promise<Map<string, number>> {
        const itemSimilarity: Map<string, number> = new Map();
        // 遍历用户偏好物品，计算与其他物品的相似度
        for (const pref of userPreferences) {
            
            const itemId = pref.get('item_id');
            const preferenceValue = pref.get('perference');
            //console.log(`${itemId} : ${preferenceValue}`);

            // 获取所有物品的偏好数据
            const allPreferences = await this.perferQuery.find();
            for (const otherPref of allPreferences) {
                const otherItemId = otherPref.get('item_id');
                const otherPreferenceValue = otherPref.get('perference');
                //console.log(`${otherItemId} : ${otherPreferenceValue}`);

                if (itemId !== otherItemId) { // 排除自身
                    console.log(`${itemId} : ${preferenceValue}`)
                    console.log(`${otherItemId} : ${otherPreferenceValue}`);
                    const similarityScore = this.computeSimilarity(preferenceValue, otherPreferenceValue);
                    console.log(`${itemId} : ${similarityScore}`);
                    itemSimilarity.set(otherItemId, (itemSimilarity.get(otherItemId) || 0) + similarityScore);
                }
            }
        }

        return itemSimilarity;
    }

    /**
     * 计算两个物品之间的余弦相似度
     * @param preference1 第一个物品的偏好值
     * @param preference2 第二个物品的偏好值
     * @returns 相似度分数
     */
    private computeSimilarity(preference1: number, preference2: number): number {
        // 计算点积
        const dotProduct = preference1 * preference2;

        // 计算模
        const magnitude1 = Math.sqrt(preference1 ** 2);
        const magnitude2 = Math.sqrt(preference2 ** 2);

        // 计算余弦相似度
        if (magnitude1 === 0 || magnitude2 === 0) {
            return 0; // 避免除以零的情况
        }

        return dotProduct / (magnitude1 * magnitude2);
    }

    /**
     * 获取前 N 个推荐物品
     * @param similarItems 物品相似度映射
     * @param howMany 推荐数量
     * @returns 推荐物品ID列表
     */
    private getTopRecommendations(similarItems: Map<string, number>, howMany: number): string[] {
        // 将相似度映射转换为数组并排序
        const sortedItems = Array.from(similarItems.entries()).sort((a, b) => b[1] - a[1]);
        // 获取前 N 个物品ID
        return sortedItems.slice(0, howMany).map(item => item[0]);
    }
}

