from pypos.core.utils.config_utils import read_app_settings, read_endpoint_config


PENJUALAN_APP_KEYS = {
    "ppn_percent_default": "ppn_percent_default",
    "ppn_percent": "ppn_percent",
    # edited by glg
    "ppn_mode": "ppn_mode",
    # edited by glg
    "startup_ppn_mode_audit_enabled": "startup_ppn_mode_audit_enabled",
    "startup_ppn_mode_audit_sample_limit": "startup_ppn_mode_audit_sample_limit",
    "voucher_enabled_for_kasir": "voucher_enabled_for_kasir",
    "pembatalan_allowed_days": "pembatalan_allowed_days",
    "pembatalan_search_limit": "pembatalan_search_limit",
    "return_allowed_days": "return_allowed_days",
    "return_search_limit": "return_search_limit",
    "toko_id": "toko_id",
    "autocomplete_limit": "autocomplete_limit",
    "autocomplete_min_keyword": "autocomplete_min_keyword",
    "autocomplete_debounce_ms": "autocomplete_debounce_ms",
    # edited by glg
    "autocomplete_contains_min_keyword": "autocomplete_contains_min_keyword",
    "performance_profile_enabled": "performance_profile_enabled",
    # edited by glg
    "lookup_require_active_harga": "lookup_require_active_harga",
    # edited by glg
    "quick_price_sync_tables": "quick_price_sync_tables",
    # edited by glg
    "preorder_probe_timeout_sec": "preorder_probe_timeout_sec",
    "preorder_disable_cooldown_sec": "preorder_disable_cooldown_sec",
    # edited by glg
    "settlement_history_reprint_enabled": "settlement_history_reprint_enabled",
    "settlement_history_reprint_button_label": "settlement_history_reprint_button_label",
    # edited by glg
    "admin_qty_verify_threshold": "admin_qty_verify_threshold",
    # edited by glg
    # Mode strict validasi payload transaksi (opsional) untuk mencegah silent-drop data baris.
    "transaksi_payload_strict_mode": "transaksi_payload_strict_mode",
    # edited by glg
    # Batas jumlah log warning per transaksi saat ada baris payload invalid.
    "transaksi_payload_error_log_limit": "transaksi_payload_error_log_limit",
}

PENJUALAN_ENDPOINT_KEYS = {
    "api_base_url": "api_base_url",
    "request_timeout": "request_timeout",
    "ep_diskon_check_free_produk": "ep_diskon_check_free_produk",
    "ep_diskon_save_free_produk": "ep_diskon_save_free_produk",
    # edited by glg
    "ep_preorder_get": "ep_preorder_get",
    "ep_preorder_use": "ep_preorder_use",
    # edited by glg
    "ep_ppn_settings": "ep_ppn_settings",
}

PENJUALAN_DEFAULTS = {
    "ppn_percent_default": 11,
    "ppn_percent": 11,
    # edited by glg
    "ppn_mode": "include",
    # edited by glg
    "startup_ppn_mode_audit_enabled": 1,
    "startup_ppn_mode_audit_sample_limit": 5,
    "voucher_enabled_for_kasir": 0,
    "pembatalan_allowed_days": 0,
    "pembatalan_search_limit": 200,
    "return_allowed_days": 0,
    "return_search_limit": 200,
    "request_timeout": 10,
    "autocomplete_limit": 50,
    "autocomplete_min_keyword": 1,
    "autocomplete_debounce_ms": 220,
    # edited by glg
    # Ambang minimal karakter untuk fallback pencarian contains (LIKE %keyword%).
    # Nilai kecil memudahkan kasir, tetapi terlalu kecil bisa menambah beban query.
    "autocomplete_contains_min_keyword": 2,
    "performance_profile_enabled": 0,
    # edited by glg
    # Jika aktif, lookup barang (barcode/nama/autocomplete) hanya mengizinkan produk
    # yang memiliki harga_list aktif > 0 agar kasir tidak memilih item tanpa harga.
    "lookup_require_active_harga": 1,
    # edited by glg
    # Tabel default untuk sinkron cepat dari popup harga belum diatur.
    "quick_price_sync_tables": ["price"],
    # edited by glg
    # Timeout probe preorder API saat buka dialog F10 (agar UI tetap responsif).
    "preorder_probe_timeout_sec": 1,
    # Cooldown skip request preorder API jika endpoint unavailable/error.
    "preorder_disable_cooldown_sec": 120,
    # edited by glg
    # Fitur print ulang nota settlement dari tabel history settlement.
    "settlement_history_reprint_enabled": 1,
    "settlement_history_reprint_button_label": "Print Ulang Nota Settlement",
    # edited by glg
    # Ambang qty besar yang mewajibkan verifikasi admin settlement.
    "admin_qty_verify_threshold": 99,
    # edited by glg
    # 0 = kompatibel legacy (skip baris invalid + warning terbatas), 1 = strict fail-fast.
    # edited by glg
    # Default dinaikkan ke strict untuk mencegah silent row-drop saat simpan transaksi.
    "transaksi_payload_strict_mode": 1,
    # edited by glg
    # Guard observability: log terbatas per transaksi agar tidak flood di skala outlet besar.
    "transaksi_payload_error_log_limit": 20,
}


