"""
ATIME CDR Reporting Tool — configuration.

Secrets (API token, email key, recipients) are loaded from the environment first,
then from a gitignored secrets.json. Nothing sensitive is hardcoded here, so this
file is safe to commit. See secrets.example.json for the expected shape.
"""

import json
import os
import re

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DATA_DIR = os.path.join(BASE_DIR, "data")
OUTPUT_DIR = os.path.join(BASE_DIR, "output")
DB_PATH = os.path.join(DATA_DIR, "atime_cdrs.db")
MAPPING_XLSX = os.path.join(DATA_DIR, "UserListWithReportingDept.xlsx")
# Live ext -> department cache pulled from the NS API (users/callqueues/autoattendants);
# lets brand-new users/queues classify by department without waiting on an xlsx export.
DEPARTMENTS_JSON = os.path.join(DATA_DIR, "atime_departments.json")
SECRETS_PATH = os.path.join(BASE_DIR, "secrets.json")

# ----------------------------------------------------------------------------
# Secrets loader: env var wins, then secrets.json, then default.
# ----------------------------------------------------------------------------
try:
    with open(SECRETS_PATH) as _f:
        _SECRETS = json.load(_f)
except (OSError, ValueError):
    _SECRETS = {}


def secret(name, default=None, required=False):
    val = os.environ.get(name) or _SECRETS.get(name) or default
    if required and not val:
        raise RuntimeError(
            f"Missing required secret '{name}'. Set the {name} env var or add it to "
            f"{SECRETS_PATH} (see secrets.example.json).")
    return val


# ----------------------------------------------------------------------------
# NetSapiens API  (base host moved to api.cloudsession.io)
# ----------------------------------------------------------------------------
API_BASE = secret("ATIME_NS_API_BASE", "https://api.cloudsession.io/ns-api/v2")
DOMAIN = secret("ATIME_NS_DOMAIN", "ATIME")


def bearer_token():
    """Resolved lazily — only pulls need it, so offline analysis works without a token."""
    return secret("ATIME_NS_TOKEN", required=True)


# CRITICAL: PAGE_SIZE must stay at 100. Larger pages silently strip ~60 fields.
PAGE_SIZE = 100
DELAY_BETWEEN_PAGES = 0.1
MAX_RETRIES = 3
REQUEST_TIMEOUT = 90

# Rolling retention: prune CDRs older than this many months on each monthly run.
RETENTION_MONTHS = 18

# ----------------------------------------------------------------------------
# Call direction
# ----------------------------------------------------------------------------
DIRECTION_LABELS = {"0": "outbound", "1": "inbound_answered", "2": "missed", "3": "internal"}

# ----------------------------------------------------------------------------
# Department -> Reporting Division matrix (authoritative, per A TIME 2026-07).
# Every NS department maps to exactly one Reporting Division; a new user picks
# up a division automatically through whatever department they are assigned.
# Keys are normalized (lower-cased, whitespace-collapsed) at lookup time.
# ----------------------------------------------------------------------------
def normalize_dept(s):
    """Lower-case, strip, and collapse internal whitespace so 'HUG  Preg Loss'
    (double space) and 'HUG Preg Loss' match the same crosswalk key."""
    return re.sub(r"\s+", " ", str(s or "").strip().lower())


_CROSSWALK_RAW = {
    "Adoption": "Adoption",
    "Auction": "Auction",
    "Chicago_Dept": "Chicago",
    "Conference": "Conference",
    "DAAS": "Conference",
    "EarlyMorningRides": "EarlyMorningRides",
    "Events": "Conference",
    "Executive": "Executive",
    "Florida_dept": "Florida",
    "Fundraising": "Fundraising",
    "General": "General",
    "HashgachaBP": "Supervision",
    "HashgachaMonsey": "Supervision",
    "HashgachaRemote": "Supervision",
    "HashgachaWilli": "Supervision",
    "HUG Helpline": "HUG",
    "HUG Preg Loss Support": "HUG",
    "Insurance Advocacy": "Insurance Advocacy",
    "Kallahs": "Kallahs",
    "Kol Chaya": "Kol Chaya",
    "Meals": "Meals",
    "Medical Helpline": "Medical",
    "Mens HUG": "HUG",
    "Neshomaleh": "Neshomaleh",
    "Office": "Office",
    "Post-Hysterectomy": "Post-Hysterectomy",
    "Rabbinic": "Rabbinic",
    "Reservations": "Reservations",
    "Shabbos Support": "Shabbos",
    "Singles": "Singles",
    "SupportGroups- Medical Helpline": "Conference",
    "System": "System",
    "Willi Office": "Office",
    # Legacy / variant strings seen in CDR text (not in the matrix, kept as aliases)
    "Medical": "Medical",
    "Confrence": "Conference",   # NS typo seen on live users
    "2 step dialing": "System",
}
DEPT_CROSSWALK = {normalize_dept(k): v for k, v in _CROSSWALK_RAW.items()}

