"""
Centralized Global Payments field-name/casing gotchas — PRD-HWONB-001 §13.1.

Every step-PRD developer adds their own section below as they discover a
GP-specific quirk (wrong-cased enum, renamed field, unexpected shape) rather
than hardcoding it inline in their section's code. Keeping these in one
module makes it possible to grep the whole "GP is weird about X" surface
area in one place.

Only constants/dicts live here — no request-building logic. Request bodies
are assembled in each section's own schema-to-payload mapper
(services.py::save_section or its per-section helper).
"""

from __future__ import annotations

# ---------------------------------------------------------------------------
# Cross-cutting (confirmed via CERT, PRD-HWONB-001 §13.1)
# ---------------------------------------------------------------------------

# C-09 — GP's own masterRecord/country lookup returns ISO-3; both "US" and
# "USA" are accepted on POST, but send "USA" to match master data.
COUNTRY_CODE_DEFAULT = "USA"  # NOT "US"

# C-20 — the accepted docType enum is Title-case "Tax", not "TAX". Full
# 15-code list confirmed verbatim via CERT (matches merchant_onboarding_documents.doc_type):
DOC_TYPE_CHOICES = (
    "SMA", "FS", "BL", "SS", "PB", "BR", "PS", "MM", "FD",
    "Tax",  # NOT "TAX"
    "NP", "ACH", "PC", "CH", "VITL", "OTHER",
)

# C-21 — attachment upload multipart field key is "documents", not "file".
ATTACHMENT_UPLOAD_FIELD_KEY = "documents"

# C-22 — attachment id field returned by list/upload/delete is "attachmentId",
# not "docId" (int, 1-based).
ATTACHMENT_ID_FIELD = "attachmentId"

# PRD-HWONB-011 §1.1 / C-03 — processingType is Title-case as returned by the
# lookup ("Retail", "MOTO", "Voice") — send the lookup's own casing back, do
# not re-case it.
PROCESSING_TYPE_CASING_NOTE = (
    "Send processingType exactly as returned by the fee-processing-detail "
    "lookup (Title case: Retail/MOTO/Voice) — do not upper/lower-case it."
)

# C-16 — Products/Equipment: "integrationProductID" is the field's LOCAL/
# internal name (matching the integrated-product catalog's own field name
# and the UI form state) — used for `schemas.products.IntegratedProduct`
# and everywhere HubWallet stores/reads this value. It is NOT the name GP
# actually accepts on the wire: a 2026-07-16 CERT trial confirmed GP
# requires "integrationSwId" for Integrated create, and
# `services._save_products_section` renames the key accordingly just before
# the outbound GP call. The two newer WebPass/Genius product schemas name
# the field `integrationSwId` directly from the start (the guide is
# unambiguous for those two), so no equivalent rename is needed there.
INTEGRATED_PRODUCT_ID_FIELD = "integrationProductID"

# C-17 — "source": "STR" is invalid on product create; omit it from the UI.
# Valid source values per the catalog set:
PRODUCT_SOURCE_CHOICES = ("EE", "MO", "PN", "PRF", "PO", "RTL", "TFD")

# C-18 — trainingAndActivationDetail accepts word values, not short codes
# (an earlier PRD draft assumed AGT/LEG/OTH short codes — wrong). Also note:
# the GET lookup returns `equipmentShippedTo` (no camelCase on "shipped"),
# `merchantTrainedBy`, `welcomeKitEmailedTo` — these key NAMES differ from
# the write fields (`equipShippedTo`, `merTrainedBy`, `welcomeKitEmailTo`),
# but the word VALUES are identical. Don't let the GET casing leak into the
# write payload.
TRAINING_ACTIVATION_VALUE_CHOICES = ("DBA", "Legal", "Agent", "Other", "NA")
TRAINING_MERCHANT_TRAINED_BY_CHOICES = ("TransFirst", "Agent")

