/*!
 * @kakao/gameplay-sdk v1.0.0-beta.3 — TypeScript declarations
 * Copyright (c) Kakao Corp.
 * https://gameplay-partners.kakao.com/api-sdk/sdk/gameplay-js/
 */
type GameplaySdkErrorCode = 'SDK_NOT_INITIALIZED' | 'INVALID_INPUT' | 'KAKAO_NOT_AVAILABLE' | 'KAKAO_SHARE_NOT_AVAILABLE' | 'AD_SDK_NOT_AVAILABLE' | 'AD_INSTANCE_NOT_AVAILABLE' | 'BRIDGE_NOT_AVAILABLE' | 'BRIDGE_METHOD_NOT_AVAILABLE';
/**
 * `Gameplay.talkShare()` 호출 시 전달하는 옵션. 카카오 메시지 템플릿을 통한 공유 흐름을 제어한다.
 *
 * - `templateId`는 필수. nullish인 경우 런타임에서 `INVALID_INPUT`으로 거부된다.
 * - `serverCallbackArgs`는 공유 웹훅으로 echo back 되는 식별 파라미터다. 생략하면 발송 결과
 *   집계와 보상 결과 콜백이 동작하지 않는다.
 * - 나머지 값은 검증 없이 `Kakao.Share.sendCustom` 으로 그대로 전달된다.
 */
type GameplayTalkShareOptions = {
    /** 카카오 메시지 템플릿 ID. nullish이면 런타임 에러. */
    templateId?: number | null;
    /** 카카오톡 미설치 사용자에게 설치 페이지를 노출할지 여부. */
    installTalk?: boolean;
    /** 템플릿 치환용 인자 (key/value 모두 문자열). */
    templateArgs?: Record<string, string>;
    /** 공유 웹훅으로 echo back 되는 식별 파라미터. */
    serverCallbackArgs?: GameplayShareServerCallbackArgs;
    /** 친구 피커 설정 (그룹/필터/한도 등). */
    pickerSettings?: {
        groupId?: number;
        args?: Record<string, unknown>;
        /** `customProps` 값은 숫자/불린도 그대로 전달할 수 있다 (예: `gameplay_template_id: 126533`). */
        logs?: {
            section: string;
            customProps: Record<string, string | number | boolean>;
        };
        limit?: number;
        type?: 'default' | 'chat' | 'friend';
    };
};
/**
 * 공유 소재 구분. 보상 여부는 이 값이 아니라 `HAS_REWARD` 가 나타낸다.
 *
 * 공유 피커 로그(`pickerSettings.logs.customProps.gameplay_message_type`)는 `REWARD_SHARE`
 * 까지 받지만, 서버 콜백은 두 값만 쓴다.
 */
type GameplayShareMessageType = 'GAME_SHARE' | 'RANKING_SHARE';
/**
 * 공유 웹훅으로 echo back 되는 식별 파라미터.
 *
 * 게임플레이 서버는 이 값으로 공유 컨텍스트를 복원해 파트너사 서버에 보상 결과를 통지한다.
 * 따라서 누락되면 발송 결과 집계와 보상 콜백이 모두 끊긴다.
 */
type GameplayShareServerCallbackArgs = {
    /** 파트너사 앱 유저 식별자 (공유 주체). */
    APP_USER_ID: number;
    /** 파트너사 식별자. 다중 게임 운영 시 라우팅에 쓰인다. */
    APP_ID: number;
    /** 파트너사 게임 식별자. 메타데이터의 `game_id` 와 같은 값. */
    GAME_TYPE: string;
    /** 공유 소재 구분. */
    MESSAGE_TYPE: GameplayShareMessageType;
    /** `true` 면 보상 있는 공유, `false` 면 단순 공유. */
    HAS_REWARD: boolean;
    /**
     * 파트너사가 공유 시점에 발급하는 트랜잭션 식별자(UUID).
     * 보상 결과 콜백의 매칭 키이자 파트너사 측 멱등성 키이므로 공유 1건당 고유해야 한다.
     * 보상 결과를 매칭할 필요가 없으면 생략할 수 있다.
     */
    SHARE_ID?: string;
};
type GameplayPhase = 'sandbox' | 'production';
/**
 * `Gameplay.init()` 호출 시 전달하는 부트스트랩 옵션.
 *
 * - `clientKey`가 없으면 Kakao JS SDK 로드를 건너뛴다. 이후 `talkShare` 호출은 `KAKAO_NOT_AVAILABLE`.
 * - 광고 SDK는 init 시점에 미리 로드한다. 첫 광고 노출을 앞당기기 위함이며,
 *   로드 결과는 init 성공 여부에 영향을 주지 않는다.
 *   광고를 쓰지 않는 게임은 `ad.preload: false` 로 끌 수 있다.
 * - `phase` 는 두 곳을 가른다 — Kakao JS SDK 의 스크립트 URL, 그리고 게임 로그의
 *   서비스 도메인과 deployment. 광고 SDK 와 Tiara 스크립트 주소는 `phase` 와 무관하다.
 */
/**
 * 게임 로그(Tiara) 설정. `init({tiara: {...}})` 로 준다.
 *
 * 여기 오는 것은 **로그에만 의미가 있는 값**이다. `gameCode` · `appId` · `accessToken` ·
 * `appUserId` 는 지금 로그에만 쓰이지만 게임·앱·사용자를 가리키는 식별값이라 최상위에
 * 둔다 — 광고의 `ctag.cp` 가 `gameCode` 와 같은 값이고, 공유 서버 콜백의 `APP_ID` 가
 * `appId` 다. 로그를 꺼도(`tiara: false`) 그 값들은 여전히 필요하다.
 */
