import { Injectable } from '@nestjs/common';
import { PrismaService } from '../../infrastructure/database/prisma.service';

@Injectable()
export class LearningSessionRepository {
  constructor(private readonly prisma: PrismaService) {}

  async create(userId: string, dto: {
    knowledgeItemId?: string;
    knowledgeBaseId?: string;
    mode?: string;
  }) {
    return this.prisma.learningSession.create({
      data: {
        userId,
        knowledgeItemId: dto.knowledgeItemId ?? null,
        knowledgeBaseId: dto.knowledgeBaseId ?? null,
        mode: dto.mode ?? 'reading',
        status: 'active',
        startedAt: new Date(),
      },
    });
  }

  async end(id: string) {
    const session = await this.prisma.learningSession.findUnique({ where: { id } });
    if (!session) return undefined;

    return this.prisma.learningSession.update({
      where: { id },
      data: {
        status: 'completed',
        endedAt: new Date(),
        durationSeconds: Math.floor(
          (Date.now() - session.startedAt.getTime()) / 1000,
        ),
      },
    });
  }

  async findByUserId(
    userId: string,
    opts?: { page?: number; limit?: number; status?: string; sort?: string },
  ) {
    const page = opts?.page ?? 1;
    const limit = opts?.limit ?? 20;
    const where: any = { userId };
    if (opts?.status) where.status = opts.status;

    let orderBy: any = { startedAt: 'desc' };
    if (opts?.sort) {
      const [field, dir] = opts.sort.split(':');
      if (['startedAt', 'durationSeconds', 'endedAt'].includes(field)) {
        orderBy = { [field]: dir === 'asc' ? 'asc' : 'desc' };
      }
    }

    return this.prisma.learningSession.findMany({
      where,
      orderBy,
      skip: (page - 1) * limit,
      take: limit,
    });
  }

  /** M8: Upsert session from reading event aggregation. */
  async upsertFromReadingEvent(tx: any, data: {
    userId: string;
    clientSessionId: string;
    materialId: string;
    readingTargetType: string;
    knowledgeBaseId: string | null;
    eventType: string;
    activeSecondsDelta: number;
    position: any | null;
    timestampMs: bigint;
  }) {
    const existing = await tx.learningSession.findFirst({
      where: { clientSessionId: data.clientSessionId },
    });

    if (existing) {
      // Guard: don't update completed/interrupted sessions (defense against out-of-order events)
      if (existing.status === 'completed' || existing.status === 'interrupted') {
        return existing;
      }

      const update: any = {
        lastEventAt: new Date(Number(data.timestampMs)),
      };
      if (data.activeSecondsDelta > 0) {
        update.totalActiveSeconds = { increment: data.activeSecondsDelta };
      }
      if (data.position) {
        update.lastPosition = data.position;
      }
      if (data.eventType === 'material_closed') {
        update.status = 'completed';
        update.endedAt = new Date(Number(data.timestampMs));
      }

      return tx.learningSession.update({ where: { id: existing.id }, data: update });
    }

    return tx.learningSession.create({
      data: {
        userId: data.userId,
        clientSessionId: data.clientSessionId,
        materialId: data.materialId,
        readingTargetType: data.readingTargetType,
        knowledgeBaseId: data.knowledgeBaseId,
        mode: 'reading',
        status: 'active',
        totalActiveSeconds: data.activeSecondsDelta,
        lastPosition: data.position ?? undefined,
        lastEventAt: new Date(Number(data.timestampMs)),
        startedAt: new Date(Number(data.timestampMs)),
      },
    });
  }
}
