import { Injectable, ForbiddenException, NotFoundException } from '@nestjs/common';
import { PrismaService } from '@chatapp/database';

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

  private async requireAdmin(wsId: string, userId: string) {
    const member = await this.prisma.workspaceMember.findUnique({
      where: { workspaceId_userId: { workspaceId: wsId, userId } },
    });
    if (!member || (member.role !== 'admin' && member.role !== 'owner')) {
      throw new ForbiddenException('Admin access required');
    }
    return member;
  }

  async list(wsId: string, userId: string) {
    await this.requireAdmin(wsId, userId);

    const policies = await this.prisma.dataRetentionPolicy.findMany({
      where: { workspaceId: wsId },
      orderBy: { createdAt: 'desc' },
      include: { channel: { select: { id: true, name: true, handle: true } } },
    });

    return {
      policies: policies.map(p => ({
        id: p.id,
        scope: p.scope,
        channel: p.channel
          ? { id: p.channel.id, name: p.channel.name, handle: p.channel.handle }
          : null,
        message_retention_days: p.messageRetentionDays,
        file_retention_days: p.fileRetentionDays,
        retain_threads: p.retainThreads,
        created_at: p.createdAt,
        updated_at: p.updatedAt,
      })),
    };
  }

  async create(wsId: string, userId: string, dto: any) {
    await this.requireAdmin(wsId, userId);

    const policy = await this.prisma.dataRetentionPolicy.create({
      data: {
        workspaceId: wsId,
        scope: dto.scope || 'workspace',
        channelId: dto.channel_id || null,
        messageRetentionDays: dto.message_retention_days ?? null,
        fileRetentionDays: dto.file_retention_days ?? null,
        retainThreads: dto.retain_threads ?? true,
      },
      include: { channel: { select: { id: true, name: true, handle: true } } },
    });

    return {
      id: policy.id,
      scope: policy.scope,
      channel: policy.channel,
      message_retention_days: policy.messageRetentionDays,
      file_retention_days: policy.fileRetentionDays,
      retain_threads: policy.retainThreads,
      created_at: policy.createdAt,
      updated_at: policy.updatedAt,
    };
  }

  async update(id: string, wsId: string, userId: string, dto: any) {
    await this.requireAdmin(wsId, userId);

    const policy = await this.prisma.dataRetentionPolicy.findFirst({
      where: { id, workspaceId: wsId },
    });
    if (!policy) throw new NotFoundException('Retention policy not found');

    const data: any = {};
    if (dto.scope !== undefined) data.scope = dto.scope;
    if (dto.channel_id !== undefined) data.channelId = dto.channel_id || null;
    if (dto.message_retention_days !== undefined) data.messageRetentionDays = dto.message_retention_days;
    if (dto.file_retention_days !== undefined) data.fileRetentionDays = dto.file_retention_days;
    if (dto.retain_threads !== undefined) data.retainThreads = dto.retain_threads;
    data.updatedAt = new Date();

    return this.prisma.dataRetentionPolicy.update({ where: { id }, data });
  }

  async delete(id: string, wsId: string, userId: string) {
    await this.requireAdmin(wsId, userId);

    const policy = await this.prisma.dataRetentionPolicy.findFirst({
      where: { id, workspaceId: wsId },
    });
    if (!policy) throw new NotFoundException('Retention policy not found');

    await this.prisma.dataRetentionPolicy.delete({ where: { id } });
    return { deleted: true };
  }

  /**
   * Devuelve mensajes que superaron el periodo de retención para ser eliminados.
   */
  async getExpiredMessages(wsId: string) {
    const policies = await this.prisma.dataRetentionPolicy.findMany({
      where: { workspaceId: wsId },
    });

    const workspacePolicy = policies.find(p => p.scope === 'workspace' && !p.channelId);
    const channelPolicies = policies.filter(p => p.channelId);

    const now = new Date();
    const expired: { channelId: string; cutoff: Date }[] = [];

    if (workspacePolicy?.messageRetentionDays) {
      const cutoff = new Date(now);
      cutoff.setDate(cutoff.getDate() - workspacePolicy.messageRetentionDays);
      const channels = await this.prisma.channel.findMany({
        where: { workspaceId: wsId },
        select: { id: true },
      });
      for (const ch of channels) {
        // Check if channel has a more specific policy
        const channelPolicy = channelPolicies.find(p => p.channelId === ch.id);
        if (channelPolicy?.messageRetentionDays) {
          const channelCutoff = new Date(now);
          channelCutoff.setDate(channelCutoff.getDate() - channelPolicy.messageRetentionDays);
          expired.push({ channelId: ch.id, cutoff: channelCutoff });
        } else {
          expired.push({ channelId: ch.id, cutoff });
        }
      }
    } else {
      for (const cp of channelPolicies) {
        if (cp.messageRetentionDays) {
          const cutoff = new Date(now);
          cutoff.setDate(cutoff.getDate() - cp.messageRetentionDays);
          expired.push({ channelId: cp.channelId!, cutoff });
        }
      }
    }

    return expired;
  }
}