type GameplayTiaraOptions = {
    /**
     * 광고 마케팅 정보 제공 동의 여부. 사용자가 고르는 값이라 동의·미동의가 모두 나온다.
     *
     * **모르면 주지 않는다.** 주지 않으면 SDK 가 `setThirdAdAgree` 를 부르지 않아 Tiara 의
     * 기본값(미설정)으로 남는다. 로그인 전이라 아직 모르는 상태를 `false`(미동의)로 적어
     * 보내면 동의한 사용자를 미동의로 기록하게 된다 — 모르는 것과 아니라고 답한 것은 다르다.
     *
     * 제3자 정보 제공 동의(`thirdProvideAgree`)는 옵션이 아니다. 게임플레이 진입 자체가 그
     * 동의를 필수로 거치므로 SDK 가 `true` 로 고정한다.
     */
    thirdAdAgree?: boolean;
};
type GameplayInitOptions = {
    /** Kakao JS SDK 초기화 키 (Kakao Developers 발급). 누락 시 Kakao 로드 skip. */
    clientKey?: string;
    /**
     * 카카오 계정 access token. Tiara 로그를 카카오 계정과 연결할 때 쓴다.
     *
     * 전사 Tiara Web SDK 가이드는 아래 **셋 중 하나**를 요구한다. 미세팅 로그는
     * 카카오 분석 환경에서 집계·활용할 수 없다.
     *
     *   1. `accessToken`
     *   2. `appUserId` + kakaoAppId
     *   3. `appUserId` + kakaoAppKey  ← 이 SDK 의 `clientKey` 가 그 값이다
     *
     * 이 SDK 에서는 `appId` 가 kakaoAppId 이고 `clientKey` 가 kakaoAppKey 다. `appId` 가
     * 필수 옵션이므로 보통은 `appUserId` 만 주면 조합 2가 성립한다.
     *
     * **optional 인 이유**: init 시점에 값을 모를 수 있다(로그인 전). 대신 조합이 하나도
     * 성립하지 않으면 sandbox 진단 경고를 낸다.
     *
     * Kakao Developers phase 중 Prod(Real) API 만 쓰므로 그 phase 의 토큰을 넣는다.
     */
    accessToken?: string;
    /**
     * 카카오 앱 사용자 ID. `accessToken` 대신 쓸 수 있다.
     *
     * **혼자서는 성립하지 않는다** — `appId`(= Tiara 의 kakaoAppId) 또는 `clientKey`
     * (= kakaoAppKey)와 짝이어야 한다. `appId` 가 필수라 보통은 그쪽이 짝이 된다.
     * 자세한 조합은 `accessToken` 주석 참고.
     */
    appUserId?: string;
    /**
     * 실행 환경. 기본 `'production'`.
     *
     * 두 곳을 가른다.
     *
     *   - Kakao JS SDK 의 스크립트 URL
     *   - 게임 로그의 서비스 도메인과 `deployment` — sandbox 로그가 production 통계에
     *     섞이지 않도록 나눈다
     *
     * 광고 SDK 와 Tiara 스크립트 주소는 이 값에 영향받지 않는다. 둘 다 이유가 있어
     * production 으로 고정돼 있다(`runtime/sdkUrls.ts`).
     */
    phase?: GameplayPhase;
    /**
     * 게임 이름. 닫기 버튼을 눌렀을 때 SDK 가 띄우는 이탈 확인 팝업의 제목으로 쓴다.
     *
     * 팝업 문구는 게임이 정하지 않으므로 이름을 미리 받아둔다. 필수인 이유는 제목 없는
     * 팝업이 파트너마다 다르게 보이는 것을 막기 위해서다. 빈 문자열을 주면 제목 없이
     * 본문만 나온다 — 이름을 지어낼 수는 없다.
     *
     * 이 값을 init 에서 받는 것이 맞는지는 다시 볼 여지가 있다. 톡이 이미 아는 값이라
     * 브리지에서 가져오는 편이 정확할 수 있다.
     */
    gameName: string;
    /**
     * 게임플레이 파트너센터에 등록한 게임 코드. 메타데이터 API 로 등록했다면 그 `code` 다.
     *
     * 게임 로그의 `section`(`sdk_ui_{gameCode}`)과 `page_meta.id` 에 쓴다. 둘 다 이 값이
     * 아니면 로그를 게임 단위로 구분할 수 없어 필수로 둔다. 게임 이름이나 공백은 쓸 수 없다.
     */
    gameCode: string;
    /**
     * 카카오디벨로퍼스 앱 ID.
     *
     * 게임 로그에서 `appUserId` 의 짝으로 쓴다 — 전사 Tiara 가이드의 조합 2
     * (`appUserId` + kakaoAppId)다. `accessToken` 을 주면 쓰이지 않는다.
     *
     * Tiara 는 문자열로 받지만 앱 ID 자체가 숫자라 숫자로 받는다. 파트너 가이드도 숫자다.
     */
    appId: number;
    /**
     * 게임 로그(Tiara) 자동 전송. 기본으로 켜져 있다.
     *
     * 파트너사가 직접 붙이던 것을 SDK 가 대신한다 — SDK 를 쓰면 아무것도 하지 않아도
     * 필수 게임 로그가 나가는 것이 목적이다. **이미 Tiara 를 직접 연동한 게임은
     * `tiara: false` 로 꺼야 한다.** 켠 채로 두면 같은 로그가 두 번 쌓인다.
     *
     * `false` 만 받게 두지 않는 이유는 계산해서 넘기는 경우다 — `tiara: !hasOwnTiara` 처럼
     * 값을 만들면 타입이 `boolean` 이라 `false` 자리에는 들어가지 않는다. `true` 는 기본값과
     * 같아 아무 일도 하지 않지만, 명시적으로 켜 두는 표기로 읽힌다.
     *
     * 정할 항목이 있으면 객체로 준다(`GameplayTiaraOptions`). 켜지는 것은 `true` 와 같고
     * 설정만 얹는 것이라, `tiara: true` 와 `tiara: {}` 는 결과가 같다.
     */
    tiara?: boolean | GameplayTiaraOptions;
    /** 광고 SDK 전역 설정. 여기서 정한 값이 모든 광고 인스턴스에 적용된다. */
    ad?: GameplayAdOptions;
    /** 웹뷰 초기 상태. init 이후 개별 메서드를 하나씩 부르지 않아도 되도록 선언형으로 받는다. */
    webview?: GameplayWebviewOptions;
    /** SDK managed UI 설정. 생략하면 navigation 이 기본 구성으로 표시된다. */
    ui?: GameplayManagedUIOptions;
};
/**
 * 게임별 매출 집계 키. 광고 노출을 어느 게임/채널에 귀속시킬지 결정한다.
 *
 * 애드핏 콘솔에서는 게임별 매출을 볼 수 없고, CP별 리포트 API 를 `cp` 기준으로 조회해야 한다.
 * 따라서 이 값이 없으면 해당 광고의 매출을 게임 단위로 추적할 수 없다.
 */
type GameplayAdCtag = {
    /** CPID. 메타데이터 API 에 등록한 게임 `code` 와 같은 값을 쓴다. */
    cp: string;
    /** 카카오가 발급한 채널 ID. */
    channel: string;
};
/**
 * 웹뷰 초기 상태 선언.
 *
 * 게임이 시작 시점에 잡아야 하는 웹뷰 상태를 한 곳에 모은다. init 이후 setter 를 하나씩
 * 부르는 것과 결과는 같지만, 게임이 요구하는 환경이 한눈에 보이고 호출 누락이 줄어든다.
 *
 * **지원하지 않는 항목은 조용히 건너뛴다.** 각 항목은 `isAvailable()` 로 먼저 확인한 뒤
 * 적용하므로, iOS 전용 설정을 Android 에서 지정해도 에러가 아니다 — 그 환경에서 의미가
 * 없을 뿐이다. 덕분에 파트너가 플랫폼 분기 없이 원하는 상태를 전부 적어둘 수 있다.
 *
 * 같은 이유로 이 설정들의 실패는 init 을 실패시키지 않는다. 부수 설정 하나 때문에 게임이
 * 시작조차 못 하는 편이 더 나쁘다. 실제 적용 여부가 중요하면 `isAvailable()` 로 미리 확인하거나
 * init 이후 개별 메서드를 직접 호출해 결과를 받는다.
 */
type GameplayWebviewOptions = {
    /** 웹뷰를 상태바 영역까지 확장 (전체화면). 톡 v26.5.0+. */
    statusBarOverlay?: boolean;
    /** 상태바 배경색 (`#RRGGBB`). 톡 v26.6.0+. */
    statusBarColor?: string;
    /**
     * 화면 방향. 게임은 보통 가로/세로 하나로 고정되므로 여기서 정한다.
     *
     * 대화면 기기(폴더블·태블릿) + Android 16 이상에서는 OS 가 방향 고정 요청을 무시한다.
     * 그 환경에서는 회전이 일어날 수 있으므로 레이아웃을 양쪽 모두 대응해 두는 편이 안전하다.
     */
    orientation?: 'portrait' | 'landscape';
    /**
     * 웹뷰 스크롤/오버스크롤 제스처. 톡 v26.7.0+.
     *
     * 연타형 게임에서 스크롤이 터치를 선점하는 것을 막으려면 `false`.
     * `false` 로 두면 바운스도 함께 사라지므로 `bouncesEnabled` 를 따로 줄 필요가 없다.
     */
    scrollEnabled?: boolean;
    /** 오버스크롤 바운스만 개별 제어. iOS 전용, 톡 v26.7.0+. */
    bouncesEnabled?: boolean;
    /** 당겨서 새로고침. iOS 전용, 톡 v26.7.0+. */
    pullToRefreshEnabled?: boolean;
    /** 링크 롱프레스 미리보기. iOS 전용, 톡 v26.7.0+. */
    linkPreviewEnabled?: boolean;
    /** 좌측 엣지 백 스와이프. iOS 전용. */
    backSwipeEnabled?: boolean;
};
/** 광고 SDK 전역 설정. */
type GameplayAdOptions = {
    /**
     * init 시점에 광고 SDK 를 미리 로드할지. 기본 `true`.
     *
     * 광고를 쓰지 않는 게임은 `false` 로 두어 불필요한 스크립트 수신을 피할 수 있다.
     * 끄더라도 광고 API 를 호출하면 그때 로드하므로 기능이 막히지는 않는다.
     */
    preload?: boolean;
};
/** 광고 인스턴스 생성 옵션. AdFit 의 두 번째 인자에 대응한다. */
type GameplayAdCreateOptions = {
    /** 발급받은 광고단위 ID. */
    adUnit: string;
    /**
     * 게임별 매출 집계 키.
     *
     * 지면 단위로 받는다. **여러 지면 중 한 곳에서 빠뜨리면 그 지면 매출만 조용히 누락된다.**
     * 에러가 나지 않으므로 발견이 늦다. 모든 생성 지점에 빠짐없이 넣을 것.
     */
    ctag?: GameplayAdCtag;
    /**
     * 광고가 노치·홈 인디케이터를 침범하지 않도록 전달하는 inset.
     *
     * 생략하면 {@link GameplaySdk.getSafeArea} 결과로 **자동으로 채운다.** 전면 광고는 화면
     * 전체를 덮으므로 inset 을 모르면 닫기 버튼이 가릴 수 있는데, 파트너가 놓치기 쉬운
     * 실수라 SDK 가 기본값을 넣는다. 브릿지가 없거나 조회에 실패하면 옵션 없이 생성한다.
     *
     * 직접 지정하면 그 값이 우선한다. 자동 채움 자체를 끄려면 `false`.
     */
    safeAreaInset?: GameplaySafeAreaInsets | false;
};
type GameplayManagedUIOptions = {
    /** SDK-owned UI root 를 붙일 container. 기본 document.body. */
    container?: HTMLElement;
    /**
     * 초기 navigation 구성. 표시 여부는 이 안의 `visible` 이 정한다.
     *
     * modal · toast · bottom sheet · loader 는 이 값과 무관하게 부른 시점에 뜬다. 여기서
     * 정하는 것은 navigation 하나다.
     */
    navigation?: GameplayNavigationControlsOptions;
};
type GameplayHapticMode = 'impact_light' | 'impact_medium' | 'impact_heavy' | 'success' | 'warning' | 'error' | 'custom';
type GameplayHapticOptions = {
    mode: GameplayHapticMode;
    /** `mode: 'custom'` 전용. 진동 패턴 배열(ms). */
    duration?: number[];
    /** `mode: 'custom'` 전용. 진동 강도 배열. 각 값 `0` ~ `255`. */
    intensity?: number[];
};
type TalkSoundState = {
    /** 기기 볼륨. `0.0` ~ `1.0`. */
    volume: number;
};
/** 네트워크 연결 상태. UPDATE 이벤트로는 오지 않으므로 필요할 때 직접 조회한다. */
type GameplayNetworkState = {
    isWifi: boolean;
    networkType: 'wifi' | 'cellular' | 'none' | 'unknown';
};
/** 실행 플랫폼. UA 로 판별하며, 판별 불가 시 `'unknown'`. */
type GameplayPlatform = 'ios' | 'android' | 'unknown';
/**
 * Android Back 입력 핸들러.
 *
 * `true` 를 반환하면 웹이 처리한 것으로 간주되어 앱 기본 뒤로가기가 실행되지 않는다.
 * `false` 를 반환하면 앱이 기본 뒤로가기를 진행한다.
 */
