import {
  Injectable,
  NotFoundException,
  ConflictException,
  BadRequestException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from '../../database/entities/user.entity';
import { FamilyMember } from '../../database/entities/family-member.entity';
import { Child } from '../../database/entities/child.entity';
import { Activity } from '../../database/entities/activity.entity';
import { AIConversation } from '../../database/entities/ai-conversation.entity';
import { Photo } from '../../database/entities/photo.entity';
import { AuditLog } from '../../database/entities/audit-log.entity';
import {
  DeletionRequest,
  DeletionRequestStatus,
} from '../../database/entities/deletion-request.entity';
import { UserDataExport } from './interfaces/user-data-export.interface';

@Injectable()
export class ComplianceService {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>,
    @InjectRepository(FamilyMember)
    private familyMemberRepository: Repository<FamilyMember>,
    @InjectRepository(Child)
    private childRepository: Repository<Child>,
    @InjectRepository(Activity)
    private activityRepository: Repository<Activity>,
    @InjectRepository(AIConversation)
    private aiConversationRepository: Repository<AIConversation>,
    @InjectRepository(Photo)
    private photoRepository: Repository<Photo>,
    @InjectRepository(AuditLog)
    private auditLogRepository: Repository<AuditLog>,
    @InjectRepository(DeletionRequest)
    private deletionRequestRepository: Repository<DeletionRequest>,
  ) {}

  /**
   * Export all user data in JSON format (GDPR compliance)
   */
  async exportUserData(userId: string): Promise<UserDataExport> {
    // 1. Get user data
    const user = await this.userRepository.findOne({
      where: { id: userId },
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    // 2. Get family memberships
    const familyMemberships = await this.familyMemberRepository.find({
      where: { userId },
      relations: ['family'],
    });

    const families = familyMemberships.map((membership) => ({
      familyId: membership.familyId,
      familyName: membership.family?.name || 'Unknown',
      role: membership.role as string,
      permissions: membership.permissions as any,
      joinedAt: membership.joinedAt,
    }));

    // 3. Get all children across all families
    const familyIds = families.map((f) => f.familyId);
    const children = await this.childRepository.find({
      where: familyIds.map((familyId) => ({ familyId })),
    });

    const childrenData = children.map((child) => ({
      id: child.id,
      name: child.name,
      birthDate: child.birthDate,
      gender: child.gender,
      photoUrl: child.photoUrl,
      createdAt: child.createdAt,
    }));

    // 4. Get all activities for all children
    const childIds = children.map((c) => c.id);
    const activities = await this.activityRepository.find({
      where: childIds.map((childId) => ({ childId })),
      order: { createdAt: 'DESC' },
    });

    const activitiesData = activities.map((activity) => ({
      id: activity.id,
      childId: activity.childId,
      type: activity.type,
      startedAt: activity.startedAt,
      endedAt: activity.endedAt,
      metadata: activity.metadata,
      notes: activity.notes,
      createdAt: activity.createdAt,
    }));

    // 5. Get AI conversation history
    const aiConversations = await this.aiConversationRepository.find({
      where: { userId },
      order: { createdAt: 'DESC' },
    });

    const aiConversationsData = aiConversations.map((conversation) => ({
      id: conversation.id,
      title: conversation.title,
      messages: conversation.messages,
      totalTokens: conversation.totalTokens,
      createdAt: conversation.createdAt,
    }));

    // 6. Get photos
    const photos = await this.photoRepository.find({
      where: childIds.map((childId) => ({ childId })),
      order: { createdAt: 'DESC' },
    });

    const photosData = photos.map((photo) => ({
      id: photo.id,
      storageKey: photo.storageKey,
      thumbnailKey: photo.thumbnailKey,
      originalFilename: photo.originalFilename,
      caption: photo.caption,
      childId: photo.childId,
      activityId: photo.activityId,
      takenAt: photo.takenAt,
      uploadedAt: photo.createdAt,
    }));

    // 7. Get audit logs (user's own actions only)
    const auditLogs = await this.auditLogRepository.find({
      where: { userId },
      order: { createdAt: 'DESC' },
      take: 1000, // Limit to last 1000 actions
    });

    const auditLogsData = auditLogs.map((log) => ({
      id: log.id,
      action: log.action,
      entityType: log.entityType,
      entityId: log.entityId,
      changes: log.changes,
      ipAddress: log.ipAddress,
      userAgent: log.userAgent,
      createdAt: log.createdAt,
    }));

    // 8. Build export object
    const exportData: UserDataExport = {
      user: {
        id: user.id,
        email: user.email,
        name: user.name,
        phone: user.phone,
        locale: user.locale,
        timezone: user.timezone,
        createdAt: user.createdAt,
        updatedAt: user.updatedAt,
        emailVerified: user.emailVerified,
      },
      families,
      children: childrenData,
      activities: activitiesData,
      aiConversations: aiConversationsData,
      photos: photosData,
      auditLogs: auditLogsData,
      exportMetadata: {
        exportedAt: new Date(),
        exportedBy: userId,
        format: 'JSON',
        dataVersion: '1.0',
      },
    };

    return exportData;
  }

  /**
   * Request account deletion with 30-day grace period (GDPR Right to Erasure)
   */
  async requestAccountDeletion(
    userId: string,
    reason?: string,
    ipAddress?: string,
    userAgent?: string,
  ): Promise<DeletionRequest> {
    // Check if user exists
    const user = await this.userRepository.findOne({ where: { id: userId } });
    if (!user) {
      throw new NotFoundException('User not found');
    }

    // Check for existing pending deletion request
    const existingRequest = await this.deletionRequestRepository.findOne({
      where: {
        userId,
        status: DeletionRequestStatus.PENDING,
      },
    });

    if (existingRequest) {
      throw new ConflictException(
        'Account deletion already requested. You can cancel the existing request before creating a new one.',
      );
    }

    // Create deletion request with 30-day grace period
    const now = new Date();
    const scheduledDeletionAt = new Date(now);
    scheduledDeletionAt.setDate(scheduledDeletionAt.getDate() + 30);

    const deletionRequest = this.deletionRequestRepository.create({
      userId,
      requestedAt: now,
      scheduledDeletionAt,
      reason,
      ipAddress,
      userAgent,
      status: DeletionRequestStatus.PENDING,
    });

    return await this.deletionRequestRepository.save(deletionRequest);
  }

  /**
   * Cancel a pending account deletion request
   */
  async cancelAccountDeletion(
    userId: string,
    cancellationReason?: string,
  ): Promise<DeletionRequest> {
    const deletionRequest = await this.deletionRequestRepository.findOne({
      where: {
        userId,
        status: DeletionRequestStatus.PENDING,
      },
    });

    if (!deletionRequest) {
      throw new NotFoundException(
        'No pending deletion request found for this account',
      );
    }

    deletionRequest.status = DeletionRequestStatus.CANCELLED;
    deletionRequest.cancelledAt = new Date();
    deletionRequest.cancelledBy = userId;
    deletionRequest.cancellationReason = cancellationReason;

    return await this.deletionRequestRepository.save(deletionRequest);
  }

  /**
   * Get pending deletion request for user
   */
  async getPendingDeletionRequest(
    userId: string,
  ): Promise<DeletionRequest | null> {
    return await this.deletionRequestRepository.findOne({
      where: {
        userId,
        status: DeletionRequestStatus.PENDING,
      },
    });
  }

  /**
   * Permanently delete user account and all associated data
   * Called by scheduled job after grace period expires
   */
  async permanentlyDeleteAccount(userId: string): Promise<void> {
    // Verify user exists
    const user = await this.userRepository.findOne({ where: { id: userId } });
    if (!user) {
      throw new NotFoundException('User not found');
    }

    // Get all family memberships to find children
    const familyMemberships = await this.familyMemberRepository.find({
      where: { userId },
    });

    const familyIds = familyMemberships.map((m) => m.familyId);

    // Get all children from user's families
    const children = await this.childRepository.find({
      where: familyIds.map((familyId) => ({ familyId })),
    });

    const childIds = children.map((c) => c.id);

    // Delete in correct order (reverse of foreign key dependencies)

    // 1. Delete activities for children
    if (childIds.length > 0) {
      await this.activityRepository.delete(
        childIds.map((childId) => ({ childId })) as any,
      );
    }

    // 2. Delete photos for children
    if (childIds.length > 0) {
      await this.photoRepository.delete(
        childIds.map((childId) => ({ childId })) as any,
      );
    }

    // 3. Delete children
    if (childIds.length > 0) {
      await this.childRepository.delete(childIds);
    }

    // 4. Delete AI conversations
    await this.aiConversationRepository.delete({ userId });

    // 5. Delete family memberships
    await this.familyMemberRepository.delete({ userId });

    // 6. Delete audit logs (keep for compliance, but anonymize)
    await this.auditLogRepository.update({ userId }, { userId: null });

    // 7. Mark deletion request as completed
    await this.deletionRequestRepository.update(
      { userId, status: DeletionRequestStatus.PENDING },
      {
        status: DeletionRequestStatus.COMPLETED,
        completedAt: new Date(),
      },
    );

    // 8. Finally, delete the user
    await this.userRepository.delete({ id: userId });
  }

  /**
   * Get all deletion requests that are ready to be processed
   * (scheduled deletion date has passed and status is still pending)
   */
  async getDeletionRequestsReadyForProcessing(): Promise<DeletionRequest[]> {
    const now = new Date();

    return await this.deletionRequestRepository
      .createQueryBuilder('deletion_request')
      .where('deletion_request.status = :status', {
        status: DeletionRequestStatus.PENDING,
      })
      .andWhere('deletion_request.scheduled_deletion_at <= :now', { now })
      .getMany();
  }
}