# PRD-HWONB-009 §2 redesign (2026-07-16) — the guessed separate EmvReader
# catalog (formerly probed via `get_emv_reader_catalog()`, an unconfirmed GP
# path with no CERT-verified existence) is retired. GP's *filtered*
# `/product/standalone/equipment` lookup (requires brand+model+industry+
# application+source together) returns an `emv[]` array per device — the
# actual, CERT-confirmed, per-terminal set of valid pinpadEmvReader
# brand/model/sourceBillTo combinations. There is no longer a static
# brand/model fallback list for this field: the filtered lookup is the only
# source of truth, and the Products & Equipment UI hides the pinpad section
# entirely when a device's `emv[]` is empty rather than offering values that
# may not apply to the selected terminal.

# PRD-HWONB-009 §2 redesign (2026-07-16) — GP's filtered standaloneEquipment
# lookup returns `communicationWithPOS`: a list of descriptive connection
# labels per device (e.g. "IP/SSL", "WiFi"), not the single-letter codes
# `Create Product` requires (B/D/Q/G/I/N/S/U/F/W). This maps GP's own labels
# to those codes so the UI can offer only the connectionMethod(s) valid for
# the selected terminal instead of the previous full 10-value static list.
#
# CERT-confirmed labels (live probe, 2026-07-16, `cert_probe.py`
# `probe_equipment_catalog_matrix` against TEST833/086386): "IP/SSL" (INGENICO
# DESK5000/DESK3500 — the exact device from the live-failure report),
# "WiFi"+"GPRS" (INGENICO MOVE5000), "BT"+"WiFi"+"GPRS" (INGENICO MOVE5000
# w/Base). Confirms the Developer Guide's letter-code meanings
# (I=IP/SSL, F=WiFi, G=GPRS) but NOT its label spelling — the guide never
# actually spells out "BT" vs "Bluetooth"; live GP uses "BT". The remaining
# entries (Dial/Ethernet/Serial/USB/N-A/Wireless) were never observed in this
# probe sweep and are seeded from the Developer Guide's code definitions only
# — unconfirmed. An unrecognized label must never be guessed at: the client
# leaves it out of the derived `connectionMethods` list entirely (see
# `base_boarding_client.get_standalone_equipment_catalog`), which degrades to
# "option not offered" rather than risking a wrong submission.
CONNECTION_METHOD_LABEL_TO_CODE = {
    "IP/SSL": "I",  # confirmed
    "IP": "I",
    "WiFi": "F",  # confirmed
    "GPRS": "G",  # confirmed
    "BT": "B",  # confirmed
    "Dial": "D",  # unconfirmed — guide only
    "Ethernet": "Q",  # unconfirmed — guide only
    "Serial": "S",  # unconfirmed — guide only
    "USB": "U",  # unconfirmed — guide only
    "N/A": "N",  # unconfirmed — guide only
    "Wireless": "W",  # unconfirmed — guide only
}

# C-04 — plans/add-ons catalog path is NOT PascalCase; genius add-ons are
# nested inside this same response, there is no separate endpoint.
PLANS_AND_ADDONS_PATH = "/feeSchedule/plans/addons"  # NOT "/feeSchedule/PlansAndAddOns"

# C-23 — the "max 3 bank accounts" cap is a real, CERT-confirmed GP rule
# (an earlier PRD draft treated it as an unconfirmed prototype carry-over).
MAX_BANK_ACCOUNTS = 3