def _as_int(value, default, min_value=None):
    try:
        parsed = int(value)
    except Exception:
        parsed = int(default)
    if min_value is not None and parsed < min_value:
        return int(default)
    return parsed


# edited by glg
def _normalize_table_list(raw_tables):
    if isinstance(raw_tables, str):
        values = [v.strip() for v in raw_tables.split(",")]
    elif isinstance(raw_tables, (list, tuple, set)):
        values = [str(v).strip() for v in raw_tables]
    else:
        values = []
    normalized = []
    seen = set()
    for value in values:
        if not value:
            continue
        if value in seen:
            continue
        seen.add(value)
        normalized.append(value)
    return normalized


def get_penjualan_app_config():
    cfg = dict(PENJUALAN_DEFAULTS)
    cfg.update(read_app_settings() or {})
    return cfg


def get_penjualan_endpoint_config():
    cfg = dict(PENJUALAN_DEFAULTS)
    cfg.update(read_endpoint_config() or {})
    return cfg


def get_ppn_percent_default(default=11):
    cfg = get_penjualan_app_config()
    return _as_int(
        cfg.get(PENJUALAN_APP_KEYS["ppn_percent_default"]),
        default,
        min_value=0,
    )


def get_ppn_percent_override(default=11):
    cfg = get_penjualan_app_config()
    raw_value = cfg.get(PENJUALAN_APP_KEYS["ppn_percent"])
    if raw_value in (None, "", "None"):
        return None
    return _as_int(raw_value, default, min_value=0)


def get_ppn_percent_for_view(default=11):
    override = get_ppn_percent_override(default=default)
    if override is not None:
        return int(override)
    return int(get_ppn_percent_default(default=default))


# edited by glg
def normalize_ppn_mode(value, default="include"):
    mode = str(value or "").strip().lower()
    if mode in {"include", "exclude"}:
        return mode
    fallback = str(default or "include").strip().lower()
    return fallback if fallback in {"include", "exclude"} else "include"


# edited by glg
def get_ppn_mode(default="include"):
    cfg = get_penjualan_app_config()
    raw_mode = cfg.get(PENJUALAN_APP_KEYS["ppn_mode"], default)
    return normalize_ppn_mode(raw_mode, default=default)


# edited by glg
def is_ppn_include_mode(default="include"):
    return get_ppn_mode(default=default) == "include"


# edited by glg
def is_startup_ppn_mode_audit_enabled():
    cfg = get_penjualan_app_config()
    return _as_int(
        cfg.get(PENJUALAN_APP_KEYS["startup_ppn_mode_audit_enabled"]),
        PENJUALAN_DEFAULTS["startup_ppn_mode_audit_enabled"],
        min_value=0,
    ) == 1


# edited by glg
def get_startup_ppn_mode_audit_sample_limit(default=5):
    cfg = get_penjualan_app_config()
    value = _as_int(
        cfg.get(PENJUALAN_APP_KEYS["startup_ppn_mode_audit_sample_limit"]),
        default,
        min_value=0,
    )
    return max(0, min(50, value))


def is_voucher_enabled_for_kasir():
    cfg = get_penjualan_app_config()
    return _as_int(
        cfg.get(PENJUALAN_APP_KEYS["voucher_enabled_for_kasir"]),
        PENJUALAN_DEFAULTS["voucher_enabled_for_kasir"],
        min_value=0,
    ) == 1


def get_pembatalan_allowed_days(default=0):
    cfg = get_penjualan_app_config()
    value = _as_int(
        cfg.get(PENJUALAN_APP_KEYS["pembatalan_allowed_days"]),
        default,
        min_value=0,
    )
    return max(0, value)


def get_pembatalan_search_limit(default=200):
    cfg = get_penjualan_app_config()
    value = _as_int(
        cfg.get(PENJUALAN_APP_KEYS["pembatalan_search_limit"]),
        default,
        min_value=1,
    )
    return max(1, value)


def get_return_allowed_days(default=0):
    cfg = get_penjualan_app_config()
    value = _as_int(
        cfg.get(PENJUALAN_APP_KEYS["return_allowed_days"]),
        default,
        min_value=0,
    )
    return max(0, value)


def get_return_search_limit(default=200):
    cfg = get_penjualan_app_config()
    value = _as_int(
        cfg.get(PENJUALAN_APP_KEYS["return_search_limit"]),
        default,
        min_value=1,
    )
    return max(1, value)


def get_request_timeout(default=10):
    cfg = get_penjualan_endpoint_config()
    return _as_int(
        cfg.get(PENJUALAN_ENDPOINT_KEYS["request_timeout"]),
        default,
        min_value=1,
    )


