import { Injectable, BadRequestException } from '@nestjs/common';
import { PrismaService } from '../../infrastructure/database/prisma.service';
import { SnapshotBuilderService } from '../ai-runtime/snapshot-builder.service';
import { enrichWithNames } from '../../common/helpers/name-resolver';

@Injectable()
export class AdminLearningService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly snapshotBuilder: SnapshotBuilderService,
  ) {}

  // ══ Name resolution ══

  private async enrich<T extends Record<string, any>>(items: T[]) {
    return enrichWithNames(this.prisma, items);
  }

  // ══ Knowledge Bases (for filter dropdown) ══

  async getKnowledgeBases() {
    const ids = await this.prisma.readingEvent.findMany({
      where: { knowledgeBaseId: { not: null } },
      select: { knowledgeBaseId: true },
      distinct: ['knowledgeBaseId'],
      take: 200,
    });
    const kbIds = [...new Set(ids.map(r => r.knowledgeBaseId!).filter(Boolean))];

    if (kbIds.length === 0) return [];

    const kbs = await this.prisma.knowledgeBase.findMany({
      where: { id: { in: kbIds } },
      select: { id: true, title: true },
      take: 200,
    });
    return kbs.map(kb => ({ id: kb.id, name: kb.title }));
  }

  // ══ Dashboard ══

  async getDashboard(filters?: { knowledgeBaseId?: string; startDate?: string; endDate?: string }) {
    const now = new Date();
    const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());

    const eventWhere: any = {};
    const sessionWhere: any = {};
    const progressWhere: any = {};
    const activityWhere: any = {};
    if (filters?.knowledgeBaseId) {
      eventWhere.knowledgeBaseId = filters.knowledgeBaseId;
      sessionWhere.knowledgeBaseId = filters.knowledgeBaseId;
      progressWhere.knowledgeBaseId = filters.knowledgeBaseId;
    }
    if (filters?.startDate || filters?.endDate) {
      eventWhere.serverReceivedAt = {};
      if (filters?.startDate) eventWhere.serverReceivedAt.gte = new Date(filters.startDate);
      if (filters?.endDate) eventWhere.serverReceivedAt.lte = new Date(filters.endDate);
    }

    const todayEventWhere = { ...eventWhere, serverReceivedAt: { ...(eventWhere.serverReceivedAt ?? {}), gte: today } };
    const todayActivityWhere = { ...activityWhere, activityDate: today };

    const [totalEvents, todayEvents, failedEvents, duplicateEvents,
      activeSessions, interruptedSessions, completedSessions, totalSessions,
      activeToday, totalRead, totalMarkedRead,
    ] = await Promise.all([
      this.prisma.readingEvent.count({ where: eventWhere }),
      this.prisma.readingEvent.count({ where: todayEventWhere }),
      this.prisma.readingEvent.count({ where: { ...eventWhere, status: 'failed' } }),
      this.prisma.readingEvent.count({ where: { ...eventWhere, status: 'duplicate' } }),
      this.prisma.learningSession.count({ where: { ...sessionWhere, status: 'active' } }),
      this.prisma.learningSession.count({ where: { ...sessionWhere, status: 'interrupted' } }),
      this.prisma.learningSession.count({ where: { ...sessionWhere, status: 'completed' } }),
      this.prisma.learningSession.count({ where: sessionWhere }),
      this.prisma.dailyLearningActivity.groupBy({ by: ['userId'], where: todayActivityWhere, _count: true }),
      this.prisma.materialReadingProgress.count({ where: { ...progressWhere, status: { not: 'not_started' } } }),
      this.prisma.materialReadingProgress.count({ where: { ...progressWhere, isMarkedRead: true } }),
    ]);

    const warningEvents = await this.prisma.readingEvent.count({ where: { ...eventWhere, status: 'warning' } });

    return {
      overview: { totalEvents, todayEvents, failedEvents, duplicateEvents, warningEvents, totalActiveSeconds: 0 },
      sessions: { active: activeSessions, interrupted: interruptedSessions, completed: completedSessions, total: totalSessions },
      users: { activeToday: activeToday.length, totalWithEvents: 0 },
      materials: { totalRead, totalMarkedRead },
      recentAnomalies: [],
    };
  }

  // ══ ReadingEvents ══

  async getReadingEvents(query: {
    userId?: string; materialId?: string; readingTargetType?: string;
    eventType?: string; status?: string; errorCode?: string;
    startDate?: string; endDate?: string;
    page?: number; limit?: number; sortBy?: string; order?: 'asc' | 'desc';
  }) {
    const where: any = {};
    if (query.userId) where.userId = query.userId;
    if (query.materialId) where.materialId = query.materialId;
    if (query.readingTargetType) where.readingTargetType = query.readingTargetType;
    if (query.eventType) where.eventType = query.eventType;
    if (query.status) where.status = query.status;
    if (query.errorCode) where.errorCode = query.errorCode;
    if (query.startDate || query.endDate) {
      where.serverReceivedAt = {};
      if (query.startDate) where.serverReceivedAt.gte = new Date(query.startDate);
      if (query.endDate) where.serverReceivedAt.lte = new Date(query.endDate);
    }

    const page = query.page ?? 1;
    const limit = Math.min(query.limit ?? 20, 100);
    const orderBy: any = {};
    orderBy[query.sortBy ?? 'serverReceivedAt'] = query.order ?? 'desc';

    const [items, total] = await Promise.all([
      this.prisma.readingEvent.findMany({ where, orderBy, skip: (page - 1) * limit, take: limit }),
      this.prisma.readingEvent.count({ where }),
    ]);
    const enriched = await this.enrich(items); return { items: enriched, total, page, limit };
  }

  async getReadingEvent(id: string) {
    const event = await this.prisma.readingEvent.findUnique({ where: { id } });
    if (!event) throw new BadRequestException('Event not found');
    return event;
  }

  // ══ Sessions ══

  async getSessions(query: { userId?: string; materialId?: string; status?: string; clientSessionId?: string; startDate?: string; endDate?: string; page?: number; limit?: number }) {
    const where: any = {};
    if (query.userId) where.userId = query.userId;
    if (query.materialId) where.materialId = query.materialId;
    if (query.status) where.status = query.status;
    if (query.clientSessionId) where.clientSessionId = query.clientSessionId;
    if (query.startDate) where.startedAt = { ...(where.startedAt ?? {}), gte: new Date(query.startDate) };
    if (query.endDate) where.startedAt = { ...(where.startedAt ?? {}), lte: new Date(query.endDate) };

    const page = query.page ?? 1;
    const limit = Math.min(query.limit ?? 20, 100);
    const [items, total] = await Promise.all([
      this.prisma.learningSession.findMany({ where, orderBy: { startedAt: 'desc' }, skip: (page - 1) * limit, take: limit }),
      this.prisma.learningSession.count({ where }),
    ]);
    const enriched = await this.enrich(items); return { items: enriched, total, page, limit };
  }

  async getSession(id: string) {
    const session = await this.prisma.learningSession.findUnique({ where: { id } });
    if (!session) throw new BadRequestException('Session not found');
    const [enriched] = await this.enrich([session]);
    return enriched;
  }

  async deleteSessions(ids: string[]) {
    if (!ids || ids.length === 0) throw new BadRequestException('请选择至少一个会话');
    const result = await this.prisma.learningSession.deleteMany({ where: { id: { in: ids } } });
    return { deleted: result.count, message: `成功删除 ${result.count} 个学习会话` };
  }

  // ══ Progress ══

  async getProgress(query: { userId?: string; materialId?: string; status?: string; page?: number; limit?: number }) {
    const where: any = {};
    if (query.userId) where.userId = query.userId;
    if (query.materialId) where.materialId = query.materialId;
    if (query.status) where.status = query.status;

    const page = query.page ?? 1;
    const limit = Math.min(query.limit ?? 20, 100);
    const [items, total] = await Promise.all([
      this.prisma.materialReadingProgress.findMany({ where, orderBy: { lastReadAt: 'desc' }, skip: (page - 1) * limit, take: limit }),
      this.prisma.materialReadingProgress.count({ where }),
    ]);
    const enriched = await this.enrich(items); return { items: enriched, total, page, limit };
  }

  async getProgressDetail(id: string) {
    const p = await this.prisma.materialReadingProgress.findUnique({ where: { id } });
    if (!p) throw new BadRequestException('Progress not found');
    return p;
  }

  // ══ DailyActivities ══

  async getDailyActivities(query: { userId?: string; startDate?: string; endDate?: string; page?: number; limit?: number }) {
    const where: any = {};
    if (query.userId) where.userId = query.userId;
    if (query.startDate) where.activityDate = { ...(where.activityDate ?? {}), gte: new Date(query.startDate) };
    if (query.endDate) where.activityDate = { ...(where.activityDate ?? {}), lte: new Date(query.endDate) };

    const page = query.page ?? 1;
    const limit = Math.min(query.limit ?? 20, 100);
    const [items, total] = await Promise.all([
      this.prisma.dailyLearningActivity.findMany({ where, orderBy: { activityDate: 'desc' }, skip: (page - 1) * limit, take: limit }),
      this.prisma.dailyLearningActivity.count({ where }),
    ]);
    const enriched = await this.enrich(items); return { items: enriched, total, page, limit };
  }

  // ══ Records ══

  async getRecords(query: { userId?: string; recordType?: string; startDate?: string; endDate?: string; page?: number; limit?: number }) {
    const where: any = {};
    if (query.userId) where.userId = query.userId;
    if (query.recordType) where.recordType = query.recordType;
    if (query.startDate) where.occurredAt = { ...(where.occurredAt ?? {}), gte: new Date(query.startDate) };
    if (query.endDate) where.occurredAt = { ...(where.occurredAt ?? {}), lte: new Date(query.endDate) };

    const page = query.page ?? 1;
    const limit = Math.min(query.limit ?? 20, 100);
    const [items, total] = await Promise.all([
      this.prisma.learningRecord.findMany({ where, orderBy: { occurredAt: 'desc' }, skip: (page - 1) * limit, take: limit }),
      this.prisma.learningRecord.count({ where }),
    ]);
    const enriched = await this.enrich(items); return { items: enriched, total, page, limit };
  }

  async getRecord(id: string) {
    const r = await this.prisma.learningRecord.findUnique({ where: { id } });
    if (!r) throw new BadRequestException('Record not found');
    return r;
  }

  // ══ AI Analysis ══

  async getAnalysis(query: { userId?: string; page?: number; limit?: number }) {
    const where: any = {};
    if (query.userId) where.userId = query.userId;
    const page = query.page ?? 1;
    const limit = Math.min(query.limit ?? 20, 100);
    const [items, total] = await Promise.all([
      this.prisma.aiAnalysisResult.findMany({
        where, orderBy: { createdAt: 'desc' }, skip: (page - 1) * limit, take: limit,
        select: { id: true, userId: true, jobId: true, summary: true, masteryScore: true, weaknesses: true, strengths: true, createdAt: true },
      }),
      this.prisma.aiAnalysisResult.count({ where }),
    ]);
    const enriched = await this.enrich(items); return { items: enriched, total, page, limit };
  }

  // ══ AI Usage ══

  async getAiUsage(query: { userId?: string; model?: string; page?: number; limit?: number }) {
    const where: any = {};
    if (query.userId) where.userId = query.userId;
    if (query.model) where.model = { contains: query.model };
    const page = query.page ?? 1;
    const limit = Math.min(query.limit ?? 20, 100);
    const [items, total] = await Promise.all([
      this.prisma.aiUsageLog.findMany({
        where, orderBy: { createdAt: 'desc' }, skip: (page - 1) * limit, take: limit,
        select: { id: true, userId: true, model: true, provider: true, inputTokens: true, outputTokens: true, estimatedCost: true, success: true, createdAt: true },
      }),
      this.prisma.aiUsageLog.count({ where }),
    ]);
    const enriched = await this.enrich(items); return { items: enriched, total, page, limit };
  }

  // ══ Timeline ══

  async getUserTimeline(userId: string, limit: number = 50) {
    const events = await this.prisma.readingEvent.findMany({
      where: { userId },
      orderBy: { clientTimestampMs: 'desc' },
      take: Math.min(limit, 200),
      select: { eventId: true, eventType: true, materialId: true, clientTimestampMs: true, status: true },
    });
    const sessions = await this.prisma.learningSession.findMany({
      where: { userId },
      orderBy: { startedAt: 'desc' },
      take: Math.min(limit, 50),
      select: { id: true, mode: true, status: true, startedAt: true, endedAt: true, totalActiveSeconds: true },
    });
    return { events, sessions };
  }

  // ══ Anomalies ══

  async getAnomalies(query: { page?: number; limit?: number }) {
    const page = query.page ?? 1;
    const limit = Math.min(query.limit ?? 20, 100);
    const skip = (page - 1) * limit;

    const [failed, duplicates, failedTotal, duplicatesTotal] = await Promise.all([
      this.prisma.readingEvent.findMany({ where: { status: 'failed' }, orderBy: { serverReceivedAt: 'desc' }, skip, take: limit }),
      this.prisma.readingEvent.findMany({ where: { status: 'duplicate' }, orderBy: { serverReceivedAt: 'desc' }, skip, take: limit }),
      this.prisma.readingEvent.count({ where: { status: 'failed' } }),
      this.prisma.readingEvent.count({ where: { status: 'duplicate' } }),
    ]);
    return { failed: { items: failed, total: failedTotal }, duplicates: { items: duplicates, total: duplicatesTotal }, page, limit };
  }

  // ══ Diagnose ══

  async diagnoseUser(userId: string) {
    const [events, sessions, progress, dailyActivity] = await Promise.all([
      this.prisma.readingEvent.count({ where: { userId } }),
      this.prisma.learningSession.findMany({ where: { userId }, orderBy: { startedAt: 'desc' }, take: 10, select: { id: true, status: true, startedAt: true, totalActiveSeconds: true } }),
      this.prisma.materialReadingProgress.findMany({ where: { userId }, orderBy: { lastReadAt: 'desc' }, take: 10, select: { materialId: true, status: true, totalActiveSeconds: true, isMarkedRead: true } }),
      this.prisma.dailyLearningActivity.findMany({ where: { userId }, orderBy: { activityDate: 'desc' }, take: 7 }),
    ]);
    return { userId, totalEvents: events, recentSessions: sessions, progress, dailyActivity };
  }

  async diagnoseMaterial(materialId: string) {
    const [events, sessions, progress] = await Promise.all([
      this.prisma.readingEvent.findMany({ where: { materialId }, orderBy: { clientTimestampMs: 'desc' }, take: 20 }),
      this.prisma.learningSession.findMany({ where: { materialId }, orderBy: { startedAt: 'desc' }, take: 10 }),
      this.prisma.materialReadingProgress.findFirst({ where: { materialId } }),
    ]);
    return { materialId, events, sessions, progress };
  }

  // ══ Temporary Materials ══

  async getTemporaryMaterials(query: { page?: number; limit?: number }) {
    const page = query.page ?? 1;
    const limit = Math.min(query.limit ?? 20, 100);
    const [items, total] = await Promise.all([
      this.prisma.temporaryReadingMaterial.findMany({ orderBy: { createdAt: 'desc' }, skip: (page - 1) * limit, take: limit }),
      this.prisma.temporaryReadingMaterial.count(),
    ]);
    const enriched = await this.enrich(items); return { items: enriched, total, page, limit };
  }

  // ══ Operations ══

  async reprocessEvent(id: string) {
    const event = await this.prisma.readingEvent.findUnique({ where: { id } });
    if (!event) throw new BadRequestException('Event not found');
    await this.prisma.readingEvent.update({ where: { id }, data: { status: 'pending', processedAt: null, errorCode: null } });
    return { id, status: 'pending' };
  }

  async batchReprocess(eventIds: string[]) {
    const result = await this.prisma.readingEvent.updateMany({
      where: { id: { in: eventIds }, status: { in: ['failed', 'duplicate'] } },
      data: { status: 'pending', processedAt: null, errorCode: null },
    });
    return { reprocessed: result.count };
  }

  async recalculateLearningData(userId?: string) {
    if (userId) {
      const snapshot = await this.snapshotBuilder.buildSnapshot(userId, 'user', userId);
      return { status: 'recalculated', userId, snapshotId: snapshot.id };
    }
    const users = await this.prisma.dailyLearningActivity.groupBy({ by: ['userId'], _count: true, take: 100, orderBy: { _count: { userId: 'desc' } } });
    const snapshots = await Promise.all(users.map(u =>
      this.snapshotBuilder.buildSnapshot(u.userId, 'user', u.userId).catch(() => null)
    ));
    return { status: 'recalculated', count: snapshots.filter(Boolean).length };
  }

  async export(query: { startDate?: string; endDate?: string; type?: string }) {
    const where: any = {};
    if (query.startDate) where.serverReceivedAt = { ...(where.serverReceivedAt ?? {}), gte: new Date(query.startDate) };
    if (query.endDate) where.serverReceivedAt = { ...(where.serverReceivedAt ?? {}), lte: new Date(query.endDate) };
    const total = await this.prisma.readingEvent.count({ where });
    const events = await this.prisma.readingEvent.findMany({ where, take: 10000 });
    return { exported: events.length, total, truncated: total > 10000, format: 'json', data: events };
  }
}
