import { getSessionAccessToken, verifySession } from '@/lib/auth/dal';
import { backendFetchStream } from '@/lib/http/backend-fetch';

/**
 * 학생 회원 목록 엑셀 다운로드 — 백엔드가 만든 xlsx를 브라우저로 중계한다.
 *
 * 왜 라우트 핸들러인가: 브라우저는 백엔드를 직접 호출하지 않는다(설계서 §7 BFF 전제). 백엔드
 * 엑셀 API는 ROLE_ADMIN 토큰을 요구하는데 그 토큰은 httpOnly 세션 안에만 있어 브라우저가 꺼낼 수
 * 없다. Server Component는 파일 응답을 만들 수 없고 Server Action은 반환값을 브라우저 메모리에
 * 펼쳐야 하므로, 파일 다운로드는 라우트 핸들러가 유일하게 맞는 자리다.
 *
 * 본문은 파싱하지 않고 업스트림 스트림을 그대로 흘려보낸다(서버 메모리에 파일 전체를 올리지
 * 않는다). 파일명·MIME도 백엔드 헤더를 그대로 전달한다 — 백엔드가 이미 RFC 5987 형식
 * (`filename*=UTF-8''회원목록_YYYYMMDD.xlsx`)으로 올바르게 내려준다.
 *
 * 응답 캐시 금지는 next.config.ts의 전역 보안 헤더(`Cache-Control: no-store, ...`)가 담당한다.
 */

const STUDENT_MEMBER_EXCEL_PATH = '/api/v1/mngr/user/list/excel';

/**
 * 이 기능은 화면의 검색·페이징과 무관하게 **항상 전체 학생 데이터**를 내려받는다(요구사항).
 *
 * 그런데 백엔드 엑셀 API는 목록 API와 같은 `selectPagination`을 그대로 쓴다 — 즉 페이징이
 * 걸리고, 파라미터를 생략하면 기본값(`recordCountPerPage=10`)이 적용돼 **10건만** 담긴 파일이
 * 나온다. "전체" 모드가 따로 없으므로 충분히 큰 상한을 명시해 1페이지로 전부 받아온다.
 * 상한을 무한대로 둘 수는 없다(백엔드가 전체 행을 메모리에 올려 워크북을 만든다).
 *
 * 회원 수가 이 상한을 넘으면 파일이 조용히 잘린다 — 그 시점에는 백엔드에 "전체 조회" 모드가
 * 필요하다. 상한을 올리는 것은 임시방편일 뿐이다.
 */
const EXCEL_ROW_LIMIT = 100_000;

/** 전체 행을 모아 워크북을 만드는 시간이 있어 일반 조회보다 넉넉히 잡는다. */
const EXCEL_TIMEOUT_MS = 60_000;

const DOWNLOAD_FAILED_MESSAGE =
  '엑셀 파일을 내려받지 못했습니다. 잠시 후 다시 시도해 주세요.';

const XLSX_CONTENT_TYPE =
  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';

export async function GET() {
  // 라우트 핸들러는 UI를 거치지 않고 직접 호출될 수 있으므로 여기서 직접 인증을 확인한다
  // (proxy의 쿠키 존재 확인은 낙관적 필터일 뿐이다 — 설계서 §8).
  await verifySession();

  const accessToken = await getSessionAccessToken();

  const result = await backendFetchStream(STUDENT_MEMBER_EXCEL_PATH, {
    // 검색 조건(searchCondition/searchKeyword)을 의도적으로 보내지 않는다 — 화면에서 무엇을
    // 검색 중이든 파일에는 전체가 담겨야 한다.
    query: { pageIndex: 1, recordCountPerPage: EXCEL_ROW_LIMIT },
    accessToken: accessToken ?? undefined,
    timeoutMs: EXCEL_TIMEOUT_MS,
  });

  if (!result.ok) {
    // 실패 사유(코드·백엔드 메시지)는 backendFetchStream이 서버 콘솔에 남긴다. 화면에는
    // 일반화된 문구만 내보낸다.
    return new Response(DOWNLOAD_FAILED_MESSAGE, {
      status: 502,
      headers: { 'Content-Type': 'text/plain; charset=utf-8' },
    });
  }

  const upstream = result.data;

  return new Response(upstream.body, {
    status: 200,
    headers: {
      'Content-Type': upstream.headers.get('content-type') ?? XLSX_CONTENT_TYPE,
      'Content-Disposition':
        upstream.headers.get('content-disposition') ??
        'attachment; filename="students.xlsx"',
    },
  });
}
