import { Injectable, Logger } from '@nestjs/common';
import { PrismaService } from '@core/database/prisma/prisma.service';
import { RedisService } from '@core/cache/redis/redis.service';
import { DepartmentTreeTooDeepException } from '../exceptions/department-tree-too-deep.exception';

const MAX_DEPT_TREE_DEPTH = 10;
const DEPT_TREE_CACHE_TTL = 60 * 60; // 1h

/**
 * 组织层级共享服务：部门树递归 + Redis 缓存 + maxDepth 异常。
 *
 * 规则对齐（09-iam-security.md §5.3.16）：
 * - 超过 maxDepth=10 抛 DepartmentTreeTooDeepException，不静默截断
 * - Redis 缓存 dept_tree:${deptId}，TTL 1h
 * - 部门 CRUD 时调用 invalidateDepartmentTree 失效缓存
 */
@Injectable()
export class OrganizationContextService {
  private readonly logger = new Logger(OrganizationContextService.name);

  constructor(
    private prisma: PrismaService,
    private redis: RedisService,
  ) {}

  async getUserDepartmentIds(userId: string): Promise<string[]> {
    const departments = await this.prisma.userDepartment.findMany({
      where: { userId, leftAt: null },
      select: { departmentId: true },
    });
    return departments.map((d) => d.departmentId);
  }

  /**
   * 返回 user 所有 active 部门及其子树（多部门取并集）
   * 并行加载各子树缓存以缩短 hot-path 延迟。
   */
  async getDepartmentTreeIds(userId: string): Promise<string[]> {
    const userDeptIds = await this.getUserDepartmentIds(userId);
    if (userDeptIds.length === 0) return [];

    const subtrees = await Promise.all(
      userDeptIds.map((rootId) => this.getSubtreeIds(rootId)),
    );
    const allDeptIds = new Set<string>(userDeptIds);
    subtrees.forEach((s) => s.forEach((id) => allDeptIds.add(id)));
    return Array.from(allDeptIds);
  }

  /**
   * 单个部门的子树（含自身）— 带 Redis 缓存
   */
  async getSubtreeIds(deptId: string): Promise<string[]> {
    const cacheKey = `dept_tree:${deptId}`;
    const cached = await this.redis.getJson<string[]>(cacheKey);
    if (cached) return cached;

    const result = new Set<string>([deptId]);
    await this.expandChildren([deptId], 0, result, deptId);

    const ids = Array.from(result);
    await this.redis.setJson(cacheKey, DEPT_TREE_CACHE_TTL, ids);
    return ids;
  }

  private async expandChildren(
    parentIds: string[],
    depth: number,
    accumulator: Set<string>,
    rootId: string,
  ): Promise<void> {
    if (parentIds.length === 0) return;
    if (depth >= MAX_DEPT_TREE_DEPTH) {
      this.logger.warn(
        `部门树超过 maxDepth=${MAX_DEPT_TREE_DEPTH} rootId=${rootId}`,
      );
      throw new DepartmentTreeTooDeepException(rootId, MAX_DEPT_TREE_DEPTH);
    }
    const children = await this.prisma.department.findMany({
      where: { parentId: { in: parentIds }, deletedAt: null },
      select: { id: true },
    });
    const newChildren = children
      .map((c) => c.id)
      .filter((id) => !accumulator.has(id));
    newChildren.forEach((id) => accumulator.add(id));
    if (newChildren.length > 0) {
      await this.expandChildren(newChildren, depth + 1, accumulator, rootId);
    }
  }

  async getUserOrganizationIds(userId: string): Promise<string[]> {
    const organizations = await this.prisma.userDepartment.findMany({
      where: { userId, leftAt: null },
      select: { organizationId: true },
      distinct: ['organizationId'],
    });
    return organizations.map((o) => o.organizationId);
  }

  /**
   * 失效单个部门的子树缓存。部门 CRUD 时调用。
   * 必要时向上遍历祖先并失效，因为祖先的子树缓存也会包含本节点。
   */
  async invalidateDepartmentTree(deptId: string): Promise<void> {
    const ancestors = await this.getAncestorIds(deptId);
    const keys = [deptId, ...ancestors].map((id) => `dept_tree:${id}`);
    await this.redis.del(...keys);
  }

  private async getAncestorIds(deptId: string): Promise<string[]> {
    const ancestors: string[] = [];
    let currentId: string = deptId;
    for (let i = 0; i < MAX_DEPT_TREE_DEPTH; i++) {
      const dept: { parentId: string | null } | null =
        await this.prisma.department.findUnique({
          where: { id: currentId },
          select: { parentId: true },
        });
      if (!dept?.parentId) break;
      ancestors.push(dept.parentId);
      currentId = dept.parentId;
    }
    return ancestors;
  }
}