# N-2 — section save sequencing gate: GP rejects processingInformation,
# addresses, owners, and cardTypes if `business` hasn't been pushed first;
# cardTypes additionally requires processingInformation. `accounts` is
# independent of this ordering. Each section's save handler should check
# this before pushing, to surface a clean local error rather than a raw GP
# 400. Keys are the section that has a prerequisite; values are the
# prerequisite section(s), in required order.
SECTION_SEQUENCING_PREREQUISITES = {
    "processing": ["business"],
    "addresses": ["business"],
    "owners": ["business"],
    "card_types": ["business", "processing"],
    # PRD-HWONB-015 — this is NOT a GP-native rejection (GP has no concept of
    # a "fees" prerequisite of its own); it's HubWallet's own wizard ordering.
    # Association is now captured upfront in Application Setup (before
    # Business Information even), so Fees' rate-card resolution
    # (base_boarding_client.push_fees/_resolve_rate_card) is guaranteed a
    # valid `application.tsys_association` by the time the merchant reaches
    # Training & Activation — no separate Pricing Tier gate is needed anymore
    # (PRD-HWONB-014's Pricing Tier step is retired, superseded by PRD-015).
    # `business` is ALSO required here (re-checked live, not just at time of
    # first push): the Monthly Service Fee's seasonal month flags mirror
    # `application.business_data["additionalInfo"]` (base_boarding_client.py
    # `_build_misc_fee_entry`), and that snapshot is written to Postgres
    # BEFORE its GP push and never rolled back if that push later fails —
    # so a merchant who edits Business again and has THAT resubmit fail
    # would otherwise reach Fees with a business snapshot GP never accepted,
    # producing GP's "seasonal fee ... must match ... business configuration
    # page" rejection instead of a clear local error.
    "fees": ["business", "training_activation"],
}

# ---------------------------------------------------------------------------
# PRD-HWONB-003 (Business Information)
# ---------------------------------------------------------------------------
# §1.1 / §5.6 — GP-confirmed enum codes. Never hardcode a parallel list in the
# frontend; these are also the server-side Pydantic enforcement (AC-1) for
# schemas/business.py. The prototype's old codes (LLC,SOLE,CORP,PART,NPO,GOV,
# FI,PCORP / RETL,REST,SERV,ECOM,WHOL) are a DIFFERENT code set and must not
# ship — this tuple is the real one.
OWNERSHIP_TYPE_CHOICES = (
    "SOLE", "PART", "LLC", "LT", "GE", "CPUB", "CO", "CNP", "TR", "PF", "PO", "LP", "FI",
)
BUSINESS_TYPE_CHOICES = (
    "RETL", "RTIP", "REST", "MP", "IN", "LC", "SMKT", "UTIL", "PHAR", "BB",
)
# §5.4 — ITIN added (prototype previously only offered EIN/SSN).
TAX_ID_TYPE_CHOICES = ("EIN", "ITIN", "SSN")
# §1.1/§4/AC-3 — sicCode values that force charity501c3Exempt=true (locked in the UI).
CHARITY_EXEMPT_SIC_CODES = {"8398", "8661"}

# ---------------------------------------------------------------------------
# PRD-HWONB-004 (Processing Information)
# ---------------------------------------------------------------------------
# C-05 — governmentIdType additionally accepts "AI"; businessIdType
# additionally accepts "BFS"/"TR" ("TR" requires businessIdNumber).
# C-07 — applicationMerchantSurvey must be the nested object form, not a bare string.
# C-08 — shippingLeadTimeDays accepts any 1-99, not a fixed bucket set.

# §1.1/§5.1 — replaces the prototype's wrong All/VisaMC/None enum.
CARD_TYPES_ACCEPTED_CHOICES = ("All", "All1", "DBT")

# §1.1 — odd grammar ("IN_ADVANCED") is a real GP enum value, sent verbatim.
PREPAYMENT_TYPE_CHOICES = (
    "BALANCE_PAYMENTS_IN_ADVANCED",
    "DEPOSITS",
    "FULL_PAYMENTS_IN_ADVANCED",
    "RECURRING_FIXED",
    "RECURRING_ROLLING",
)

