#!/usr/bin/env python3
"""
Shared constants and tiny helpers for AI-driven App Store metadata automation.

Used by asc_metadata_detector.py, asc_metadata_applier.py, app_context_scanner.py
and their tests. Keep this file small and dependency-free -- it is the contract
between the read-side (detector), write-side (applier), and context scanner.
"""

from __future__ import annotations

import os
import sys


def warn(msg: str) -> None:
    """Emit a GitHub-Actions-formatted warning on stderr."""
    print(f"::warning::{msg}", file=sys.stderr)


def log(msg: str) -> None:
    """Emit a plain stderr log line (not parsed by GitHub Actions)."""
    print(msg, file=sys.stderr)


def require_env(name: str) -> str:
    """Return a non-empty env var or raise SystemExit with a GHA error line."""
    value = os.environ.get(name, "").strip()
    if not value:
        raise SystemExit(f"::error::{name} env var is required")
    return value


# Fields owned by appInfoLocalizations (the "app-level" metadata resource).
# These persist across version submissions -- editing them requires an
# editable appInfo state (see EDITABLE_APP_INFO_STATES below).
APP_LEVEL_FIELDS = ("name", "subtitle")

# Fields owned by appStoreVersionLocalizations (per-version metadata).
# Edited through the version slot resolved by manage_marketing_version.py.
VERSION_LEVEL_FIELDS = (
    "description",
    "keywords",
    "promotionalText",
    "whatsNew",
)

# URL-typed metadata fields, grouped by owning resource. Never generated by AI,
# never PATCHed by the applier, never reported by the detector as "empty".
APP_LEVEL_URL_FIELDS = ("privacyPolicyUrl", "privacyChoicesUrl")
VERSION_LEVEL_URL_FIELDS = ("supportUrl", "marketingUrl")

# Unioned skip set used by both detector and applier as a single gate.
SKIP_URL_FIELDS = frozenset(APP_LEVEL_URL_FIELDS + VERSION_LEVEL_URL_FIELDS)

# Full per-resource field lists (editable + URL) as fetched from the ASC API.
APP_INFO_LOC_FIELDS = APP_LEVEL_FIELDS + APP_LEVEL_URL_FIELDS
VERSION_LOC_FIELDS = VERSION_LEVEL_FIELDS + VERSION_LEVEL_URL_FIELDS


# Apple allows editing appInfoLocalizations only while the parent appInfo
# record is in one of these states. Outside of these, the applier must
# no-op (the API would reject the PATCH).
#
# Mirrors asc_common.EDITABLE_STATES but narrowed -- IN_REVIEW /
# WAITING_FOR_REVIEW lock the appInfo resource even though they permit
# some version-level edits.
EDITABLE_APP_INFO_STATES = frozenset({
    "PREPARE_FOR_SUBMISSION",
    "REJECTED",
    "METADATA_REJECTED",
    "DEVELOPER_REJECTED",
    "WAITING_FOR_REVIEW",
})


# App Store version states that accept PATCHes on appStoreVersionLocalizations
# (description, keywords, promotionalText, whatsNew). Outside of these, Apple's
# API returns 409 Conflict. Narrower than asc_common.EDITABLE_STATES -- we
# exclude IN_REVIEW and INVALID_BINARY which reject localization edits.
EDITABLE_VERSION_STATES = frozenset({
    "PREPARE_FOR_SUBMISSION",
    "DEVELOPER_REJECTED",
    "METADATA_REJECTED",
    "REJECTED",
    "WAITING_FOR_REVIEW",
    "READY_FOR_REVIEW",
})


# Per-field character caps enforced by Apple's App Store submission review.
# IMPORTANT: these are CHARACTER counts (len(str)) not BYTE counts. Apple's
# UI and API both measure by character, which matters for CJK locales
# where one character is often 3+ UTF-8 bytes. Validate with len(value).
CHAR_LIMITS = {
    "name": 30,
    "subtitle": 30,
    "keywords": 100,
    "description": 4000,
    "promotionalText": 170,
    "whatsNew": 4000,
}