type GameplayBackClickHandler = () => boolean;
/**
 * 디바이스·호스트 판별 결과.
 *
 * `platform` 과 `isIOS` / `isAndroid` 는 항상 일치한다. 편의를 위한 중복이므로 어느 쪽을 써도 된다.
 */
type GameplayDeviceInfo = {
    platform: GameplayPlatform;
    /** iPadOS 13+ 포함. 해당 기기는 UA 에 `iPad` 토큰이 없어 터치 포인트로 판별한다. */
    isIOS: boolean;
    isAndroid: boolean;
    /**
     * `platform` 의 OS 버전. iOS 는 `18.6`, Android 는 `14` 형식이다.
     *
     * **`platform` 과 짝이 맞을 때만 값이 있다.** iPadOS 데스크톱모드는 UA 가 `Macintosh` 라
     * 실제 iOS 버전을 알 수 없어 `undefined` 다 — `platform` 이 `'ios'` 인데도 그렇다.
     * 그 상태에서 macOS 버전을 내면 존재하지 않는 "iOS 10.15" 가 된다.
     *
     * 기능 지원 여부 판단에는 쓰지 말 것. 그 목적에는 {@link GameplaySdk.isAvailable} 이 정확하다.
     */
    osVersion?: string;
    /**
     * 태블릿 여부. iPad(iPadOS 데스크톱모드 포함)와 Android 태블릿.
     *
     * 판정은 User-Agent 기준이며 **화면 크기를 보지 않는다.** 폴더블 폰을 펼치면 화면이
     * 태블릿만큼 넓어지지만 태블릿이 아니고, iPad 는 split view 에서 viewport 가 기기 크기와
     * 무관해진다. 레이아웃 분기가 필요하면 이 값과 CSS 미디어 쿼리를 함께 쓰는 것이 안전하다.
     */
    isTablet: boolean;
    /** 카카오톡 인앱 웹뷰(게임웹뷰 포함) 여부. */
    isKakaoTalk: boolean;
    /** 게임웹뷰(비즈 전용웹뷰) 여부. 일반 인앱브라우저와 구분된다. */
    isGameWebView: boolean;
    /**
     * 카카오톡 버전(semver). Android 는 UA 에 빌드번호로 실리며 semver 로 정규화된다.
     * 카카오톡이 아니거나 형식을 해석하지 못하면 `undefined`.
     *
     * 기능 지원 여부 판단에는 쓰지 말 것 — 패치·백포트 빌드에서 어긋난다.
     * 그 목적에는 실제 메서드 존재를 보는 {@link GameplaySdk.isAvailable} 이 정확하다.
     * 이 값은 "톡 업데이트 안내" UX, 분석 지표, 버그 리포트 용도다.
     */
    talkVersion?: string;
};
/** Safe Area Inset. 노치/홈 인디케이터를 피해 UI 를 배치할 때 쓴다. 단위 px. */
type GameplaySafeAreaInsets = {
    top: number;
    left: number;
    bottom: number;
    right: number;
};
/**
 * 가속도 센서 값. 단위 **m/s²**, W3C `DeviceMotionEvent.accelerationIncludingGravity` 스펙과 동일하다.
 *
 * 중력이 포함된 값이라 기기를 수평으로 놓으면 `z ≈ +9.81` 이다. iOS CoreMotion 의 기본값(G 단위,
 * Apple 컨벤션)은 호스트가 이 스펙으로 변환해 전달하므로 두 플랫폼에서 같은 값을 쓸 수 있다.
 */
type GameplayAccelerometerReading = {
    x: number;
    y: number;
    z: number;
};
type GameplayAccelerometerOptions = {
    /** 수집 주기(ms). 기본 `50`, 최소 `33`. 더 짧게 주어도 호스트가 최소값으로 올린다. */
    interval?: number;
};
/** 화면 방향. `'current'` 는 전환 없이 현재 방향만 조회한다. */
type GameplayTalkOrientationMode = 'portrait' | 'landscape' | 'current';
type GameplayTalkOrientationOptions = {
    mode: GameplayTalkOrientationMode;
};
type GameplayTalkOrientationResult = {
    value: 'portrait' | 'landscape';
};
type GameplayLifecycleEventMessage = {
    event: 'REQUEST' | 'RESPONSE' | 'UPDATE';
    type: 'LIFECYCLE';
    payload: {
        state: 'BACKGROUND' | 'FOREGROUND';
    };
};
type GameplaySafeAreaEventMessage = {
    event: 'REQUEST' | 'RESPONSE' | 'UPDATE';
    type: 'SAFE_AREA';
    payload: GameplaySafeAreaInsets;
};
type GameplaySoundStateEventMessage = {
    event: 'REQUEST' | 'RESPONSE' | 'UPDATE';
    type: 'SOUND_STATE';
    payload: TalkSoundState;
};
type GameplayAccelerometerEventMessage = {
    event: 'REQUEST' | 'RESPONSE' | 'UPDATE';
    type: 'ACCELEROMETER';
    payload: GameplayAccelerometerReading;
};
type GameplayUnknownTalkEventMessage = {
    event: 'REQUEST' | 'RESPONSE' | 'UPDATE';
    type: string;
    payload?: unknown;
};
type GameplayTalkEventMessage = GameplayLifecycleEventMessage | GameplaySafeAreaEventMessage | GameplaySoundStateEventMessage | GameplayAccelerometerEventMessage | GameplayUnknownTalkEventMessage;
type GameplayNativeEventHandler = (message: GameplayTalkEventMessage) => void;
/**
 * 광고 로딩 실패 콜백.
 *
 * `error` 는 현재 항상 `undefined` 다. 광고 SDK 가 실패 사유를 함께 주지 않기 때문이다.
 * 나중에 값이 실리면 그대로 전달되도록 인자 자리는 남겨 둔다.
 */
