import {Injectable, Logger} from '@nestjs/common';
import {CollectorOrchestratorService, OrchestratorResult} from "../collector/collector-orchestrator.service";
import {PrismaService} from "../../../prisma/prisma.service";
import {TrendWhereInput} from "../../generated/prisma/models/Trend";
import {TrendItem} from "../../common/interfaces/trend-item.interface";
import {TextUtil} from "../../common/utils/text.util";

@Injectable()
export class TrendsService {
    private readonly logger = new Logger(TrendsService.name);

    constructor(
        private prisma: PrismaService,
        private readonly orchestrator: CollectorOrchestratorService,
    ) {
    }

    /**
     * Lấy trends từ DB với filters
     */
    async getTrends(dto) {
        const {
            category,
            source,
            limit = 50,
            minScore = 0,
            q,
            maxAgeHours = 24,
        } = dto;

        const minDate = new Date(Date.now() - maxAgeHours * 60 * 60 * 1000);
        const qbWhere: TrendWhereInput = {};
        qbWhere.score = {gte: minScore};
        qbWhere.sourceTimestamp = {gte: minDate};

        if (category) {
            qbWhere.category = category;
        }
        if (source) {
            qbWhere.source = {
                contains: source,
                mode: 'insensitive', // Sẽ tìm thấy cả "Admin", "ADMIN", "adMin"...
            };
        }

        if (q) {
            qbWhere.title = {
                contains: q,
                mode: 'insensitive',
            }
        }

        const {data, total,} = await this.prisma.getManyAndCount(this.prisma.trend,
            {
                where: qbWhere,
                orderBy: [{score: 'desc'}, {sourceTimestamp: 'desc'}],
                take: limit,
            })

        return {
            data,
            total,
            filters: {category, source, limit, minScore, q, maxAgeHours},
        };
    }

    /**
     * Trigger collection thủ công + lưu vào DB
     */
    async collectAndStore(source?: string): Promise<OrchestratorResult> {
        console.debug('collectAndStore ==>: ', new Date().toISOString());
        let result: OrchestratorResult;

        if (source) {
            const collectorResult = await this.orchestrator.collectFromSource(source);
            result = {
                items: collectorResult.items,
                stats: {
                    totalRaw: collectorResult.items.length,
                    afterDedup: collectorResult.items.length,
                    bySource: {[source]: collectorResult.items.length},
                    errors: collectorResult.error ? [collectorResult.error] : [],
                    durationMs: collectorResult.durationMs,
                },
            };
        } else {
            result = await this.orchestrator.collectAll();
        }

        // Lưu vào DB
        await this.upsertTrends(result.items);

        return result;
    }

    /**
     * Upsert trends vào DB — update nếu đã tồn tại (by fingerprint)
     */
    async upsertTrends(items: TrendItem[]): Promise<number> {
        let upsertCount = 0;

        for (const item of items) {
            if (!item.title || item.title.trim().length === 0) continue;

            const fingerprint = TextUtil.fingerprint(item.title);

            try {
                // Check existing by fingerprint
                const existing = await this.prisma.trend.findFirst({
                    where: {fingerprint},
                });

                if (existing) {
                    // Update score nếu mới hơn hoặc cao hơn
                    if (item.score > existing.score) {
                        await this.prisma.trend.update({
                                data: {
                                    score: item.score,
                                    engagement: item.engagement as any,
                                    updatedAt: new Date(),
                                },
                                where: {id: existing.id}
                            }
                        );
                        upsertCount++;
                    }
                } else {
                    // Insert new
                    await this.prisma.trend.create({
                        data: {
                            title: item.title,
                            description: item.description || '',
                            url: item.url || '',
                            source: item.source,
                            score: item.score,
                            category: item.category || 'general',
                            tags: item.tags || [],
                            engagement: item.engagement || {},
                            raw: item.raw || {},
                            sourceTimestamp: item.timestamp || new Date(),
                            fingerprint,
                        }
                    });
                    upsertCount++;
                }
            } catch (error) {
                this.logger.warn(
                    `Failed to upsert trend "${item.title.substring(0, 50)}": ${error.message}`,
                );
            }
        }

        this.logger.log(`Upserted ${upsertCount}/${items.length} trends to DB`);

        return upsertCount;
    }

    /**
     * Thống kê nhanh
     */
    async getStats() {
        const total = await this.prisma.trend.count();

        const last24h = await this.prisma.trend.count({
            where: {
                sourceTimestamp: {gt: new Date(Date.now() - 24 * 60 * 60 * 1000)},
            },
        });

        const byCategory = await this.prisma.trend.groupBy({
            by: ['category'],
            _count: {
                _all: true,
            },
            _avg: {
                score: true, // Trung bình cộng
            },

        })
        // .createQueryBuilder('trend')
        // .select('trend.category', 'category')
        // .addSelect('COUNT(*)', 'count')
        // .addSelect('AVG(trend.score)', 'avgScore')
        // .groupBy('trend.category')
        // .getRawMany();

        const bySource = await this.prisma.$queryRaw`
            SELECT
                COUNT(1)::int as count ,
    CASE 
        WHEN STRPOS(source, ':') > 0 
        THEN SUBSTRING(source, 1, STRPOS(source, ':') - 1) 
        ELSE source
            END,
    source
FROM "Trend"
GROUP BY source;
            `
        ;
        console.log({bySource});

        const topTrends = await this.prisma.trend.findMany({
            where: {
                sourceTimestamp: {gt: new Date(Date.now() - 24 * 60 * 60 * 1000)}
            },
            orderBy: {score: 'desc'},
            take: 10,
            select: {
                id: true,
                title: true,
                score: true,
                category: true
            }
        });

        return {
            total,
            last24h,
            byCategory,
            bySource,
            topTrends,
        };
    }

    /**
     * Xóa trends cũ hơn N ngày
     */
    async cleanupOldTrends(olderThanDays: number = 7): Promise<number> {
        const cutoff = new Date(
            Date.now() - olderThanDays * 24 * 60 * 60 * 1000,
        );

        const {count} = await this.prisma.trend.deleteMany({
            where: {
                sourceTimestamp: {lt: cutoff},
            }
        })

        this.logger.log(`Cleaned up ${count} old trends`);
        return count;
    }
}