# All Reporting Divisions, classic donor-facing helplines first, then programs.
_HELPLINES_FIRST = ["Medical", "Supervision", "HUG", "Shabbos", "Conference",
                    "Rabbinic", "Kallahs", "Singles", "Adoption",
                    "Insurance Advocacy", "Post-Hysterectomy"]
REPORTING_DIVISIONS = _HELPLINES_FIRST + sorted(
    set(_CROSSWALK_RAW.values()) - set(_HELPLINES_FIRST))

# Tab-2 / spreadsheet division-label normalization (short codes -> canonical name)
DIVISION_NORMALIZE = {
    "MHL": "Medical", "Medical": "Medical", "Kallah": "Kallahs", "Kallahs": "Kallahs",
    "Single": "Singles", "Singles": "Singles", "Supervision": "Supervision",
    "HUG": "HUG", "Hug": "HUG", "Shabbos": "Shabbos", "Conference": "Conference",
    "Rabbinic": "Rabbinic", "Adoption": "Adoption",
}

# Sentinel still recognized from the spreadsheet's "Reporting Department" column;
# not produced by the department matrix (excluded from all division metrics).
ADMIN = "Admin"

# ----------------------------------------------------------------------------
# Voicemail / caller extraction / time windows
# ----------------------------------------------------------------------------
VMAIL_TO_URI_TOKEN = "vmail"
VMAIL_MATCH_URI_TOKEN = "record-vmail"
PHONE_RE = re.compile(r"sip:\+?1?(\d{10})@")
MAIN_AA_EXTENSION = "998"

BUSINESS_START_HOUR = 8
BUSINESS_END_HOUR = 19
OVERNIGHT_START_HOUR = 22
OVERNIGHT_END_HOUR = 6
LOCAL_TZ = "US/Eastern"

# ----------------------------------------------------------------------------
# Regional deep-dive definitions.
# Each region produces its own monthly report. area_codes: code -> coverage label.
# ----------------------------------------------------------------------------
REGIONS = {
    "cleveland": {
        "key": "cleveland",
        "name": "Cleveland",
        "display": "Cleveland, Ohio",
        "msa_name": "Cleveland–Elyria, OH Metro (Cuyahoga, Geauga, Lake, Lorain, Medina)",
        "accent": "#C97B67",   # deepened A TIME terracotta (apricot-lilly hue)
        "counties": "Cuyahoga, Geauga, Lake, Lorain, and Medina counties",
        "excluded_note": "Akron–Canton (330 / 234) is a separate metro and is not included in these figures.",
        "area_codes": {
            "216": "Cleveland city + inner-ring Cuyahoga suburbs",
            "440": "Outer suburbs / Greater Cleveland (Lake, Geauga, Lorain, Medina)",
        },
    },
    "florida": {
        "key": "florida",
        "name": "Florida",
        "display": "Florida",
        "msa_name": "State of Florida (all metros)",
        "accent": "#2F7D6C",   # deepened A TIME teal (aqua-light hue)
        "counties": "statewide — Miami-Dade, Broward, Palm Beach, Orlando, Tampa Bay, and beyond",
        "excluded_note": "Covers Florida area codes only; out-of-state snowbird numbers are not reassigned.",
        "area_codes": {
            "305": "Miami-Dade", "786": "Miami-Dade",
            "954": "Broward", "754": "Broward",
            "561": "Palm Beach / Boca", "772": "Treasure Coast",
            "407": "Orlando", "689": "Orlando", "321": "Space Coast",
            "813": "Tampa", "656": "Tampa", "727": "Pinellas / St. Pete",
            "352": "North Central", "386": "Volusia / Daytona",
            "904": "Jacksonville", "239": "Naples / SW FL",
            "941": "Sarasota", "863": "Polk / Central", "850": "Panhandle / Tallahassee",
        },
    },
    "nyc": {
        "key": "nyc",
        "name": "New York City",
        "display": "New York City",
        "msa_name": "New York City — the five boroughs",
        "accent": "#5A5AC9",   # deepened A TIME indigo (purple hue #8888FB)
        "counties": "Manhattan, Brooklyn, Queens, the Bronx, and Staten Island",
        "excluded_note": "Five-borough area codes only; Long Island (516/631), Westchester (914), "
                         "and Rockland/Hudson Valley (845) are reported separately in the regional rollup.",
        "area_codes": {
            "212": "Manhattan", "646": "Manhattan", "332": "Manhattan",
            "718": "Brooklyn / Queens / Bronx / SI", "347": "Brooklyn / Queens / Bronx / SI",
            "929": "Brooklyn / Queens / Bronx / SI", "917": "NYC mobile (all boroughs)",
        },
    },
}