type GameplayAdErrorCallback = (error?: unknown) => void;
type GameplayAdCallback = () => void;
interface GameplayInterstitialAd {
    /** 노출하지 않고 미리 로딩만 한다. 노출 지연을 줄이려는 경우에만 필요하다. */
    load: () => void;
    /** 광고를 노출한다. `load()` 를 하지 않았으면 로딩부터 진행하므로 단독 호출해도 된다. */
    open: () => void;
    /** 사용자가 닫기 버튼을 누른 것과 동일하게 동작한다. */
    close: () => void;
    /** 광고를 강제로 닫고 제거한다. 이후 `load`/`open`/`close` 는 `AD_INSTANCE_NOT_AVAILABLE`. */
    destroy: () => void;
    onLoad: (callback: GameplayAdCallback) => void;
    offLoad: (callback: GameplayAdCallback) => void;
    /**
     * 광고가 실제로 노출된 시점.
     *
     * `open()` 은 노출 요청일 뿐 성사를 보장하지 않으므로, 노출 집계나
     * 게임 일시정지/BGM 음소거 처리는 이 콜백을 기준으로 해야 한다.
     */
    onOpen: (callback: GameplayAdCallback) => void;
    offOpen: (callback: GameplayAdCallback) => void;
    onClose: (callback: GameplayAdCallback) => void;
    offClose: (callback: GameplayAdCallback) => void;
    /** 광고 리소스가 해제된 시점. 정리 훅. */
    onUnload: (callback: GameplayAdCallback) => void;
    offUnload: (callback: GameplayAdCallback) => void;
    onError: (callback: GameplayAdErrorCallback) => void;
    offError: (callback: GameplayAdErrorCallback) => void;
}
interface GameplayRewardedInterstitialAd extends GameplayInterstitialAd {
    onReward: (callback: GameplayAdCallback) => void;
    offReward: (callback: GameplayAdCallback) => void;
}
/**
 * `isAvailable()` 로 지원 여부를 물어볼 수 있는 기능 이름.
 *
 * `version` 은 빠진다 — 호출이 아니라 번들에 박힌 값이라 "쓸 수 있는가" 를 물을 대상이 아니다.
 */
type GameplayCapability = Exclude<keyof GameplaySdk, 'version' | 'init' | 'isAvailable' | 'onTalkEventMessage' | 'UI'>;
/**
 * 버튼 색. Talk 디자인 시스템의 Square Button 두 종류에 대응한다.
 *
 * 어느 쪽이 강조인지는 화면마다 다르다. primary/secondary 라는 자리로 색을 정하지 않고
 * 버튼마다 고르게 둔다. 기본은 `gray` 라 값을 주지 않으면 지금 모습 그대로다.
 */
type GameplayUiActionColor = 'gray' | 'yellow';
/**
 * `TOptions` 는 이 버튼이 달린 오버레이의 옵션이다. `onClick` 이 그 오버레이의 핸들을 받으므로
 * 콜백 안에서 `handle.update(...)` / `handle.close()` 를 바로 부를 수 있다.
 *
 * 인자로 주는 이유는 `const sheet = open({onClick: () => sheet.update(...)})` 처럼 자기 자신을
 * 참조하는 선언을 강제하지 않기 위해서다. 그 형태는 시트를 연 곳과 콜백이 같은 스코프여야만
 * 성립한다. 인자를 쓰지 않는 콜백은 그대로 둬도 된다.
 */
type GameplayUiAction<TOptions = unknown> = {
    label: string;
    /** 기본 `gray`. (Figma `B. Gray / 01. Large`, `A. Yellow / 01. Large`) */
    color?: GameplayUiActionColor;
    onClick?: (handle: GameplayUiHandle<TOptions>) => Promise<void>;
};
/**
 * 열려 있는 오버레이의 조작 핸들.
 *
 * `TOptions` 는 그 오버레이가 받는 옵션이다. 기본값이 `unknown` 인 것은 타입 인자 없이
 * `GameplayUiHandle` 만 쓰던 기존 코드를 깨지 않기 위해서다.
 */
/**
 * 보조 버튼. 색을 받지 않는다 — 항상 gray 다.
 *
 * 강조는 화면당 하나여야 한다. 둘 다 노랑이면 어느 쪽을 눌러야 하는지 알 수 없다.
 * 규칙을 문서로만 두면 지켜지지 않으므로 타입에서 뺀다.
 */
type GameplayUiSecondaryAction<TOptions = unknown> = Omit<GameplayUiAction<TOptions>, 'color'>;
type GameplayUiHandle<TOptions = unknown> = {
    close(): void;
    destroy(): void;
    /** 부분 갱신. 주지 않은 필드는 현재 값이 그대로 남는다. */
    update(next: TOptions): void;
};
type GameplayToastId = string;
/**
 * 토스트가 붙는 쪽. 기본 `bottom`.
 *
 * 쌓이는 방향도 함께 뒤집힌다. `bottom` 은 아래에서 올라와 위로 쌓이고, `top` 은 위에서
 * 내려와 아래로 쌓인다.
 */
type GameplayToastPosition = 'top' | 'bottom';
type GameplayToastConfig = {
    /** 동시에 보이는 토스트 수. 기본 1, 최대 3. 더 큰 값을 주면 3으로 클램프한다. */
    maxVisible?: number;
    /**
     * 기본 `bottom`. 스택 전체의 설정이며 토스트마다 다르게 줄 수 없다.
     *
     * 방향이 섞이면 화면에 위아래로 갈린 두 무리가 남고, `maxVisible` 이 어느 쪽을 세는지
     * 알 수 없게 된다.
     *
     * 값을 바꾸면 지금 떠 있는 토스트는 이전 위치에서 닫힌다.
     */
    position?: GameplayToastPosition;
};
type GameplayToastOptions = {
    /** ms. 기본 5000, 최대 5000. */
    duration?: number;
};
type GameplayToastApi = {
    configure(options: GameplayToastConfig): void;
    show(message: string, options?: GameplayToastOptions): GameplayToastId;
    dismiss(toastId?: GameplayToastId): void;
};
/**
 * `auto` 는 내용이 높이를 정하고 최소·최대 범위로 clamp 된다. `full` 은 최대 높이다.
 * number 는 px 이며 역시 최소·최대로 clamp 된다.
 */
type GameplayBottomSheetHeight = 'auto' | 'full' | number;
/**
 * 본문 맨 위에 넣는 이미지. 크기 제한은 없고 폭을 넘으면 비율을 지킨 채 줄인다.
 *
 * 임의 DOM 이 아니라 URL 만 받는다. 보안 검수에서 iframe 과 임의 노드 삽입이 막혔고,
 * 이미지 한 장을 위해 그 제약을 풀 이유가 없다.
 */
type GameplayBottomSheetImage = {
    /** `https:`, `blob:`, `data:image/...` 만 허용한다. 그 외는 렌더하지 않는다. */
    src: string;
    /** 장식용이면 빈 문자열을 준다. */
    alt: string;
};
type GameplayBottomSheetOptions = {
    title?: string;
    dismissible?: boolean;
    /** 기본 `auto`. */
    height?: GameplayBottomSheetHeight;
    /** 기본 `false`. 본문 위에 스피너를 덮고 액션을 비활성한다. */
    loading?: boolean;
    /** 주면 본문 위에 이미지 영역이 생긴다. */
    image?: GameplayBottomSheetImage;
    /** 본문 텍스트. 자동 이스케이프되어 텍스트로 안전하게 렌더된다. */
    content?: string;
    primaryAction?: GameplayUiAction<GameplayBottomSheetOptions>;
    secondaryAction?: GameplayUiSecondaryAction<GameplayBottomSheetOptions>;
};
type GameplayBottomSheetApi = {
    open(options?: GameplayBottomSheetOptions): GameplayUiHandle<GameplayBottomSheetOptions>;
    close(): void;
};
type GameplayModalOptions = {
    title?: string;
    description?: string;
    primaryAction?: GameplayUiAction<GameplayModalOptions>;
    secondaryAction?: GameplayUiSecondaryAction<GameplayModalOptions>;
    dismissible?: boolean;
};
type GameplayConfirmOptions = Omit<GameplayModalOptions, 'primaryAction' | 'secondaryAction'> & {
    confirmLabel?: string;
    cancelLabel?: string;
    onConfirm?: (handle: GameplayUiHandle<GameplayModalOptions>) => Promise<void>;
    onCancel?: (handle: GameplayUiHandle<GameplayModalOptions>) => Promise<void>;
};
type GameplayModalApi = {
    open(options?: GameplayModalOptions): GameplayUiHandle<GameplayModalOptions>;
    confirm(options?: GameplayConfirmOptions): GameplayUiHandle<GameplayModalOptions>;
    close(): void;
};
/**
 * 로더는 항상 뒤쪽 탭을 막는다. 막지 않는 로더는 대기 중에도 같은 동작이 여러 번 들어갈
 * 수 있어, 옵션으로 두지 않는다.
 */
