import { Injectable, BadRequestException, NotFoundException, Inject, Optional } from '@nestjs/common';
import { UsersRepository } from './users.repository';
import { PrismaService } from '../../infrastructure/database/prisma.service';
import { EffectiveMembershipService } from '../membership/effective-membership.service';
import { EffectiveQuotaService } from '../membership/effective-quota.service';

const DELETION_COOLING_DAYS = 7;

@Injectable()
export class UsersService {
  constructor(
    private readonly usersRepository: UsersRepository,
    private readonly prisma: PrismaService,
    // M-MEMBER-01-04: 可选注入——避免循环依赖。未注入时回退旧逻辑。
    @Optional() @Inject(EffectiveMembershipService) private readonly membershipService?: EffectiveMembershipService,
    @Optional() @Inject(EffectiveQuotaService) private readonly quotaService?: EffectiveQuotaService,
  ) {}

  async getProfile(userId: string) { return this.usersRepository.findProfileByUserId(userId); }
  async updateProfile(userId: string, dto: any) { return this.usersRepository.updateProfile(userId, dto); }
  async getProfileDetail(userId: string) { return this.usersRepository.findUserProfile(userId); }
  async updateProfileDetail(userId: string, dto: any) { return this.usersRepository.upsertUserProfile(userId, dto); }
  async updatePreferences(userId: string, dto: any) { return this.usersRepository.updatePreferences(userId, dto); }

  // ── Membership ──

  async getMembership(userId: string) {
    // M-MEMBER-01-04: 优先使用 EffectiveMembershipService
    if (this.membershipService) {
      const membership = await this.membershipService.compute(userId);
      return {
        effectivePlan: membership.effectivePlan,
        grants: membership.grants,
        appAccountToken: membership.appAccountToken ?? await this.ensureAppAccountToken(userId),
      };
    }
    // 回退：旧逻辑（MembershipModule 未加载时）
    return this.prisma.userMembership.findFirst({
      where: { userId, active: true },
      include: { plan: true },
      orderBy: { createdAt: 'desc' },
    });
  }

  // ── Account Deletion ──

  async requestDeletion(userId: string) {
    const existing = await this.prisma.accountDeletionRequest.findFirst({
      where: { userId, status: 'pending' },
    });
    if (existing) throw new BadRequestException('已有进行中的注销申请');

    const coolingEndsAt = new Date(Date.now() + DELETION_COOLING_DAYS * 86400000);
    return this.prisma.accountDeletionRequest.create({
      data: { userId, coolingEndsAt },
    });
  }

  async cancelDeletion(userId: string) {
    const req = await this.prisma.accountDeletionRequest.findFirst({
      where: { userId, status: 'pending' },
    });
    if (!req) throw new NotFoundException('未找到进行中的注销申请');

    return this.prisma.accountDeletionRequest.update({
      where: { id: req.id },
      data: { status: 'cancelled', cancelledAt: new Date() },
    });
  }

  // ── Device Management ──

  async getDevices(userId: string) {
    return this.prisma.userDevice.findMany({
      where: { userId },
      orderBy: { lastSeenAt: 'desc' },
    });
  }

  async removeDevice(userId: string, deviceId: string) {
    await this.prisma.userDevice.deleteMany({
      where: { userId, deviceId },
    });
    return { success: true };
  }

  // ── Storage ──

  async getStorage(userId: string) {
    const now = new Date();
    const [files, pendingReservations] = await Promise.all([
      this.prisma.uploadedFile.findMany({
        where: { userId },
        select: { sizeBytes: true, mimeType: true },
      }),
      this.prisma.uploadSession.findMany({
        where: {
          userId,
          status: { in: ['created', 'uploaded'] },
          expiresAt: { gt: now },
        },
        select: { reservedBytes: true },
      }),
    ]);
    const committedBytes = files.reduce((sum, f) => sum + Number(f.sizeBytes), 0);
    const reservedBytes = pendingReservations.reduce((sum, item) => sum + Number(item.reservedBytes), 0);
    const usedBytes = committedBytes + reservedBytes;

    let totalBytes: number | null = null;
    if (this.quotaService) {
      try {
        const q = await this.quotaService.computeEffectiveQuota(userId);
        const storageLimit = q.limits.find(l => l.quotaType === 'storage_bytes');
        if (storageLimit && !storageLimit.isUnlimited) totalBytes = storageLimit.limit;
      } catch { /* fall through */ }
    }
    if (totalBytes === null && this.membershipService) {
      try {
        const entitlements = await this.membershipService.getEntitlements(userId);
        totalBytes = entitlements.entitlements?.maxStorageBytes ?? null;
      } catch { /* leave null */ }
    }
    return { totalBytes, usedBytes, fileCount: files.length };
  }

  // ── Assets Summary ──

  async getAssetsSummary(userId: string) {
    const [kbCount, itemCount, cardCount] = await Promise.all([
      this.prisma.knowledgeBase.count({ where: { userId, deletedAt: null } }),
      this.prisma.knowledgeItem.count({ where: { userId, deletedAt: null } }),
      this.prisma.reviewCard.count({ where: { userId } }),
    ]);
    return { knowledgeBaseCount: kbCount, knowledgeItemCount: itemCount, reviewCardCount: cardCount };
  }

  // M-MEMBER-01-02: Auto-generate appAccountToken if missing
  private async ensureAppAccountToken(userId: string): Promise<string> {
    const token = require('crypto').randomUUID();
    await this.prisma.user.update({ where: { id: userId }, data: { appAccountToken: token } });
    return token;
  }
}
