import { NextRequest, NextFetchEvent, NextResponse } from 'next/server';

declare const UTM_KEYS: readonly ["utm_source", "utm_medium", "utm_campaign", "utm_content", "utm_term"];
type UtmKey = (typeof UTM_KEYS)[number];
type UtmRecord = Partial<Record<UtmKey, string>>;
type ConsumerMiddleware = (req: NextRequest, event?: NextFetchEvent) => NextResponse | void | undefined | null | Promise<NextResponse | void | undefined | null>;

/**
 * utm-collector 백엔드로 보내는 페이로드.
 *
 * Lambda(`utm-collector`)의 입력 스키마와 정확히 매칭된다.
 * 스키마가 바뀌면 양쪽을 함께 갱신해야 한다.
 */
type UtmCapturePayload = {
    userId?: string;
    anonId: string;
    utm: UtmRecord;
    url: string;
    referrer?: string;
    capturedAt: string;
};

type WithUtmCaptureOptions = {
    /**
     * 백엔드 인증 토큰 (필수).
     *
     * 컨슈머가 자신의 시크릿 관리 방식대로 주입한다 — `process.env.XXX`,
     * secret manager 등. SDK는 토큰을 어디서 가져오는지 알지 못한다.
     *
     * 클라이언트 번들에 절대 노출되어선 안 된다 — server-only env 또는 server-only
     * 시크릿으로 관리할 것 (`NEXT_PUBLIC_` 접두사 금지).
     */
    token: string;
    /**
     * 인증된 user_id 추출. 미인증 요청은 anonId만 사용.
     * 동기 또는 비동기 모두 지원.
     */
    getUserId?: (req: NextRequest) => string | undefined | Promise<string | undefined>;
    /** 쿠키 도메인. default '.spartaclub.kr' */
    cookieDomain?: string;
    /** anon id 쿠키 이름. default 'sparta_anon_id' */
    anonCookieName?: string;
    /** anon id 쿠키 만료 (초). default 180일 */
    anonCookieMaxAge?: number;
};
/**
 * 미들웨어를 감싸서 UTM 캡처를 추가하는 HOF.
 *
 * 컨슈머의 미들웨어 결과(NextResponse / redirect / rewrite)를 그대로 보존하면서
 * anon 쿠키 set과 백엔드 영속화만 얹는다.
 *
 * - 컨슈머 미들웨어가 throw하면 그대로 throw (의도된 흐름 보장).
 * - UTM 캡처 단계 에러는 절대 throw하지 않음 — stderr 로그만 남기고 응답은 정상 반환.
 *
 * 컨슈머가 NextResponse를 반환하지 않으면 (void/null) `NextResponse.next()`로 fallback.
 * 기존 미들웨어가 없는 경우 `() => NextResponse.next()`를 넘기면 된다.
 *
 * 백엔드 호출은 `event.waitUntil`로 백그라운드에서 실행되므로 응답 TTFB에 영향이 없다.
 * `event`가 주입되지 않은 환경(테스트 등)에서는 await fallback으로 동작한다.
 * anon 쿠키 set은 첫 await 이전에 동기로 res에 박히므로, 백그라운드 실행이어도
 * 응답에 함께 실려나간다.
 *
 * 백엔드 endpoint는 SDK 내부에 hardcode되어 있다 — 컨슈머는 Lambda의 존재를 모른다.
 * 인증 토큰은 `opts.token`으로 주입한다 — 컨슈머가 자신의 시크릿 관리 방식대로
 * (`process.env.XXX`, secret manager 등) 결정한다.
 *
 * 토큰은 server-only로 관리할 것 — 클라이언트 번들에 노출되어선 안 된다.
 * Edge/Node 어느 런타임에서도 동작한다 — fetch + Web Crypto만 쓰므로 Node 전용 API 의존 없음.
 */
declare function withUtmCapture(middleware: ConsumerMiddleware, opts: WithUtmCaptureOptions): (req: NextRequest, event?: NextFetchEvent) => Promise<NextResponse>;

type WithUtmCookieOptions = {
    /** 쿠키 도메인. default '.spartaclub.kr' */
    cookieDomain?: string;
    /**
     * utm 쿠키 이름.
     * default 'utm_last' (브라우저 SDK의 setUTMCookies와 호환).
     */
    utmCookieName?: string;
    /** utm 쿠키 만료 (초). default 7일 */
    utmCookieMaxAge?: number;
};
/**
 * 미들웨어를 감싸 UTM 쿠키 SSR 발급을 추가하는 HOF.
 *
 * query string에 utm이 있을 때 1st-party 쿠키로 발급한다.
 * 백엔드 영속화나 식별자 발급은 하지 않는다.
 *
 * 기존 미들웨어가 없는 경우 `() => NextResponse.next()`를 넘기면 된다.
 *
 * 브라우저 측 `setUTMCookies`도 동일한 `utm_last` 쿠키를 발급한다.
 * SSR 응답에서 1차 발급(이 함수) → hydration 후 클라 SDK가 동일 키로 재발급.
 * 두 발급 경로 모두 raw JSON value를 사용해 정합성을 유지한다 — 백엔드가
 * 어느 쪽 발급의 쿠키를 받든 cookie-parser → JSON.parse로 동일하게 처리된다.
 */
declare function withUtmCookie(middleware: ConsumerMiddleware, opts?: WithUtmCookieOptions): (req: NextRequest, event?: NextFetchEvent) => Promise<NextResponse>;

export { type UtmCapturePayload, type UtmRecord, type WithUtmCaptureOptions, type WithUtmCookieOptions, withUtmCapture, withUtmCookie };
