import { Injectable } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { AuthenticatedUser } from '../auth/auth.types';
import { JobsService } from '../jobs/jobs.service';
import { PrismaService } from '../prisma/prisma.service';
import { WebhooksService } from '../webhooks/webhooks.service';
import { CreateAuditLogDto } from './dto/create-audit-log.dto';

@Injectable()
export class LogsService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly jobsService: JobsService,
    private readonly webhooksService: WebhooksService,
  ) {}

  getWebhookLogs(limit?: number, provider?: string, status?: string) {
    return this.webhooksService.findAll(limit, provider, status);
  }

  getJobLogs(limit?: number, queueName?: string, status?: string) {
    return this.jobsService.findAll(limit, queueName, status);
  }

  getAuditTrail(
    page = 1,
    limit = 100,
    range?: string,
    actorName?: string,
    actionType?: string,
    moduleName?: string,
    search?: string,
  ) {
    const where: Prisma.AuditLogWhereInput = {
      ...(actorName ? { actorName } : {}),
      ...(actionType ? { actionType } : {}),
      ...(moduleName ? { module: moduleName } : {}),
    };

    const trimmedSearch = search?.trim();
    if (trimmedSearch) {
      where.OR = [
        { actorName: { contains: trimmedSearch, mode: 'insensitive' } },
        { actorEmail: { contains: trimmedSearch, mode: 'insensitive' } },
        { actionType: { contains: trimmedSearch, mode: 'insensitive' } },
        { module: { contains: trimmedSearch, mode: 'insensitive' } },
        { details: { contains: trimmedSearch, mode: 'insensitive' } },
        { ipAddress: { contains: trimmedSearch, mode: 'insensitive' } },
      ];
    }

    const createdAt = this.resolveCreatedAtRange(range);
    if (createdAt) {
      where.createdAt = createdAt;
    }

    const take = Math.min(Math.max(limit, 1), 500);
    const skip = Math.max(page - 1, 0) * take;

    return this.prisma.$transaction(async (tx) => {
      const [items, total] = await Promise.all([
        tx.auditLog.findMany({
          where,
          orderBy: { createdAt: 'desc' },
          take,
          skip,
        }),
        tx.auditLog.count({ where }),
      ]);

      return {
        items,
        total,
        page,
        pageSize: take,
        totalPages: Math.max(1, Math.ceil(total / take)),
      };
    });
  }

  async exportAuditTrailCsv(
    range?: string,
    actorName?: string,
    actionType?: string,
    moduleName?: string,
    search?: string,
  ) {
    const result = await this.getAuditTrail(1, 5000, range, actorName, actionType, moduleName, search);
    const header = ['Timestamp', 'Admin User', 'Admin Email', 'Action Type', 'Module', 'IP Address', 'Severity', 'Details'];
    const rows = result.items.map((item) =>
      [
        item.createdAt.toISOString(),
        item.actorName,
        item.actorEmail || '',
        item.actionType,
        item.module,
        item.ipAddress || '',
        item.severity,
        item.details,
      ]
        .map((cell) => `"${String(cell).replaceAll('"', '""')}"`)
        .join(','),
    );

    return [header.join(','), ...rows].join('\n');
  }

  async createAuditLog(
    dto: CreateAuditLogDto,
    user: AuthenticatedUser,
    ipAddress?: string,
  ) {
    const actor = await this.prisma.user.findUnique({
      where: { id: user.sub },
      select: { id: true, name: true, email: true },
    });

    return this.prisma.auditLog.create({
      data: {
        actorUserId: actor?.id,
        actorName: actor?.name || user.email,
        actorEmail: actor?.email || user.email,
        actionType: dto.actionType,
        module: dto.module,
        ipAddress: ipAddress || null,
        severity: dto.severity || 'default',
        details: dto.details,
      },
    });
  }

  createSystemAuditLog(input: {
    actorUserId?: string | null;
    actorName: string;
    actorEmail?: string | null;
    actionType: string;
    module: string;
    ipAddress?: string | null;
    severity?: 'default' | 'alert';
    details: string;
    metadataJson?: Prisma.InputJsonValue;
  }) {
    return this.prisma.auditLog.create({
      data: {
        actorUserId: input.actorUserId || null,
        actorName: input.actorName,
        actorEmail: input.actorEmail || null,
        actionType: input.actionType,
        module: input.module,
        ipAddress: input.ipAddress || null,
        severity: input.severity || 'default',
        details: input.details,
        metadataJson: input.metadataJson,
      },
    });
  }

  private resolveCreatedAtRange(range?: string): Prisma.DateTimeFilter | undefined {
    if (!range || range === 'all') {
      return undefined;
    }

    const now = Date.now();
    const offset =
      range === '24h'
        ? 24 * 60 * 60 * 1000
        : range === '7d'
          ? 7 * 24 * 60 * 60 * 1000
          : range === '30d'
            ? 30 * 24 * 60 * 60 * 1000
            : null;

    if (!offset) {
      return undefined;
    }

    return {
      gte: new Date(now - offset),
    };
  }
}
