import { Injectable } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../prisma/prisma.service';
import { ReportSnapshot } from '../../../domain/aggregates/report-snapshot';
import { IReportSnapshotRepository } from '../../../domain/repositories';
import { ReportType, ReportPeriod } from '../../../domain/value-objects';
import { ReportSnapshotMapper } from '../mappers/report-snapshot.mapper';

@Injectable()
export class ReportSnapshotRepository implements IReportSnapshotRepository {
  constructor(private readonly prisma: PrismaService) {}

  async save(snapshot: ReportSnapshot): Promise<ReportSnapshot> {
    const data = ReportSnapshotMapper.toPersistence(snapshot);

    if (snapshot.id) {
      const updated = await this.prisma.reportSnapshot.update({
        where: { id: snapshot.id },
        data: {
          snapshotData: data.snapshotData as Prisma.InputJsonValue,
          summaryData: data.summaryData as Prisma.InputJsonValue | undefined,
          dataSources: data.dataSources,
          dataFreshness: data.dataFreshness,
          filterParams: data.filterParams as Prisma.InputJsonValue | undefined,
          rowCount: data.rowCount,
          expiresAt: data.expiresAt,
        },
      });
      return ReportSnapshotMapper.toDomain(updated);
    }

    const created = await this.prisma.reportSnapshot.create({
      data: {
        reportType: data.reportType,
        reportCode: data.reportCode,
        reportPeriod: data.reportPeriod,
        periodKey: data.periodKey,
        snapshotData: data.snapshotData as Prisma.InputJsonValue,
        summaryData: data.summaryData as Prisma.InputJsonValue | undefined,
        dataSources: data.dataSources,
        dataFreshness: data.dataFreshness,
        filterParams: data.filterParams as Prisma.InputJsonValue | undefined,
        rowCount: data.rowCount,
        periodStartAt: data.periodStartAt,
        periodEndAt: data.periodEndAt,
        expiresAt: data.expiresAt,
      },
    });
    return ReportSnapshotMapper.toDomain(created);
  }

  async findById(id: bigint): Promise<ReportSnapshot | null> {
    const found = await this.prisma.reportSnapshot.findUnique({
      where: { id },
    });
    return found ? ReportSnapshotMapper.toDomain(found) : null;
  }

  async findByCodeAndPeriodKey(
    reportCode: string,
    periodKey: string,
  ): Promise<ReportSnapshot | null> {
    const found = await this.prisma.reportSnapshot.findUnique({
      where: {
        uk_report_period: {
          reportCode,
          periodKey,
        },
      },
    });
    return found ? ReportSnapshotMapper.toDomain(found) : null;
  }

  async findByType(type: ReportType): Promise<ReportSnapshot[]> {
    const found = await this.prisma.reportSnapshot.findMany({
      where: { reportType: type },
      orderBy: { generatedAt: 'desc' },
    });
    return found.map(ReportSnapshotMapper.toDomain);
  }

  async findByCode(reportCode: string): Promise<ReportSnapshot[]> {
    const found = await this.prisma.reportSnapshot.findMany({
      where: { reportCode },
      orderBy: { generatedAt: 'desc' },
    });
    return found.map(ReportSnapshotMapper.toDomain);
  }

  async findByPeriod(period: ReportPeriod): Promise<ReportSnapshot[]> {
    const found = await this.prisma.reportSnapshot.findMany({
      where: { reportPeriod: period },
      orderBy: { generatedAt: 'desc' },
    });
    return found.map(ReportSnapshotMapper.toDomain);
  }

  async findLatestByCode(reportCode: string): Promise<ReportSnapshot | null> {
    const found = await this.prisma.reportSnapshot.findFirst({
      where: { reportCode },
      orderBy: { generatedAt: 'desc' },
    });
    return found ? ReportSnapshotMapper.toDomain(found) : null;
  }

  async findExpired(): Promise<ReportSnapshot[]> {
    const found = await this.prisma.reportSnapshot.findMany({
      where: {
        expiresAt: {
          lt: new Date(),
        },
      },
    });
    return found.map(ReportSnapshotMapper.toDomain);
  }

  async deleteExpired(): Promise<number> {
    const result = await this.prisma.reportSnapshot.deleteMany({
      where: {
        expiresAt: {
          lt: new Date(),
        },
      },
    });
    return result.count;
  }

  async delete(id: bigint): Promise<void> {
    await this.prisma.reportSnapshot.delete({
      where: { id },
    });
  }
}
