import {
  ConflictException,
  Injectable,
  Logger,
  NotFoundException,
} from '@nestjs/common';
import { FieldAccess } from '@prisma/client';
import { PrismaService } from '@core/database/prisma/prisma.service';
import { DataScopeUser } from './data-scope.service';
import { MaskingService } from './masking.service';
import { IamAuditService } from './iam-audit.service';
import { SkipAssertAccess } from '../decorators/skip-assert-access.decorator';
import { isAdministrator } from '../utils/role-check.util';

/**
 * 字段级权限（规则 §5.4）
 *
 * 使用方式（Service 查询后处理）：
 *   const rows = await prisma.xxx.findMany(...);
 *   return this.fieldPermissionService.filter(user, 'purchase_order', rows);
 *
 * 对每一行根据用户角色聚合最宽松的字段访问（多角色取 max），执行：
 * - VISIBLE：原样输出
 * - READONLY：原样输出（READONLY 仅对前端有意义，表示禁止编辑）
 * - HIDDEN：字段从输出删除
 * - DESENSITIZE：走 MaskingService 处理
 *
 * 规则约束：多角色取最宽松（配错风险由审计兜底，而非代码）— 与 DataScope 最宽原则一致。
 */

const ACCESS_PRIORITY: Record<FieldAccess, number> = {
  [FieldAccess.HIDDEN]: 1,
  [FieldAccess.DESENSITIZE]: 2,
  [FieldAccess.READONLY]: 3,
  [FieldAccess.VISIBLE]: 4,
};

@Injectable()
export class FieldPermissionService {
  private readonly logger = new Logger(FieldPermissionService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly masking: MaskingService,
    private readonly audit: IamAuditService,
  ) {}

  // ---------------- Admin CRUD（IAM 后台用） ----------------

  adminList(filters: { resource?: string; roleId?: string }) {
    return this.prisma.fieldPermission.findMany({
      where: {
        resource: filters.resource ?? undefined,
        roleId: filters.roleId ?? undefined,
      },
      orderBy: [{ resource: 'asc' }, { field: 'asc' }],
    });
  }

  async adminCreate(
    input: {
      roleId: string;
      resource: string;
      field: string;
      access: FieldAccess;
    },
    actor: string,
  ) {
    const exists = await this.prisma.fieldPermission.findUnique({
      where: {
        roleId_resource_field: {
          roleId: input.roleId,
          resource: input.resource,
          field: input.field,
        },
      },
    });
    if (exists) {
      throw new ConflictException(
        '该角色对 (resource, field) 已存在字段权限配置',
      );
    }

    const created = await this.prisma.fieldPermission.create({ data: input });

    await this.audit.record({
      actor,
      action: 'CREATE',
      resource: 'FieldPermission',
      targetId: created.id,
      after: created,
    });

    return created;
  }

  async adminUpdate(
    id: string,
    input: { access?: FieldAccess },
    actor: string,
  ) {
    const before = await this.prisma.fieldPermission.findUnique({ where: { id } });
    if (!before) throw new NotFoundException('FieldPermission 不存在');

    const updated = await this.prisma.fieldPermission.update({
      where: { id },
      data: { access: input.access ?? undefined },
    });

    await this.audit.record({
      actor,
      action: 'UPDATE',
      resource: 'FieldPermission',
      targetId: id,
      before,
      after: updated,
    });

    return updated;
  }

  @SkipAssertAccess('IAM 配置表：FieldPermission 是角色字段权限规则，无业务归属，鉴权走 iam_admin:manage')
  async adminRemove(id: string, actor: string) {
    const before = await this.prisma.fieldPermission.findUnique({ where: { id } });
    if (!before) throw new NotFoundException('FieldPermission 不存在');

    await this.prisma.fieldPermission.delete({ where: { id } });

    await this.audit.record({
      actor,
      action: 'DELETE',
      resource: 'FieldPermission',
      targetId: id,
      before,
    });

    return { ok: true };
  }

  async filter<T extends Record<string, any>>(
    user: DataScopeUser,
    resource: string,
    records: T[] | T,
  ): Promise<T[] | T> {
    if (isAdministrator(user)) {
      return records;
    }

    const fieldAccessMap = await this.resolveEffectiveAccess(user, resource);
    if (Object.keys(fieldAccessMap).length === 0) {
      return records;
    }

    const apply = (row: T) => this.applyToRow(row, fieldAccessMap);
    return Array.isArray(records) ? records.map(apply) : apply(records as T);
  }

  /**
   * 聚合用户所有角色对该 resource 的字段级权限，多角色取最宽松
   */
  private async resolveEffectiveAccess(
    user: DataScopeUser,
    resource: string,
  ): Promise<Record<string, FieldAccess>> {
    const roles = user.roles || [];
    if (roles.length === 0) return {};

    // 通过 role code 反查 roleId
    const roleRecords = await this.prisma.role.findMany({
      where: { code: { in: roles } },
      select: { id: true },
    });
    const roleIds = roleRecords.map((r) => r.id);
    if (roleIds.length === 0) return {};

    const perms = await this.prisma.fieldPermission.findMany({
      where: { roleId: { in: roleIds }, resource },
    });

    const merged: Record<string, FieldAccess> = {};
    for (const p of perms) {
      const prev = merged[p.field];
      if (!prev || ACCESS_PRIORITY[p.access] > ACCESS_PRIORITY[prev]) {
        merged[p.field] = p.access;
      }
    }
    return merged;
  }

  private applyToRow<T extends Record<string, any>>(
    row: T,
    map: Record<string, FieldAccess>,
  ): any {
    const out: Record<string, any> = { ...row };
    for (const [field, access] of Object.entries(map)) {
      if (!(field in out)) continue;
      switch (access) {
        case FieldAccess.HIDDEN:
          delete out[field];
          break;
        case FieldAccess.DESENSITIZE:
          out[field] = this.masking.mask(field, out[field]);
          break;
        case FieldAccess.READONLY:
        case FieldAccess.VISIBLE:
        default:
          // 保留原值
          break;
      }
    }
    return out;
  }
}
