import { Repository, FindOptionsWhere, Between, LessThanOrEqual, MoreThanOrEqual } from 'typeorm';
import {
  AuditLog,
  EntityChange,
  LoginHistory,
  SensitiveDataAccess,
  DataExport,
  PermissionChange,
  ConfigChange,
} from '../entities';

export interface AuditLogFilters {
  userId?: string;
  entityType?: string;
  action?: string;
  category?: string;
  startDate?: Date;
  endDate?: Date;
  ipAddress?: string;
}

export interface PaginationOptions {
  page?: number;
  limit?: number;
}

export class AuditService {
  constructor(
    private readonly auditLogRepository: Repository<AuditLog>,
    private readonly entityChangeRepository: Repository<EntityChange>,
    private readonly loginHistoryRepository: Repository<LoginHistory>,
    private readonly sensitiveDataAccessRepository: Repository<SensitiveDataAccess>,
    private readonly dataExportRepository: Repository<DataExport>,
    private readonly permissionChangeRepository: Repository<PermissionChange>,
    private readonly configChangeRepository: Repository<ConfigChange>
  ) {}

  // ============================================
  // AUDIT LOGS
  // ============================================

  async createAuditLog(tenantId: string, data: Partial<AuditLog>): Promise<AuditLog> {
    const log = this.auditLogRepository.create({
      ...data,
      tenantId,
    });
    return this.auditLogRepository.save(log);
  }

  async findAuditLogs(
    tenantId: string,
    filters: AuditLogFilters = {},
    pagination: PaginationOptions = {}
  ): Promise<{ data: AuditLog[]; total: number }> {
    const { page = 1, limit = 50 } = pagination;
    const where: FindOptionsWhere<AuditLog> = { tenantId };

    if (filters.userId) where.userId = filters.userId;
    if (filters.entityType) where.resourceType = filters.entityType;
    if (filters.action) where.action = filters.action as any;
    if (filters.category) where.actionCategory = filters.category as any;
    if (filters.ipAddress) where.ipAddress = filters.ipAddress;

    if (filters.startDate && filters.endDate) {
      where.createdAt = Between(filters.startDate, filters.endDate);
    } else if (filters.startDate) {
      where.createdAt = MoreThanOrEqual(filters.startDate);
    } else if (filters.endDate) {
      where.createdAt = LessThanOrEqual(filters.endDate);
    }

    const [data, total] = await this.auditLogRepository.findAndCount({
      where,
      order: { createdAt: 'DESC' },
      skip: (page - 1) * limit,
      take: limit,
    });

    return { data, total };
  }

  async findAuditLogsByEntity(
    tenantId: string,
    entityType: string,
    entityId: string
  ): Promise<AuditLog[]> {
    return this.auditLogRepository.find({
      where: { tenantId, resourceType: entityType, resourceId: entityId },
      order: { createdAt: 'DESC' },
    });
  }

  // ============================================
  // ENTITY CHANGES
  // ============================================

  async createEntityChange(tenantId: string, data: Partial<EntityChange>): Promise<EntityChange> {
    const change = this.entityChangeRepository.create({
      ...data,
      tenantId,
    });
    return this.entityChangeRepository.save(change);
  }

  async findEntityChanges(
    tenantId: string,
    entityType: string,
    entityId: string
  ): Promise<EntityChange[]> {
    return this.entityChangeRepository.find({
      where: { tenantId, entityType, entityId },
      order: { changedAt: 'DESC' },
    });
  }

  async getEntityVersion(
    tenantId: string,
    entityType: string,
    entityId: string,
    version: number
  ): Promise<EntityChange | null> {
    return this.entityChangeRepository.findOne({
      where: { tenantId, entityType, entityId, version },
    });
  }

  // ============================================
  // LOGIN HISTORY
  // ============================================

  async createLoginHistory(data: Partial<LoginHistory>): Promise<LoginHistory> {
    const login = this.loginHistoryRepository.create(data);
    return this.loginHistoryRepository.save(login);
  }

  async findLoginHistory(
    userId: string,
    tenantId?: string,
    limit: number = 20
  ): Promise<LoginHistory[]> {
    const where: FindOptionsWhere<LoginHistory> = { userId };
    if (tenantId) where.tenantId = tenantId;

    return this.loginHistoryRepository.find({
      where,
      order: { attemptedAt: 'DESC' },
      take: limit,
    });
  }