type GameplayLoaderOptions = {
    /** reserved; current UI does not render text. */
    label?: string;
    /** ms. 기본 10000, 최대 10000. */
    maxDuration?: number;
};
type GameplayLoaderApi = {
    show(options?: GameplayLoaderOptions): GameplayUiHandle<GameplayLoaderOptions>;
    hide(): void;
};
/**
 * 파트너에게 제공하는 내비게이션 옵션.
 *
 * 여기 없는 것은 의도적으로 뺀 것이다. 배치는 값이 하나뿐이라 고를 이유가 없고, 나가기
 * 버튼과 더보기는 끌 수 없다. 게임웹뷰를 벗어날 길과 공유·문의 동선은 SDK 가 책임지는
 * 최소 범위이기 때문이다.
 */
type GameplayNavigationControlsOptions = {
    /**
     * navigation 을 표시할지. 기본 `true`.
     *
     * 구성과는 **다른 축**이다. `{visible: false, dropdown: {items}}` 처럼 구성은 미리 해두고
     * 숨긴 채 시작할 수 있고, 나중에 {@link GameplayNavigationApi.show} 를 부르면 그 구성
     * 그대로 뜬다. 자체 UI 를 이미 가진 게임이 SDK 를 단계적으로 들일 때 쓰는 값이다.
     *
     * `update()` 는 이 값을 병합만 한다 — 숨긴 상태에서 다른 옵션을 고쳐도 뜨지 않는다.
     * 표시를 바꾸는 것은 이 값과 `show()` · `hide()` 뿐이다.
     *
     * ⚠️ **숨긴 동안에는 사용자가 게임웹뷰를 벗어날 방법이 없다.** 나가기 버튼이 이 안에
     * 있기 때문이다. 게임이 자체 종료 동선을 제공하거나, 잠깐 숨겼다면 반드시 되돌려야 한다.
     */
    visible?: boolean;
    /**
     * 내비게이션 바(44px) 배경색. `#RGB` / `#RRGGBB` / `#RRGGBBAA` 만 받는다.
     *
     * 기본값은 투명이다. 게임 화면이 상태바까지 올라오는 "확장" 배치에서는 버튼만 떠 있어야
     * 하기 때문이다. 색을 주면 상태바 아래 44px 띠가 그 색으로 채워지는 "비확장" 배치가 되며,
     * 상태바 색과 같은 값을 주면 시안처럼 위쪽이 한 덩어리로 보인다.
     *
     * 형식에 맞지 않는 값은 예외를 던지지 않고 무시한다. 색 하나 때문에 내비게이션이
     * 사라지는 편이 더 나쁘다.
     */
    backgroundColor?: string;
    /**
     * 나가기 버튼. 눌렀을 때의 흐름 전체를 SDK 가 갖는다.
     *
     *   탭 → `onClick` → 확인 팝업 → 확인/취소 → `onConfirm`/`onCancel` → 닫기/해제
     *
     * 게임은 각 지점에 할 일만 끼워 넣는다. 팝업을 띄우는 것도 닫는 것도 SDK 가 한다.
     * 팝업 문구는 바꿀 수 없다 — 제목은 `init` 의 `gameName`, 본문과 버튼은 고정이다.
     *
     * `onConfirm` 은 웹뷰를 닫기 **전에** 실행되고 끝날 때까지 기다린다. 닫고 나면 저장이나
     * 로그 전송을 할 기회가 없다.
     *
     * 버튼 자체는 숨길 수 없다. 게임웹뷰를 벗어날 길이 사라지기 때문이다.
     */
    exitAction?: {
        onClick?: () => Promise<void>;
        onConfirm?: () => Promise<void>;
        onCancel?: () => Promise<void>;
    };
    /**
     * iOS 플로팅 뷰 전환 버튼. 눌리면 SDK 가 `keepBrowser()` 를 부른다.
     *
     * 지원하지 않는 환경에서는 값을 주지 않아도 자동으로 숨는다. 게임이 톡 버전이나 플랫폼을
     * 확인해 분기할 필요가 없다. `false` 를 주면 지원 여부와 무관하게 숨긴다.
     */
    keepBrowserAction?: false | {
        onClick?: () => Promise<void>;
    };
    /**
     * 더보기 메뉴. 끌 수 없다 — `공유하기`, `문의하기` 두 항목은 항상 노출된다.
     *
     * 게임웹뷰에서 공유와 문의는 SDK 가 책임지는 최소 동선이다. 게임이 이 둘을 감추면
     * 사용자가 도달할 다른 경로가 없다. `items` 로 항목을 덧붙이는 것만 가능하다.
     */
    dropdown?: {
        items?: Array<GameplayNavigationDropdownItem>;
    };
    /**
     * 내비게이션 버튼을 **게임 영역 기준**으로 배치한다. 주지 않으면 지금처럼 기기 화면
     * 우측 상단이 기준이다.
     *
     * 태블릿처럼 가로가 긴 기기에서 게임이 가운데 영역만 쓰면 좌우에 레터박스가 생긴다.
     * 디자인 가이드가 레터박스 배경을 검은색(`#000000`)으로 규정하므로, 기기 우측에 붙은
     * 내비 버튼은 **검은 배경 위의 어두운 버튼**이 되어 눈에 띄지 않는다. 가이드가 "기기 기준
     * 우측 상단이 아닌 게임 영역 내 우측 상단" 을 요구하는 것이 이 때문이다.
     *
     * 게임 영역을 감싸는 요소를 그대로 넘기면 된다. 내비 바와 더보기 목록이 함께 그 안으로
     * 들어온다.
     *
     * ```js
     * Gameplay.init({ui: {navigation: {contentElement: document.querySelector('#stage')}}});
     * ```
     *
     * **숫자(폭)가 아니라 요소를 받는다.** 두 가지를 없애기 위해서다.
     *
     * - 단위 — 게임은 보통 캔버스 해상도로 폭을 생각하는데 그 값은 `devicePixelRatio` 만큼
     *   CSS 픽셀과 다르다. 요소를 재면 언제나 CSS 픽셀이라 이 질문이 사라진다.
     * - 낡음 — 가로 모드는 높이에 맞춰 만들라는 가이드에 따라 게임 폭은 뷰포트 높이에서
     *   파생된다. 회전하면 달라지므로 한 번 넘긴 숫자는 그 순간 틀린 값이 된다.
     *
     * 게임 영역이 화면만큼 넓어지면 안전영역 기준으로 자동으로 돌아간다. 좌우 여백이 다른
     * 레이아웃은 지원하지 않는다 — 가이드가 좌우 대칭 레터박스를 규정한다.
     *
     * **쓸 수 없는 값은 조용히 무시하고 기본 위치로 둔다.** 예외를 던지지 않는다 — 셀렉터
     * 오타로 `null` 이 오거나, 요소가 아직 DOM 에 붙지 않았거나, iframe 안에 있거나,
     * 화면 전환 중에 잠깐
     * `display: none` 이 되는 일은 흔하다. 그때 내비가 사라지면 사용자가 게임웹뷰를 벗어날
     * 길을 잃으므로, 배치를 포기할지언정 UI 는 유지한다.
     */
    contentElement?: Element | null;
};
/**
 * 더보기 항목이 자기 자신을 고치는 핸들. `onClick` 의 인자로 들어온다.
 *
 * `Navigation.updateDropdownItem(id, patch)` 와 같은 일을 하며, 자기 `id` 가 이미 묶여 있어
 * 콜백 안에서 다시 적지 않아도 된다.
 */