def build_endpoint_url(path_key, endpoint_cfg=None):
    cfg = endpoint_cfg if isinstance(endpoint_cfg, dict) else get_penjualan_endpoint_config()
    base_url = str(cfg.get(PENJUALAN_ENDPOINT_KEYS["api_base_url"]) or "").rstrip("/")
    path = str(cfg.get(path_key) or "").strip()
    if path and not path.startswith("/"):
        path = "/" + path
    if not base_url:
        return "", "missing_api_base_url"
    if not path:
        return "", f"missing_{path_key}"
    return f"{base_url}{path}", ""


def get_toko_id_from_config(default=None):
    cfg = get_penjualan_app_config()
    raw_value = cfg.get(PENJUALAN_APP_KEYS["toko_id"], default)
    try:
        parsed = int(raw_value)
    except Exception:
        return default
    if parsed <= 0:
        return default
    return parsed


def get_autocomplete_limit(default=50):
    cfg = get_penjualan_app_config()
    value = _as_int(
        cfg.get(PENJUALAN_APP_KEYS["autocomplete_limit"]),
        default,
        min_value=5,
    )
    return max(5, value)


def get_autocomplete_min_keyword(default=1):
    cfg = get_penjualan_app_config()
    value = _as_int(
        cfg.get(PENJUALAN_APP_KEYS["autocomplete_min_keyword"]),
        default,
        min_value=0,
    )
    return max(0, value)


def get_autocomplete_debounce_ms(default=220):
    cfg = get_penjualan_app_config()
    value = _as_int(
        cfg.get(PENJUALAN_APP_KEYS["autocomplete_debounce_ms"]),
        default,
        min_value=50,
    )
    return max(50, value)


# edited by glg
def get_autocomplete_contains_min_keyword(default=2):
    cfg = get_penjualan_app_config()
    value = _as_int(
        cfg.get(PENJUALAN_APP_KEYS["autocomplete_contains_min_keyword"]),
        default,
        min_value=1,
    )
    return max(1, min(5, value))


# edited by glg
def get_admin_qty_verify_threshold(default=99):
    cfg = get_penjualan_app_config()
    value = _as_int(
        cfg.get(PENJUALAN_APP_KEYS["admin_qty_verify_threshold"]),
        default,
        min_value=1,
    )
    return max(1, value)


# edited by glg
def is_transaksi_payload_strict_mode_enabled(default=0):
    cfg = get_penjualan_app_config()
    return _as_int(
        cfg.get(PENJUALAN_APP_KEYS["transaksi_payload_strict_mode"]),
        default,
        min_value=0,
    ) == 1


# edited by glg
def get_transaksi_payload_error_log_limit(default=20):
    cfg = get_penjualan_app_config()
    value = _as_int(
        cfg.get(PENJUALAN_APP_KEYS["transaksi_payload_error_log_limit"]),
        default,
        min_value=1,
    )
    return max(1, min(200, value))


def is_performance_profile_enabled():
    cfg = get_penjualan_app_config()
    return _as_int(
        cfg.get(PENJUALAN_APP_KEYS["performance_profile_enabled"]),
        PENJUALAN_DEFAULTS["performance_profile_enabled"],
        min_value=0,
    ) == 1


# edited by glg
def is_lookup_require_active_harga_enabled(default=1):
    cfg = get_penjualan_app_config()
    return _as_int(
        cfg.get(PENJUALAN_APP_KEYS["lookup_require_active_harga"]),
        default,
        min_value=0,
    ) == 1


# edited by glg
def get_quick_price_sync_tables(default=None):
    fallback = _normalize_table_list(default or PENJUALAN_DEFAULTS.get("quick_price_sync_tables") or ["price"])
    cfg = get_penjualan_app_config()
    raw_value = cfg.get(PENJUALAN_APP_KEYS["quick_price_sync_tables"])
    tables = _normalize_table_list(raw_value)
    return tables or fallback or ["price"]


# edited by glg
def get_preorder_probe_timeout_sec(default=1):
    cfg = get_penjualan_app_config()
    value = _as_int(
        cfg.get(PENJUALAN_APP_KEYS["preorder_probe_timeout_sec"]),
        default,
        min_value=1,
    )
    return max(1, min(5, value))


# edited by glg
def get_preorder_disable_cooldown_sec(default=120):
    cfg = get_penjualan_app_config()
    value = _as_int(
        cfg.get(PENJUALAN_APP_KEYS["preorder_disable_cooldown_sec"]),
        default,
        min_value=5,
    )
    return max(5, min(600, value))


# edited by glg
def is_settlement_history_reprint_enabled(default=1):
    cfg = get_penjualan_app_config()
    return _as_int(
        cfg.get(PENJUALAN_APP_KEYS["settlement_history_reprint_enabled"]),
        default,
        min_value=0,
    ) == 1


# edited by glg
def get_settlement_history_reprint_button_label(default="Print Ulang Nota Settlement"):
    cfg = get_penjualan_app_config()
    raw = cfg.get(PENJUALAN_APP_KEYS["settlement_history_reprint_button_label"], default)
    text = str(raw or "").strip()
    if text:
        return text
    return str(default or "Print Ulang Nota Settlement")