  async getActiveSessionsCount(userId: string): Promise<number> {
    // Note: LoginHistory tracks login attempts, not sessions
    // This counts successful login attempts (not truly active sessions)
    return this.loginHistoryRepository.count({
      where: { userId, status: 'success' },
    });
  }

  // Note: Session logout tracking requires a separate Session entity
  // LoginHistory only tracks login attempts

  // ============================================
  // SENSITIVE DATA ACCESS
  // ============================================

  async logSensitiveDataAccess(
    tenantId: string,
    data: Partial<SensitiveDataAccess>
  ): Promise<SensitiveDataAccess> {
    const access = this.sensitiveDataAccessRepository.create({
      ...data,
      tenantId,
    });
    return this.sensitiveDataAccessRepository.save(access);
  }

  async findSensitiveDataAccess(
    tenantId: string,
    filters: { userId?: string; dataType?: string; startDate?: Date; endDate?: Date } = {}
  ): Promise<SensitiveDataAccess[]> {
    const where: FindOptionsWhere<SensitiveDataAccess> = { tenantId };

    if (filters.userId) where.userId = filters.userId;
    if (filters.dataType) where.dataType = filters.dataType as any;

    if (filters.startDate && filters.endDate) {
      where.accessedAt = Between(filters.startDate, filters.endDate);
    }

    return this.sensitiveDataAccessRepository.find({
      where,
      order: { accessedAt: 'DESC' },
      take: 100,
    });
  }

  // ============================================
  // DATA EXPORTS
  // ============================================

  async createDataExport(tenantId: string, data: Partial<DataExport>): Promise<DataExport> {
    const exportRecord = this.dataExportRepository.create({
      ...data,
      tenantId,
      status: 'pending',
    });
    return this.dataExportRepository.save(exportRecord);
  }

  async findDataExport(id: string): Promise<DataExport | null> {
    return this.dataExportRepository.findOne({ where: { id } });
  }

  async findUserDataExports(tenantId: string, userId: string): Promise<DataExport[]> {
    return this.dataExportRepository.find({
      where: { tenantId, userId },
      order: { requestedAt: 'DESC' },
    });
  }

  async updateDataExportStatus(
    id: string,
    status: string,
    updates: Partial<DataExport> = {}
  ): Promise<DataExport | null> {
    const exportRecord = await this.findDataExport(id);
    if (!exportRecord) return null;

    exportRecord.status = status as any;
    Object.assign(exportRecord, updates);

    if (status === 'completed') {
      exportRecord.completedAt = new Date();
    }

    return this.dataExportRepository.save(exportRecord);
  }

  // ============================================
  // PERMISSION CHANGES
  // ============================================

  async logPermissionChange(
    tenantId: string,
    data: Partial<PermissionChange>
  ): Promise<PermissionChange> {
    const change = this.permissionChangeRepository.create({
      ...data,
      tenantId,
    });
    return this.permissionChangeRepository.save(change);
  }

  async findPermissionChanges(
    tenantId: string,
    targetUserId?: string
  ): Promise<PermissionChange[]> {
    const where: FindOptionsWhere<PermissionChange> = { tenantId };
    if (targetUserId) where.targetUserId = targetUserId;

    return this.permissionChangeRepository.find({
      where,
      order: { changedAt: 'DESC' },
      take: 100,
    });
  }

  // ============================================
  // CONFIG CHANGES
  // ============================================

  async logConfigChange(tenantId: string, data: Partial<ConfigChange>): Promise<ConfigChange> {
    const change = this.configChangeRepository.create({
      ...data,
      tenantId,
    });
    return this.configChangeRepository.save(change);
  }

  async findConfigChanges(tenantId: string, configType?: string): Promise<ConfigChange[]> {
    const where: FindOptionsWhere<ConfigChange> = { tenantId };
    if (configType) where.configType = configType as any;

    return this.configChangeRepository.find({
      where,
      order: { changedAt: 'DESC' },
      take: 100,
    });
  }

  // Note: ConfigChange entity doesn't track versions
  // Use changedAt timestamp to get specific config snapshots
  async getConfigChangeByDate(
    tenantId: string,
    configKey: string,
    date: Date
  ): Promise<ConfigChange | null> {
    return this.configChangeRepository.findOne({
      where: { tenantId, configKey },
      order: { changedAt: 'DESC' },
    });
  }
}
