import { ApiCode } from '@pac/types';
import { assertPlanClaimedBy } from '../src/modules/plan/claim-guard';
import { BizError } from '../src/common/errors/biz-error';

/**
 * 认领闸回归 —— 「未认领只能看,不能操作」。
 *
 * 背景(2026-07 生产实证):submitExecution 当时只校验 status、**完全没看 assignee**,
 * 结果缺牙召回有两条 plan 在 `assignee IS NULL` 状态下被直接关闭。
 * 危害不止"撞单":execution.service 特意保留 assigneeUserId 作**业绩归属**
 * (那里的注释写明清空会让「我的已完成」/ 转化率永远为 0)—— 未认领就提交执行,
 * 这条执行没有任何归属人,统计里直接蒸发。所以闸必须在服务端,前端提示替代不了。
 *
 * 三条红线(下面各有用例锁住):
 *   1. 没人认领 → 拒绝,且码是 PLAN_NOT_CLAIMED(引导去认领)
 *   2. 别人认领 → 拒绝,且码**不同**(PLAN_CLAIMED_BY_OTHER),否则组员以为自己没点对
 *   3. 自己认领 → 放行(闸不能把正常作业也拦了)
 */

const ME = 'u-me';

function catchErr(fn: () => void): BizError {
  try {
    fn();
  } catch (e) {
    return e as BizError;
  }
  throw new Error('期望抛错,但没抛');
}

describe('assertPlanClaimedBy — 认领闸', () => {
  test('⭐ 红线③:自己认领 → 放行', () => {
    expect(() => assertPlanClaimedBy({ assigneeUserId: ME }, ME)).not.toThrow();
  });

  test('⭐ 红线①:没人认领 → PLAN_NOT_CLAIMED', () => {
    const err = catchErr(() => assertPlanClaimedBy({ assigneeUserId: null }, ME));
    expect(err).toBeInstanceOf(BizError);
    expect(err.code).toBe(ApiCode.PLAN_NOT_CLAIMED);
    expect(err.msg).toContain('认领');
  });

  test('⭐ 红线②:别人认领 → PLAN_CLAIMED_BY_OTHER(跟"没人认领"必须是不同的码)', () => {
    const err = catchErr(() => assertPlanClaimedBy({ assigneeUserId: 'u-other' }, ME));
    expect(err.code).toBe(ApiCode.PLAN_CLAIMED_BY_OTHER);
    expect(err.code).not.toBe(ApiCode.PLAN_NOT_CLAIMED); // 两态文案不能合并
    // details 带占用人 —— 前端据此查字典换姓名展示("已被 张三 认领")
    expect(err.details).toEqual({ assigneeUserId: 'u-other' });
  });

  test('空串 assignee 当作未认领(不是"被叫空串的人认领了")', () => {
    const err = catchErr(() => assertPlanClaimedBy({ assigneeUserId: '' }, ME));
    expect(err.code).toBe(ApiCode.PLAN_NOT_CLAIMED);
  });

  test('用户 id 区分大小写 / 不做模糊匹配 —— 不是自己就是不行', () => {
    expect(() => assertPlanClaimedBy({ assigneeUserId: 'U-ME' }, ME)).toThrow();
    expect(() => assertPlanClaimedBy({ assigneeUserId: 'u-me-2' }, ME)).toThrow();
  });
});