# ----------------------------------------------------------------------------
# Regional rollup for the general geographic section (area code -> region label)
# ----------------------------------------------------------------------------
REGION_BY_AREA_CODE = {
    "718": "NYC Metro", "347": "NYC Metro", "917": "NYC Metro", "929": "NYC Metro",
    "646": "NYC Metro", "212": "NYC Metro", "332": "NYC Metro",
    "516": "Long Island", "631": "Long Island",
    "845": "Hudson Valley / Rockland", "914": "Westchester",
    "732": "New Jersey", "848": "New Jersey", "201": "New Jersey", "551": "New Jersey",
    "973": "New Jersey", "862": "New Jersey", "908": "New Jersey", "609": "New Jersey",
    "410": "Baltimore / MD", "443": "Baltimore / MD", "667": "Baltimore / MD",
    "301": "DC / MD", "240": "DC / MD",
    "216": "Cleveland Metro", "440": "Cleveland Metro",
    "305": "South Florida", "786": "South Florida", "954": "South Florida", "561": "South Florida",
    "407": "Central Florida", "813": "Tampa Bay", "904": "Jacksonville",
    "323": "Los Angeles", "213": "Los Angeles", "310": "Los Angeles",
    "773": "Chicago", "312": "Chicago", "847": "Chicago",
    "513": "Cincinnati", "614": "Columbus",
    "617": "Boston", "857": "Boston",
    "267": "Philadelphia", "215": "Philadelphia", "484": "Philadelphia", "610": "Philadelphia",
}

# ----------------------------------------------------------------------------
# Email (Postmark or Resend transactional API)
# ----------------------------------------------------------------------------
EMAIL = {
    "provider": secret("ATIME_EMAIL_PROVIDER", "postmark"),   # "postmark" | "resend"
    "api_key": secret("ATIME_EMAIL_API_KEY", ""),
    "from": secret("ATIME_EMAIL_FROM", "reports@pressone.net"),
    "reply_to": secret("ATIME_EMAIL_REPLY_TO", ""),
    # recipients per region: secrets.json -> {"recipients": {"cleveland": ["a@x.com"], ...}}
    "recipients": _SECRETS.get("recipients", {}),
    # If truthy, do not actually send — write .eml previews to output/ instead.
    "dry_run": bool(secret("ATIME_EMAIL_DRY_RUN", "")),
}

# ----------------------------------------------------------------------------
# A TIME branding — pulled from atime.org's live theme CSS (wp-content/themes/
# a-time/assets/css/main.css), 2026-07. Values marked "(deepened)" are the same
# hue as an A TIME swatch but darker/more saturated: their own palette tops out
# in pastel tones (e.g. aqua-light-400 #B0DBD2, apricot-lilly-400 #F6D3BF) that
# don't have enough contrast against white for report text/data-ink, so the hue
# is preserved and only lightness/saturation is adjusted for legibility.
# Heading font on the live site is the licensed "P22MackinacPro" (no webfont
# license to embed it here); Inter is used throughout instead — it's the same
# family as their body font "InterDisplay" and is free (SIL Open Font License).
# ----------------------------------------------------------------------------
BRAND = {
    "purple_deep": "#1A242A",   # --color-grey-900: real heading / primary-button color
    "purple_med":  "#5C6267",   # --color-grey-600: secondary text
    "purple_light": "#F4C3B8",  # --color-pink-400: real light pastel accent
    "orange": "#2F7D6C",        # deepened teal (--color-aqua-light hue)
    "gold": "#C97B67",          # deepened terracotta (--color-apricot-lilly hue)
    "text": "#363D41",          # --color-grey-800
    "muted": "#6F767C",         # --color-grey-500
    "border": "#E0E7EC",        # --color-grey-50
    "border_soft": "#D1D9DD",   # --color-grey-100
    "bg": "#F8F9FA",            # --color-grey-00
    "bg_warm": "#FAEDE9",       # --color-pink-100 (callout/card tint)
    "faint": "#9EA5AA",         # --color-grey-300 (faint chart labels)
    "font": "Inter",
    "font_url": "https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap",
}

ORG_NAME = "A TIME"
ORG_TAGLINE = "24 divisions. Around the clock. For families building families."