# §1.2 field table, already reconciled against the C-05 CERT probe (AI/TPV and
# BFS/TR both confirmed to validate; the field table below is the union GP
# actually documents — do not add the prototype's other alternates (DL/PP/
# ART/PRT) without a further CERT trial per the PRD's own open item C-06/C-14
# sibling caution).
GOVERNMENT_ID_TYPE_CHOICES = ("AI", "TDL", "TPV")
BUSINESS_ID_TYPE_CHOICES = ("GIBL", "TR", "AINC", "BFS")
# §1.2 conditional-requirement sets.
BUSINESS_ID_TYPE_REQUIRES_PLACE_OF_ISSUANCE = {"GIBL", "AINC", "BFS"}
BUSINESS_ID_TYPE_REQUIRES_DATE_ISSUED = {"GIBL", "AINC", "BFS", "TR"}

# §1.1/§4/§5.2 — up to 9 additional entries (10 total with the primary
# affiliatedMerchantId).
MAX_ADDITIONAL_AFFILIATED_MERCHANTS = 9

# ---------------------------------------------------------------------------
# PRD-HWONB-005 (Addresses)
# ---------------------------------------------------------------------------
# C-10 — contactType enum is distinct between Addresses and Owners sections.
# C-11 — addresses envelope is a keyed object: {dba, legal, mailing, chargeback, isSameAsDba}.
# N-3 — ZIP lookup is GET /masterRecord/zipcode?partner={partner}&zipCode={zip} (param is zipCode, partner required).

# C-10 — the Addresses contactType set (19 values, case-sensitive). Distinct
# from the Owners screen's short-code set (PRD-HWONB-007) — do NOT share
# this constant with owners.py. Offline fallback only; the live
# `GET /onboarding/lookups/contactType` response is authoritative (AC-3).
ADDRESS_CONTACT_TITLE_CHOICES = (
    "CEO", "COWN", "CONT", "CSEC", "DIR", "EDIR", "FOFF", "GMGR", "GPTR", "IRS",
    "LPTR", "MGR", "MEM", "OWN", "PRES", "SECR", "STRE", "SVP", "VP",
)

# C-11 — addresses request/response envelope is keyed by these 4 addressType
# sub-objects; only `dba` has no `isSameAsDba` flag.
ADDRESS_KEYS = ("dba", "legal", "mailing", "chargeback")
ADDRESS_CONDITIONAL_KEYS = ("legal", "mailing", "chargeback")

# N-3 — the HubWallet-facing proxy query param is `zip` (see PRD-HWONB-005
# §3A: `GET /onboarding/lookups/zipcode?zip={zip}`) but the real GP param
# name, once proxied server-side, is `zipCode` (base_boarding_client.lookup_zipcode).
# Do not confuse the two when wiring the router's query param.
ZIPCODE_LOOKUP_HUBWALLET_QUERY_PARAM = "zip"
ZIPCODE_LOOKUP_GP_QUERY_PARAM = "zipCode"

# ---------------------------------------------------------------------------
# PRD-HWONB-006 (Bank Accounts)
# ---------------------------------------------------------------------------
# C-23 — max 3 accounts is a real, CERT-confirmed GP rule (see MAX_BANK_ACCOUNTS
# above in the cross-cutting section) — enforced in crud.upsert_accounts, not
# via a DB constraint (models/account.py docstring).
ACCOUNT_TYPE_CHOICES = ("C", "S", "B")  # Checking | Savings | GL Ledger
USAGE_TYPE_CHOICES = ("ACHF", "ACHS", "C", "M", "D")

# ---------------------------------------------------------------------------
# PRD-HWONB-007 (Owners & Principals) — open items, not yet CERT-confirmed
# ---------------------------------------------------------------------------
# C-13 — ssn-exempt ownership types: keep as {"FI", "GE", "CPUB"} until confirmed.
OWNERSHIP_TYPES_SSN_EXEMPT = {"FI", "GE", "CPUB"}