type GameplayNavigationDropdownItemHandle = {
    update(patch: GameplayNavigationDropdownItemPatch): void;
};
type GameplayNavigationDropdownItem = {
    id: string;
    label: string;
    /**
     * 비활성 여부. `Navigation.update` 나 `updateDropdownItem` 으로 런타임에 바뀐다.
     *
     * 선언 시점에만 정할 수 있으면 "보상을 다 받은 뒤 항목을 잠그는" 식의 처리를 할 수 없다.
     */
    disabled?: boolean;
    onClick?: (item: GameplayNavigationDropdownItemHandle) => Promise<void>;
};
type GameplayNavigationApi = {
    /**
     * 구성을 병합한다. **표시 여부는 건드리지 않는다** — 숨긴 상태에서 불러도 뜨지 않는다.
     *
     * 표시까지 함께 하려면 `update({visible: true, ...})` 이거나 {@link show} 다.
     */
    update(options?: GameplayNavigationControlsOptions): void;
    /**
     * navigation 을 표시한다. 구성은 지금까지 쌓인 값을 그대로 쓴다.
     *
     * `update({visible: true})` 와 같다. 짧고 의도가 드러나는 쪽을 함께 둔다.
     */
    show(): void;
    /**
     * navigation 을 숨긴다. 구성은 남아 있어 {@link show} 하면 그대로 돌아온다.
     *
     * ⚠️ 숨긴 동안에는 사용자가 게임웹뷰를 벗어날 방법이 없다. 게임이 자체 종료 동선을
     * 제공하거나, 연출 때문에 잠깐 숨겼다면 반드시 되돌려야 한다.
     */
    hide(): void;
    /**
     * 더보기 항목 하나만 고친다. 주지 않은 필드는 현재 값이 그대로 남는다.
     *
     * `update({dropdown: {items}})` 는 목록 전체를 다시 선언하는 것이라, 값 하나를 바꾸려고
     * 나머지 항목과 `label` 까지 다시 적어야 했다. 게임이 항목 목록을 자기 상태로 따로
     * 들고 있어야 하는 것도 그 때문이다 — SDK 가 이미 가진 것을 이중으로 관리하게 된다.
     *
     * 없는 `id` 를 주면 아무 일도 하지 않는다. 예외를 던지지 않는 이유는, 조건에 따라
     * 항목이 있을 수도 없을 수도 있는 화면에서 호출부가 매번 존재를 확인하게 만들지
     * 않기 위해서다.
     */
    updateDropdownItem(id: string, patch: GameplayNavigationDropdownItemPatch): void;
};
/** `id` 로 찾은 항목에 덮어쓸 값. 주지 않은 필드는 현재 값이 남는다. */
type GameplayNavigationDropdownItemPatch = Partial<Omit<GameplayNavigationDropdownItem, 'id'>>;
type GameplayUiFacade = {
    Toast: GameplayToastApi;
    BottomSheet: GameplayBottomSheetApi;
    Modal: GameplayModalApi;
    Loader: GameplayLoaderApi;
    Navigation: GameplayNavigationApi;
};
/**
 * Gameplay SDK 공개 API. Core capability 는 `Gameplay.<method>` 평면 API 로 호출하고,
 * managed UI 직접 호출은 `Gameplay.UI.<component>` facade 를 사용한다.
 *
 * Core capability 는 호출 전 `init()`이 완료되어야 한다. 미초기화 상태에서 호출 시 `SDK_NOT_INITIALIZED` 에러.
 */
