const REQUESTER_FOR_TEST = { role: 'admin', admId: 9 } as const;

// 판정 · 버전 검사는 이 spec 의 대상이 아니다 (쓰기 순서와 감사 원자성이 대상). 판정 본문은 permission-guardrail.spec 과
// 격리 시험 (permission-guardrail.itspec · admin-edit-guardrail.itspec) 이 실제 DB 로 본다.
jest.mock('../auth/permission/permission-guardrail', () => ({
  ...jest.requireActual('../auth/permission/permission-guardrail'),
  assertAccountGuardrailInTx: jest.fn().mockResolvedValue({ targetAthId: null }),
  assertRoleGuardrailInTx: jest.fn().mockResolvedValue(undefined),
  assertVersionMatch: jest.fn(),
}));
// 계정 인증 상태 잠금 · 세션 끊기(관리자 2차 인증 3.5 · 3.10) — 가짜 tx 라 여기서 비운다. 동작은 auth/admin/self-password-change.spec 이 본다.
jest.mock('../auth/admin/admin-session', () => ({
  ...jest.requireActual('../auth/admin/admin-session'),
  lockAuthState: jest.fn().mockResolvedValue(undefined),
  revokeSessions: jest.fn().mockResolvedValue(undefined),
  passwordSetByOther: jest.fn().mockResolvedValue(undefined),
  deleteAuthStateOf: jest.fn().mockResolvedValue(undefined),
}));
jest.mock('../auth/permission/permission-version', () => ({
  ...jest.requireActual('../auth/permission/permission-version'),
  lockPermissionVersion: jest.fn().mockResolvedValue(undefined),
  readGlobalVersion: jest.fn().mockResolvedValue(1),
}));
import { ConfigService } from '@nestjs/config';
import { AuditService } from '../audit/audit.service';
import { AdminRepository } from './admin.repository';

// 상승 호출을 가짜로 바꾸지 않는다 (permission-version 은 실제 구현). 같은 tx 로 한 번 불리고
// 업무 쓰기 뒤, 감사 적재 앞에 오는지를 호출 순서로 본다. 상승이 빠지거나 tx 밖이면 순서 목록이 달라진다.
// drizzle 스키마는 스텁이라 테이블 식별이 안 된다. 이 spec 에서 갱신 호출은 모두 상승 또는 업무 갱신이다.

const ctx = {
  actor: {
    role: 'ADMIN',
    refId: 1,
    refLabel: 'super',
    orgPath: null,
    personId: null,
  },
  requestId: 'req-bump',
  ip: '192.0.2.21',
  userAgent: 'test-agent',
} as any;

// 한 번의 transaction 콜백이 받는 tx 하나. 호출 순서를 log 에 쌓는다.
function recordingTx(log: string[], selectRows: unknown[]) {
  const thenable = (rows: unknown[]) =>
    Object.assign(Promise.resolve(rows), {
      //* 삭제의 승인 행 잠금 읽기(`.where().for('update')`) — 승인 행 없음
      for: async () => [],
      limit: (n: number) =>
        Object.assign(Promise.resolve(rows.slice(0, n)), { for: async () => rows.slice(0, n) }),
    });
  return {
    select: () => ({ from: () => ({ where: () => thenable(selectRows) }) }),
    update: () => {
      log.push('update');
      return {
        set: () => ({ where: async () => [{ affectedRows: 1 }] }),
      };
    },
    delete: () => {
      log.push('delete');
      return { where: async () => undefined };
    },
  } as any;
}

function repoWith(log: string[], selectRows: unknown[]) {
  const tx = recordingTx(log, selectRows);
  const drizzle = {
    transaction: async (fn: (t: unknown) => Promise<unknown>) => fn(tx),
  } as any;
  const audit = {
    appendSecurity: jest.fn(async () => {
      log.push('audit');
    }),
  } as unknown as AuditService;
  const config = { get: () => 'k' } as unknown as ConfigService;
  return new AdminRepository(drizzle, config, audit);
}

describe('(z) 관리자 계정 쓰기의 상승 위치 (리뷰 R1 MEDIUM · 두 사이트)', () => {
  it('(z) 삭제는 업무 삭제 → 상승 → 감사 순서로 같은 tx 에서 돈다', async () => {
    const log: string[] = [];
    const repo = repoWith(log, [{ admId: 1, admLoginId: 'gone' }]);
    await repo.delete([1], ctx);
    //* 신청 표 행 → 원천 행 두 DELETE(노트 84 T12)
    expect(log).toEqual(['delete', 'delete', 'update', 'audit']);
  });

  it('(z) 수정은 업무 갱신 → 상승 → 감사 순서로 같은 tx 에서 돈다', async () => {
    const log: string[] = [];
    const repo = repoWith(log, [{ athId: 1 }]);
    await repo.update(
      1,
      {
        athId: 2,
        admPassword: '',
        admName: 'n',
        admPhone: 'p',
        admEmail: '',
      } as any,
      REQUESTER_FOR_TEST, ctx,
    );
    expect(log).toEqual(['update', 'update', 'audit']);
  });

  it('(z) 이름·연락처만 바꾼 수정은 상승하지 않는다 (binding 불변)', async () => {
    const log: string[] = [];
    const repo = repoWith(log, [{ athId: 1 }]);
    await repo.update(
      1,
      {
        athId: 1,
        admPassword: '',
        admName: 'n2',
        admPhone: 'p2',
        admEmail: '',
      } as any,
      REQUESTER_FOR_TEST, ctx,
    );
    expect(log).toEqual(['update', 'audit']);
  });

  it('(z) 비밀번호만 바꾼 수정은 상승하지 않는다 (사용자 결정 · m8b 항목 10)', async () => {
    const log: string[] = [];
    const repo = repoWith(log, [{ athId: 1 }]);
    await repo.update(
      1,
      {
        athId: 1,
        admPassword: 'new-pass-1!',
        admName: 'n',
        admPhone: 'p',
        admEmail: '',
      } as any,
      REQUESTER_FOR_TEST, ctx,
    );
    expect(log).toEqual(['update', 'audit']);
  });
});