# C-10 — CERT-confirmed real (owner create accepted the short code "O"): the
# Owners screen's contactTitle enum is a DISTINCT short-code set from the
# Addresses screen's 19-value contactType enum (PRD-HWONB-005) — do not reuse
# the Addresses list here. This is the Postman-confirmed create-table set
# (PRD-007 §1); the PDF's own POST vs PATCH tables disagree with each other
# too, so re-verify against a real PATCH example before building owner-edit.
OWNER_CONTACT_TITLE_CHOICES = (
    "CEO", "CO", "CTR", "CSEC", "D", "EXDR", "FOFF", "GM", "G",
    "L", "MGR", "MEM", "O", "P", "S", "ST", "SVP", "VP",
)

# PRD-007 §1 — home-address streetDirection enum (US-citizen branch only).
OWNER_STREET_DIRECTION_CHOICES = ("N", "S", "E", "W", "NE", "NW", "SE", "SW")

# C-14 — TSYS/GP's streetType is a 2-CHARACTER coded enum (its own scheme, not
# USPS suffix abbreviations). TSYS hard-rejects anything not exactly 2 chars
# ("streetType must be exact 2 characters"), so the earlier USPS list (AVE,
# BLVD, ...) was invalid. There is no confirmed master-record endpoint for the
# full ~130-value code set, so this is only the subset confirmed valid by the
# Base Boarding postman collection (ST/LN/PL/AV) and PRD-007 §1's examples
# (BV/RD/DR/CT). Expand only from a TSYS-supplied master code list. The owners
# schema enforces the length rule (exactly 2 chars) rather than membership, so
# a valid-but-uncatalogued code isn't wrongly rejected. Labels live in the UI.
OWNER_STREET_TYPE_CHOICES = (
    "AV", "BV", "CT", "DR", "LN", "PL", "RD", "ST",
)

# ---------------------------------------------------------------------------
# PRD-HWONB-008 (Card Types)
# ---------------------------------------------------------------------------
# C-15 — ebtFnsFcsNumber is required for ebtService in {"F", "B"}, not just "F".
EBT_FNS_FCS_REQUIRED_FOR = {"F", "B"}

# ---------------------------------------------------------------------------
# PRD-HWONB-009 (Products & Equipment)
# ---------------------------------------------------------------------------
# C-16 (round 2) — create path is POST /applications/{appId}/products (NO
# trailing id) — GP generates and returns `terminalNumber`. The full 13-bool
# `features` block (amex/ebt/pinDebit/debitEbtCashback included) is required,
# or the earlier drafts' 502s recur. See INTEGRATED_PRODUCT_ID_FIELD above,
# CONNECTION_METHOD_LABEL_TO_CODE above, PRODUCT_SOURCE_CHOICES above (C-17).
# Standalone confirmed-absent fields (do not add): encryption,
# p2peDeploymentFee/p2peMonthlyFee, staticIp{} — Integrated/WebPASS/Genius
# Equipment sub-resource only, never this product body.

# ---------------------------------------------------------------------------
# PRD-HWONB-010 (Training & Activation)
# ---------------------------------------------------------------------------
# C-18/C-19 — see TRAINING_ACTIVATION_VALUE_CHOICES/TRAINING_MERCHANT_TRAINED_BY_CHOICES
# above. equipShippedTo="NA" with CP/standalone equipment present is NOT
# rejected by GP at section-save time (C-19) — keep the "hide NA when CP" rule
# as a client-side (and this schema's server-side) UX guard only, never
# advertise it as an authoritative GP rule.

# ---------------------------------------------------------------------------
# PRD-HWONB-011 (Pricing & Fees) — AC-14 hard blocker, see PRD §6
# ---------------------------------------------------------------------------
# C-01 — rate-card fields (baseRates/perItemFees/authorizationFees/pinDebitFees)
# must be populated server-side from get_fee_processing_detail()'s thresholds,
# never sent null. additionalFees.ensureBill{setUpFee,monthlyFee,deliveryFee}
# is a required nested object regardless of plan.