interface GameplaySdk {
    /**
     * 이 번들의 버전. 예: `'1.0.0-beta.0'`.
     *
     * `init()` 전에도 읽을 수 있다. 로드된 스크립트에 박힌 값이라 호출이 필요 없다.
     *
     * **기능 지원 여부 판단에는 쓰지 말 것.** 같은 SDK 버전이라도 호스트 앱(카카오톡) 버전에
     * 따라 쓸 수 있는 기능이 다르다. 그 목적에는 {@link GameplaySdk.isAvailable} 이 정확하다.
     * 쓰임은 장애 대응이다. 문의가 들어왔을 때 어느 빌드가 로드됐는지 콘솔 한 줄로 특정한다.
     */
    readonly version: string;
    /**
     * SDK를 초기화한다. 페이지 라이프사이클당 1회 호출 권장. 동시 호출 시 동일 promise를 공유한다.
     */
    init: (options: GameplayInitOptions) => Promise<void>;
    /**
     * 카카오 메시지 템플릿으로 공유 시트를 띄운다. 내부적으로 `Kakao.Share.sendCustom`을 호출한다.
     *
     * @throws `INVALID_INPUT` — `templateId`가 nullish.
     * @throws `KAKAO_NOT_AVAILABLE` / `KAKAO_SHARE_NOT_AVAILABLE` — 카카오 SDK 미로드.
     */
    talkShare: (options: GameplayTalkShareOptions) => Promise<void>;
    /**
     * 현재 게임 웹뷰를 닫는다. 호스트 측에서 라우팅 처리.
     *
     * @throws `BRIDGE_NOT_AVAILABLE` — `window.kakaotalkGamePlay` 부재.
     * @throws `BRIDGE_METHOD_NOT_AVAILABLE` — `close` 메서드 부재.
     */
    close: () => Promise<void>;
    /**
     * iOS 게임 전용 웹뷰를 플로팅 뷰로 전환한다.
     *
     * @throws `BRIDGE_NOT_AVAILABLE` — `window.kakaotalkGamePlay` 부재.
     * @throws `BRIDGE_METHOD_NOT_AVAILABLE` — `keepBrowser` 메서드 부재.
     */
    keepBrowser: () => Promise<void>;
    /**
     * 햅틱 피드백을 트리거한다. iOS/Android 모두 지원.
     *
     * @throws `BRIDGE_NOT_AVAILABLE` — `window.kakaotalkGamePlay` 부재.
     * @throws `BRIDGE_METHOD_NOT_AVAILABLE` — `haptic` 메서드 부재.
     */
    haptic: (options: GameplayHapticOptions) => Promise<void>;
    /**
     * 호스트의 사운드 상태(volume 등)를 조회한다.
     *
     * @throws `BRIDGE_NOT_AVAILABLE` — `window.kakaotalkGamePlay` 부재.
     * @throws `BRIDGE_METHOD_NOT_AVAILABLE` — `getSoundState` 메서드 부재.
     */
    getSoundState: () => Promise<TalkSoundState>;
    /**
     * 네트워크 연결 상태(Wi-Fi/cellular/none)를 조회한다.
     *
     * @throws `BRIDGE_NOT_AVAILABLE` — `window.kakaotalkGamePlay` 부재.
     * @throws `BRIDGE_METHOD_NOT_AVAILABLE` — `getNetworkState` 메서드 부재.
     */
    getNetworkState: () => Promise<GameplayNetworkState>;
    /**
     * 디바이스 safe area inset(top/right/bottom/left)을 조회한다.
     *
     * @throws `BRIDGE_NOT_AVAILABLE` — `window.kakaotalkGamePlay` 부재.
     * @throws `BRIDGE_METHOD_NOT_AVAILABLE` — `getSafeArea` 메서드 부재.
     */
    getSafeArea: () => Promise<GameplaySafeAreaInsets>;
    /**
     * 화면 방향을 강제 전환한다. `current`는 호스트 기본값을 따른다.
     *
     * @throws `BRIDGE_NOT_AVAILABLE` — `window.kakaotalkGamePlay` 부재.
     * @throws `BRIDGE_METHOD_NOT_AVAILABLE` — `setOrientation` 메서드 부재.
     */
    setOrientation: (options: GameplayTalkOrientationOptions) => Promise<GameplayTalkOrientationResult>;
    /**
     * 좌측 백 스와이프(엣지 백 제스처) 제어. iOS 전용.
     *
     * Android 에서는 호스트가 이 메서드를 노출하지 않아 `BRIDGE_METHOD_NOT_AVAILABLE` 을 던진다(무시되지 않는다).
     * 호출 전 `isAvailable('setBackSwipeEnabled')` 로 확인한다.
     *
     * @throws `BRIDGE_NOT_AVAILABLE` — `window.kakaotalkGamePlay` 부재.
     * @throws `BRIDGE_METHOD_NOT_AVAILABLE` — `setBackSwipeEnabled` 메서드 부재.
     */
    setBackSwipeEnabled: (enabled: boolean) => Promise<void>;
    /**
     * WKWebView 내부 UIScrollView 의 스크롤 제스처 활성/비활성. iOS 전용.
     *
     * **iOS · Android 모두 지원** (카카오톡 v26.7.0+).
     *
     * 연타형 게임에서 스크롤 제스처가 터치 입력을 선점하는 문제를 막을 때 쓴다.
     * 터치 이벤트(touchstart/move/end)는 그대로 웹에 전달되므로 인게임 드래그에는 영향이 없다.
     *
     * - iOS — WKWebView UIScrollView 의 `scrollEnabled` / `bounces` 를 끈다.
     * - Android — 오버스크롤(바운스/글로우)을 제거하고 웹뷰 스크롤 이동을 억제한다.
     *
     * 구버전 카카오톡에서는 호스트가 노출하지 않으므로 호출 전 `isAvailable('setScrollEnabled')` 로 확인한다.
     *
     * @throws `BRIDGE_NOT_AVAILABLE` — `window.kakaotalkGamePlay` 부재.
     * @throws `BRIDGE_METHOD_NOT_AVAILABLE` — `setScrollEnabled` 메서드 부재.
     */
    setScrollEnabled: (enabled: boolean) => Promise<void>;
    /**
     * WKWebView 내부 UIScrollView 의 바운스(스크롤 끝 도달 시 튕김) 효과 활성/비활성. iOS 전용.
     *
     * Android 에서는 호스트가 이 메서드를 노출하지 않아 `BRIDGE_METHOD_NOT_AVAILABLE` 을 던진다(무시되지 않는다).
     * 호출 전 `isAvailable('setBouncesEnabled')` 로 확인한다.
     *
     * @throws `BRIDGE_NOT_AVAILABLE` — `window.kakaotalkGamePlay` 부재.
     * @throws `BRIDGE_METHOD_NOT_AVAILABLE` — `setBouncesEnabled` 메서드 부재.
     */
    setBouncesEnabled: (enabled: boolean) => Promise<void>;
    /**
     * 아래로 당겨 새로고침(Pull-to-Refresh) 제스처 활성/비활성. iOS 전용.
     *
     * Android 에서는 호스트가 이 메서드를 노출하지 않아 `BRIDGE_METHOD_NOT_AVAILABLE` 을 던진다(무시되지 않는다).
     * 호출 전 `isAvailable('setPullToRefreshEnabled')` 로 확인한다.
     *
     * @throws `BRIDGE_NOT_AVAILABLE` — `window.kakaotalkGamePlay` 부재.
     * @throws `BRIDGE_METHOD_NOT_AVAILABLE` — `setPullToRefreshEnabled` 메서드 부재.
     */
    setPullToRefreshEnabled: (enabled: boolean) => Promise<void>;
    /**
     * 3D Touch / Haptic Touch 링크 미리보기(allowsLinkPreview) 활성/비활성. iOS 전용.
     *
     * Android 에서는 호스트가 이 메서드를 노출하지 않아 `BRIDGE_METHOD_NOT_AVAILABLE` 을 던진다(무시되지 않는다).
     * 호출 전 `isAvailable('setLinkPreviewEnabled')` 로 확인한다.
     *
     * @throws `BRIDGE_NOT_AVAILABLE` — `window.kakaotalkGamePlay` 부재.
     * @throws `BRIDGE_METHOD_NOT_AVAILABLE` — `setLinkPreviewEnabled` 메서드 부재.
     */
    setLinkPreviewEnabled: (enabled: boolean) => Promise<void>;
    /**
     * 현재 웹뷰의 히스토리를 초기화하고 새 URL로 이동시킨다. Android 전용.
     *
     * **iOS · Android 모두 지원** (카카오톡 v26.5.0+).
     *
     * 게임 결과 페이지로 전환할 때 써서, 뒤로가기로 게임 페이지에 돌아가지 않도록 한다.
     * 결과 페이지를 먼저 로드한 뒤 다시 부를 필요 없이 이 호출 하나로 이동과 초기화가 함께 처리된다.
     *
     * 구버전 카카오톡에서는 호스트가 노출하지 않으므로 호출 전 `isAvailable('resetWebViewHistory')` 로 확인한다.
     *
     * @throws `INVALID_INPUT` — `url`이 http/https 스킴이 아님.
     * @throws `BRIDGE_NOT_AVAILABLE` — `window.kakaotalkGamePlay` 부재.
     * @throws `BRIDGE_METHOD_NOT_AVAILABLE` — `resetWebViewHistory` 메서드 부재.
     */
    resetWebViewHistory: (url: string) => Promise<void>;
    /**
     * 호스트 네이티브 이벤트 핸들러를 등록한다. 동일 이름에 다중 등록 가능(multimap dispatcher).
     *
     * `handlerName` 은 `window` 전역에 dispatcher 를 설치하므로 유효 식별자여야 하며,
     * 기존 전역/SDK/브라우저 API 를 덮어쓰는 예약 이름은 거부된다.
     *
     * @returns teardown 함수. 호출 시 자신만 해제하며, 마지막 핸들러 해제 시 dispatcher도 정리된다.
     * @throws `INVALID_INPUT` — `handlerName`이 식별자 형식이 아니거나 예약된 전역 이름.
     * @throws `BRIDGE_NOT_AVAILABLE` — `window.kakaotalkGamePlay` 부재.
     * @throws `BRIDGE_METHOD_NOT_AVAILABLE` — `registerNativeHandler` 메서드 부재.
     */
    registerNativeHandler: (handlerName: string, handler: GameplayNativeEventHandler) => Promise<() => void>;
    /**
     * `registerNativeHandler('onTalkEventMessage', …)` 축약형.
     *
     * @throws `registerNativeHandler` 와 동일.
     */
    onTalkEventMessage: (handler: GameplayNativeEventHandler, options?: {
        handlerName?: string;
    }) => Promise<() => void>;
    /**
     * 가속도계 스트림을 시작한다. `ACCELEROMETER` talk event로 데이터가 전달된다.
     *
     * @throws `BRIDGE_NOT_AVAILABLE` — `window.kakaotalkGamePlay` 부재.
     * @throws `BRIDGE_METHOD_NOT_AVAILABLE` — `startAccelerometer` 메서드 부재.
     */
    startAccelerometer: (options?: GameplayAccelerometerOptions) => Promise<void>;
    /**
     * 가속도계 스트림을 중단한다.
     *
     * @throws `BRIDGE_NOT_AVAILABLE` — `window.kakaotalkGamePlay` 부재.
     * @throws `BRIDGE_METHOD_NOT_AVAILABLE` — `stopAccelerometer` 메서드 부재.
     */
    stopAccelerometer: () => Promise<void>;
    /**
     * 호스트 네비게이션 백 스택에서 뒤로가기 가능 여부 조회. 플랫폼별 브릿지 자동 분기.
     *
     * @throws `BRIDGE_NOT_AVAILABLE` — iOS `window.kakaotalk` / Android `window.webview` 모두 부재.
     */
    canGoBack: () => Promise<boolean>;
    /**
     * 플랫폼 / 카카오톡 여부 / 게임웹뷰 여부 / 카카오톡 버전을 한 번에 조회.
     *
     * User-Agent 파싱만 하므로 네이티브 브릿지가 없어도 동작하며, 데스크톱 브라우저에서도
     * 결과를 반환한다(`platform: 'unknown'`). throw 하지 않는다.
     *
     * 기능 지원 여부 분기에는 {@link GameplaySdk.isAvailable} 을 쓸 것. 자세한 이유는
     * {@link GameplayDeviceInfo.talkVersion} 참고.
     */
    getDeviceInfo: () => Promise<GameplayDeviceInfo>;
    /**
     * Android 하드웨어/제스처 Back 입력을 웹이 가로챈다. **Android 전용.**
     *
     * 게임 내 레이어를 닫는 등 웹이 먼저 처리해야 할 때 쓴다. 핸들러가 `true` 를 반환하면
     * 앱 기본 뒤로가기가 실행되지 않는다.
     *
     * 브릿지 호출이 아니라 전역 슬롯(`window.onBackClick`) 할당이라 **슬롯이 하나뿐이다.**
     * 두 곳에서 등록하면 나중 것만 남는다. 해제하려면 `null` 을 넘긴다.
     *
     * iOS 의 엣지 백 스와이프 제어는 {@link GameplaySdk.setBackSwipeEnabled} 를 쓴다.
     */
    setBackClickHandler: (handler: GameplayBackClickHandler | null) => Promise<void>;
    /**
     * 웹뷰를 상태바 영역까지 확장하거나 기본 레이아웃으로 되돌린다 (전체화면 토글).
     * iOS · Android 모두 지원 (카카오톡 v26.5.0+).
     *
     * `init({statusBarOverlay})` 은 초기 상태만 정한다. 게임 진입/종료처럼 실행 중에
     * 전환해야 하면 이 메서드를 쓴다.
     *
     * 레이아웃이 바뀌면 새 인셋이 `SAFE_AREA` UPDATE 이벤트로 자동 전송된다.
     * 호출 직후 {@link GameplaySdk.getSafeArea} 를 다시 부르기보다 그 이벤트를 구독하는 편이 정확하다.
     *
     * @throws `BRIDGE_NOT_AVAILABLE` — `window.kakaotalkGamePlay` 부재.
     * @throws `BRIDGE_METHOD_NOT_AVAILABLE` — `setStatusBarOverlay` 메서드 부재.
     */
    setStatusBarOverlay: (enable: boolean) => Promise<void>;
    /**
     * 상태바 색상을 변경한다(`RRGGBB` 또는 nullish로 초기화). 현재 public API 는
     * `window.kakaotalkGamePlay.changeStatusBarColor` 를 우선 사용하고, 없으면
     * 기존 bridge 로 fallback 한다.
     *
     * @throws `INVALID_INPUT` — `'#RRGGBB'` 또는 `'RRGGBB'` 형식이 아님.
     * @throws `BRIDGE_NOT_AVAILABLE` — `window.kakaotalkGamePlay` / `window.kakaotalk` / `window.webview` 모두 부재.
     */
    changeStatusBarColor: (color?: string) => Promise<void>;
    /**
     * 전면 광고 인스턴스를 생성한다. open()으로 노출.
     *
     * @throws `AD_SDK_NOT_AVAILABLE` — Kakao AdFit SDK 를 로드/주입하지 못함.
     * @throws `AD_INSTANCE_NOT_AVAILABLE` — 광고 인스턴스 생성 실패, 또는 이미 `destroy()` 된 인스턴스에 `load`/`open`/`close` action 시도.
     */
    createInterstitialAd: (options: GameplayAdCreateOptions) => Promise<GameplayInterstitialAd>;
    /**
     * 보상형 전면 광고 인스턴스를 생성한다. onReward()로 보상 콜백 등록 후 open()으로 노출.
     *
     * @throws `AD_SDK_NOT_AVAILABLE` — Kakao AdFit SDK 를 로드/주입하지 못함.
     * @throws `AD_INSTANCE_NOT_AVAILABLE` — 광고 인스턴스 생성 실패, 또는 이미 `destroy()` 된 인스턴스에 `load`/`open`/`close` action 시도.
     */
    createRewardedInterstitialAd: (options: GameplayAdCreateOptions) => Promise<GameplayRewardedInterstitialAd>;
    /** 기능 사용 가능 여부 정적 점검(런타임 브릿지 존재 여부 기반). */
    isAvailable: (capability: GameplayCapability) => boolean;
    /** SDK managed UI component facade. 별도 `GameplayUI` 전역은 public contract 로 사용하지 않는다. */
    UI: GameplayUiFacade;
}
type GameplayMockHostLog = {
    channel: 'bridge' | 'kakao' | 'ad' | 'common';
    action: string;
    payload: unknown;
    timestamp: string;
};
interface GameplayMockHostController {
    clearLogs: () => void;
    emitTalkEventMessage: (message: GameplayTalkEventMessage) => void;
    getLogs: () => GameplayMockHostLog[];
    setSafeArea: (safeArea: GameplaySafeAreaInsets) => void;
    setNetworkState: (networkState: GameplayNetworkState) => void;
    teardown: () => void;
}