# C-03 — processingType is returned Title-case by the lookup. This is the
# closed set CERT has observed so far; if GP ever adds a 4th processingType
# this list (and FeesRequest's validator) needs updating.
PROCESSING_TYPE_CHOICES = ("Retail", "MOTO", "Voice")

# C-01 round 2 — GP rejects any baseRates/perItemFees/authorizationFees/
# pinDebitFees rate value of exactly 0.00 (error 40213). When a
# feeProcessingDetail defaultValue is 0/None, bump it to this small positive
# floor before sending — still inside the field's own min/max bounds in
# every observed case (min was always 0).
RATE_CARD_ZERO_FLOOR = 0.01

# Maps a baseRatesThresholds rate "stem" (the fieldName with its
# Percentage/PerItem suffix stripped, e.g. "AllCardTypes", "Amex") to the
# (baseRates key, perItemFees key) pair from PRD-011 §1.2.
#
# WARNING — only the "AllCardTypes" stem is CERT-confirmed. C-02's saved
# example response only exercised one rate field pair
# (AllCardTypesPercentage / AllCardTypesPerItem, on plan 30/option 41
# "TransFreedom"). The remaining entries below are inferred from §1.2's
# field-name list by pattern-matching GP's own naming convention (act=All
# Card Types, vsMc=Visa/Mastercard, signDt=signature debit,
# regSignDt=regulated signature debit, qual=qualified, midQual=mid-qualified,
# cr=check/card rebate) — they are NOT independently CERT-verified. If a
# real feeProcessingDetail response ever returns a `fieldName` not covered
# here, `_resolve_rate_card` (base_boarding_client.py) logs a warning and
# skips that field rather than guessing further. Re-run cert_probe.py
# against a plan that exercises more than the AllCardTypes bucket to firm
# this up before relying on it for a real merchant.
RATE_CARD_FIELD_NAME_MAP = {
    "AllCardTypes": ("actBaseRateOrVsMcBaseRate", "actPerItemOrVsMcPerItem"),
    "Amex": ("amexBaseRate", "amexPerItem"),
    "SignatureDebit": ("signDtBaseRate", "signDtPerItem"),
    "RegulatedSignatureDebit": ("regSignDtBaseRate", "regSignDtPerItem"),
    "Qualified": ("qualBaseRate", "qualPerItem"),
    "Reward": ("rewardBaseRate", "rewardPerItem"),
    "MidQualified": ("midQualBaseRate", "midQualPerItem"),
    "NonQualified": (
        "differentialOrQualifiedProcessingFeeOrNonQualBaseRate",
        "differentialOrQualifiedProcessingFeeOrNonQualPerItem",
    ),
    "CheckCardRebate": ("crBaseRate", "crPerItem"),
}

# §1.3 authorizationFees — unconditionally-required object, 5 fields.
AUTHORIZATION_FEE_KEYS = ("allCardTypes", "batchClose", "voice", "aru", "amex")

# §1.4 pinDebitFees — required once cardTypes.debitRequested=true. `pdpPricePlan`
# is a plan/catalog identifier (not a rate) — never bumped to the zero floor;
# the other 5 are rate/amount fields that must be > 0.00 (C-01 round 2).
PIN_DEBIT_FEE_RATE_KEYS = (
    "pinDebitPerItemFee",
    "pinDebitRatePercent",
    "pinDebitEBTPerItemFee",
    "pinDebitMonthlyFee",
    "pinDebitApplicationFee",
)

# EnsureBill — live CERT-confirmed (2026-07-20) catalog entry names from
# `GET /feeSchedule/feeDetail/feeType/additionalServiceFee` (keyed by
# `feeName`, e.g. `{"feeName": "EnsureBILLSetupFee", "feeDefaultValue": 0}`).
# Exact casing confirmed live — do not re-case.
ENSURE_BILL_SETUP_FEE_CATALOG_KEY = "EnsureBILLSetupFee"
ENSURE_BILL_MONTHLY_FEE_CATALOG_KEY = "EnsureBILLMonthlyFee"

# §1.5 — fallback misc fee if the feeDetail miscFee catalog lookup fails or
# returns an unrecognized shape. 8001 "MONTHLY SERVICE FEE" is the exact
# CERT-confirmed example from PRD-011 §1.5.
FALLBACK_MISC_FEE_CODE = "8001"
FALLBACK_MISC_FEE_AMOUNT = 5.0

# Named, merchant-facing miscFee entries — live CERT-confirmed (2026-07-20)
# feeCode/feeFrequency pairs from `GET /feeSchedule/feeDetail/feeType/miscFee`
# (identical catalog observed across associations 086386/090823/095884).
# Maps FeesRequest's optional override field name -> (feeCode, feeFrequency).
# "monthlyServiceFee" is feeCode 8001, the same entry FALLBACK_MISC_FEE_*
# above defaults to when nothing else is supplied — this makes that fee
# merchant-editable instead of only ever being the hardcoded fallback amount.
NAMED_MISC_FEE_CODES = {
    "achChangeFee": ("8045", "Per-Instance"),
    "annualFee": ("8008", "Annual"),
    "applicationSetupFee": ("8054", "One-Time"),
    "breachCoverageFee": ("8005", "Monthly"),
    "monthlyServiceFee": ("8001", "Monthly"),
}

# Month-flag keys in GP's fee{Mon}Flag naming, in calendar order.
MISC_FEE_MONTH_FLAG_KEYS = (
    "feeJanFlag", "feeFebFlag", "feeMarFlag", "feeAprFlag",
    "feeMayFlag", "feeJunFlag", "feeJulFlag", "feeAugFlag",
    "feeSepFlag", "feeOctFlag", "feeNovFlag", "feeDecFlag",
)

# Business Information's own operating-months flags
# (`additionalInfo.{month}Flag`, schemas/business.py `AdditionalInfo`), same
# calendar order as MISC_FEE_MONTH_FLAG_KEYS above — a seasonal-Monthly misc
# fee's month flags must mirror these (GP: "seasonal fee selected month must
# match ... business configuration page").
BUSINESS_MONTH_FLAG_KEYS = (
    "janFlag", "febFlag", "marFlag", "aprFlag",
    "mayFlag", "junFlag", "julFlag", "augFlag",
    "sepFlag", "octFlag", "novFlag", "decFlag",
)

# ---------------------------------------------------------------------------
# PRD-HWONB-012 (Documents & Agreement incl. UMA)
# ---------------------------------------------------------------------------
# §2.3 — build against the stricter Production limit (2MB), not CERT's looser
# 10MB; a file that passes client-side against 10MB would fail in production.
MAX_DOCUMENT_SIZE_BYTES = 2 * 1024 * 1024  # 2MB

# §2.3 — "Allowed types: JPG, PDF, XLS, XLSX only." (Note: the same PRD's
# §5A UI-copy hint text lists "PDF, JPG, PNG, XLS, XLSX" — an internal PRD
# inconsistency; §2.3's "File constraints" section is treated as the
# authoritative backend rule here since it is explicitly the constraints
# section, not UI copy. Flagged for the PRD owner to reconcile.)
ALLOWED_DOCUMENT_CONTENT_TYPES = {
    "application/pdf",
    "image/jpeg",
    "image/jpg",  # non-standard but seen from some browsers/clients
    "application/vnd.ms-excel",
    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
}

# §2.4/N-2 — attachments (and every other section except `accounts`) cannot
# be pushed/uploaded until `business` has been saved. Documents piggybacks on
# the same section_state["business"]["pushed"] gate the other sections use.
DOCUMENTS_PREREQUISITE_SECTION = "business"