declare global {
    const Gameplay: GameplaySdk;
    interface Window {
        Gameplay: GameplaySdk;
    }
}

declare const createGameplaySdk: (targetWindowInput: Window) => GameplaySdk;
declare const gameplay: GameplaySdk;

interface GameplaySdkErrorOptions {
    /** 원본 에러(raw host/network 에러)를 보존하기 위한 cause. */
    cause?: unknown;
}
/**
 * SDK 전역 에러 타입. `code`로 분기하여 사용자 메시지를 결정한다.
 *
 * 에러 코드 표는 `docs/design/error-handling.md` 참고.
 *
 * @example
 * try { await gameplay.talkShare({templateId: 10001}); }
 * catch (error) {
 *   if (error instanceof GameplaySdkError && error.code === 'KAKAO_NOT_AVAILABLE') { ... }
 * }
 */
declare class GameplaySdkError extends Error {
    /** 안정적인 에러 식별자. UI 분기/로깅 키로 사용. */
    code: GameplaySdkErrorCode;
    constructor(code: GameplaySdkErrorCode, message: string, options?: GameplaySdkErrorOptions);
}

/** 호스트 측에서 capability 가 노출되는 플랫폼. */
type CapabilityPlatform = 'ios' | 'android';
/**
 * Capability 의 비-런타임 메타데이터. 문서 자동 생성 / IDE 도움말 / 변경 추적 용도이며,
 * `isAvailable` 의 런타임 판정 로직에는 사용되지 않는다 (raw global 존재 여부가 ground truth).
 */
interface FeatureMetadata {
    /** 한 줄 설명. 자동 생성 docs 의 capability 표 행에 사용. */
    description: string;
    /** 최초 도입된 SDK 버전. semver. */
    since: string;
    /** 호스트 측에서 제공되는 플랫폼. 미지정이면 양 플랫폼 모두. */
    platforms?: readonly CapabilityPlatform[];
    /**
     * 이 기능이 노출되는 최소 카카오톡 버전 (semver).
     *
     * 런타임 판정에는 쓰지 않는다 — `isAvailable` 은 실제 메서드 존재를 보며 그쪽이 정확하다.
     * 이 값은 `BRIDGE_METHOD_NOT_AVAILABLE` 에러에 "몇 버전부터 되는지" 를 덧붙이는 용도다.
     *
     * 게임웹뷰 브릿지를 쓰는 capability 는 최초 버전부터 있던 것이라도 값을 채운다
     * (`GAMEPLAY_WEBVIEW_BASELINE`). 생략은 "이 capability 는 버전 힌트를 낼 수 없다" 는 뜻이며,
     * 브릿지를 쓰지 않거나(`talkShare`, 광고, `getDeviceInfo`) 브릿지 부재 시 throw 하지 않고
     * 폴백하는 경우(`canGoBack`)에 한한다.
     */
    minTalkVersion?: string;
}
declare const featureNames: GameplayCapability[];
/** Capability 의 메타데이터 조회. docs 생성 / IDE 도움말 / 변경 추적 용도. */
declare const getCapabilityMetadata: (capability: GameplayCapability) => FeatureMetadata;

declare const installMockGameplayHost: (targetWindowInput: Window) => GameplayMockHostController;

export { GameplaySdkError, createGameplaySdk, featureNames, gameplay, getCapabilityMetadata, installMockGameplayHost };
export type { CapabilityPlatform, FeatureMetadata, GameplayAccelerometerEventMessage, GameplayAccelerometerOptions, GameplayAccelerometerReading, GameplayAdCallback, GameplayAdCreateOptions, GameplayAdCtag, GameplayAdErrorCallback, GameplayAdOptions, GameplayBackClickHandler, GameplayBottomSheetApi, GameplayBottomSheetHeight, GameplayBottomSheetImage, GameplayBottomSheetOptions, GameplayCapability, GameplayConfirmOptions, GameplayDeviceInfo, GameplayHapticMode, GameplayHapticOptions, GameplayInitOptions, GameplayInterstitialAd, GameplayLifecycleEventMessage, GameplayLoaderApi, GameplayLoaderOptions, GameplayManagedUIOptions, GameplayMockHostController, GameplayMockHostLog, GameplayModalApi, GameplayModalOptions, GameplayNativeEventHandler, GameplayNavigationApi, GameplayNavigationControlsOptions, GameplayNavigationDropdownItem, GameplayNavigationDropdownItemPatch, GameplayNetworkState, GameplayPhase, GameplayPlatform, GameplayRewardedInterstitialAd, GameplaySafeAreaEventMessage, GameplaySafeAreaInsets, GameplaySdk, GameplaySdkErrorCode, GameplaySdkErrorOptions, GameplayShareMessageType, GameplayShareServerCallbackArgs, GameplaySoundStateEventMessage, GameplayTalkEventMessage, GameplayTalkOrientationMode, GameplayTalkOrientationOptions, GameplayTalkOrientationResult, GameplayTalkShareOptions, GameplayTiaraOptions, GameplayToastApi, GameplayToastConfig, GameplayToastId, GameplayToastOptions, GameplayToastPosition, GameplayUiAction, GameplayUiActionColor, GameplayUiFacade, GameplayUiHandle, GameplayUiSecondaryAction, GameplayUnknownTalkEventMessage, GameplayWebviewOptions, TalkSoundState };
