"""
Configuration settings for FoundryMatch SaaS/iPaaS integration.
All hardcoded values should be defined here.
"""

from typing import Optional
import json
import os
from pathlib import Path
from dotenv import load_dotenv

# Load environment variables from .env file FIRST (before reading any env vars!)
# Also load .env.dev for development-specific settings
load_dotenv()
if Path(".env.dev").exists():
    load_dotenv(".env.dev", override=True)


def _normalize_ollama_model(
    name: Optional[str], fallback: Optional[str] = None
) -> Optional[str]:
    """
    Normalize common Ollama model name variants so `model-7b` resolves to `model:7b`.

    Ollama stores tags using a colon separator (e.g. `qwen2.5-coder:7b`) but our historical
    defaults used hyphenated suffices. This helper keeps compatibility with either form.
    """
    candidate = (name or fallback or "").strip()
    if not candidate:
        return None
    if ":" in candidate:
        return candidate

    prefix, sep, suffix = candidate.rpartition("-")
    normalized_suffix = suffix.lower()
    common_tags = {
        "7b",
        "8b",
        "13b",
        "14b",
        "32b",
        "70b",
        "small",
        "medium",
        "large",
        "mini",
        "latest",
        "instruct",
    }
    if sep and normalized_suffix in common_tags:
        return f"{prefix}:{suffix}"
    return candidate


# Diagnostic/testing knobs (AFTER loading .env)
RUN_DIAGNOSTIC_FUZZY = os.getenv("RUN_DIAGNOSTIC_FUZZY", "false").lower() == "true"
DIAGNOSTIC_FUZZY_SAMPLE = int(os.getenv("DIAGNOSTIC_FUZZY_SAMPLE", "10"))
PERSONAL_DOMAIN_FILTER = os.getenv("PERSONAL_DOMAIN_FILTER", "true").lower() == "true"

# ──────────────────── Environment ─────────────────────────────
ENV = os.getenv("ENV", "development")
IS_PRODUCTION = ENV == "production"
SENTRY_DSN: str = os.getenv("SENTRY_DSN", "")  # Set via env var SENTRY_DSN
ENVIRONMENT: str = os.getenv("ENVIRONMENT", ENV)  # Set via env var ENVIRONMENT
SF_GATEWAY_CACHE_IN_DEV = (
    os.getenv("SF_GATEWAY_CACHE_IN_DEV", "false").lower() == "true"
)
TRIAL_LENGTH_DAYS = int(os.getenv("TRIAL_LENGTH_DAYS", "30"))

# Single source of truth for dev account ID
DEV_ACCOUNT_ID = os.getenv("DEV_ACCOUNT_ID", "dev-account-id")

# Vertex AI / Generative AI defaults (shared across AI features)
VERTEX_PROJECT = (
    os.getenv("VERTEX_PROJECT")
    or os.getenv("GOOGLE_CLOUD_PROJECT")
    or os.getenv("GCP_PROJECT")
)
VERTEX_LOCATION = os.getenv("VERTEX_LOCATION", "us-central1")
VERTEX_MODEL = os.getenv("VERTEX_MODEL") or os.getenv("GENAI_MODEL")
DEFAULT_VERTEX_TIMEOUT_S = int(
    os.getenv(
        "DEFAULT_VERTEX_TIMEOUT_S",
        os.getenv(
            "DEFAULT_VERTEX_TIMEOUT",
            os.getenv("VERTEX_GENERATE_TIMEOUT", "20"),
        ),
    )
)

# LLM routing defaults (shared by LLM-backed services)
LLM_DEFAULT_MODEL = os.getenv("LLM_DEFAULT_MODEL", "gemini-3-flash-preview")
LLM_FALLBACK_MODEL = os.getenv("LLM_FALLBACK_MODEL", "gemini-2.5-pro")

# Timeouts (seconds) - per tier
LLM_FAST_UI_TIMEOUT_S = int(os.getenv("LLM_FAST_UI_TIMEOUT_S", "12"))
LLM_STANDARD_INTEL_TIMEOUT_S = int(os.getenv("LLM_STANDARD_INTEL_TIMEOUT_S", "25"))
LLM_HEAVY_REASON_TIMEOUT_S = int(os.getenv("LLM_HEAVY_REASON_TIMEOUT_S", "45"))

# Wall-clock budgets (total time including retries)
LLM_FAST_UI_BUDGET_S = int(os.getenv("LLM_FAST_UI_BUDGET_S", "12"))
LLM_STANDARD_INTEL_BUDGET_S = int(os.getenv("LLM_STANDARD_INTEL_BUDGET_S", "25"))
LLM_HEAVY_REASON_BUDGET_S = int(os.getenv("LLM_HEAVY_REASON_BUDGET_S", "45"))

# Concurrency - conservative defaults for preview quotas
LLM_GLOBAL_CONCURRENCY = int(os.getenv("LLM_GLOBAL_CONCURRENCY", "10"))
LLM_ORG_CONCURRENCY = int(os.getenv("LLM_ORG_CONCURRENCY", "2"))

# Circuit breaker
LLM_CIRCUIT_THRESHOLD = int(os.getenv("LLM_CIRCUIT_THRESHOLD", "5"))
LLM_CIRCUIT_RESET_S = int(os.getenv("LLM_CIRCUIT_RESET_S", "60"))

# Retry jitter ranges (ms)
LLM_RETRY_JITTER_MIN_MS = int(os.getenv("LLM_RETRY_JITTER_MIN_MS", "200"))
LLM_RETRY_JITTER_MAX_MS = int(os.getenv("LLM_RETRY_JITTER_MAX_MS", "600"))

# ──────────────────── Database ─────────────────────────────
DATABASE_URL = os.getenv(
    "DATABASE_URL",
    "postgresql+asyncpg://foundrymatch:foundrymatch@localhost:5432/foundrymatch",
)
DATABASE_POOL_SIZE = int(os.getenv("DATABASE_POOL_SIZE", "10"))
DATABASE_MAX_OVERFLOW = int(os.getenv("DATABASE_MAX_OVERFLOW", "10"))
DATABASE_POOL_TIMEOUT = int(os.getenv("DATABASE_POOL_TIMEOUT", "30"))
DATABASE_POOL_PRE_PING = os.getenv("DATABASE_POOL_PRE_PING", "true").lower() == "true"

# ──────────────────── Redis & RQ ─────────────────────────────
# IMPORTANT: Must match exactly between API and Worker!
REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379/0")
RQ_QUEUE_NAME = os.getenv("RQ_QUEUE_NAME", "default")

# ──────────────────── Processing Limits ─────────────────────────────
# Row limits
SYNC_ROW_LIMIT = int(os.getenv("SYNC_ROW_LIMIT", "10000"))
MAX_ROWS_DEFAULT = int(os.getenv("MAX_ROWS_DEFAULT", "1000000"))  # 1M rows
SAMPLE_SIZE_LARGE_FILES = int(os.getenv("SAMPLE_SIZE_LARGE_FILES", "1000000"))

# Memory limits
MAX_SYNC_MEMORY_MB = int(os.getenv("MAX_SYNC_MEMORY_MB", "100"))
SMALL_FILE_SIZE_MB = int(os.getenv("SMALL_FILE_SIZE_MB", "100"))
MEDIUM_FILE_SIZE_MB = int(os.getenv("MEDIUM_FILE_SIZE_MB", "500"))

# File size limits
MAX_UPLOAD_SIZE = int(os.getenv("MAX_UPLOAD_SIZE", "104857600"))  # 100MB
MAX_STRING_DATA_MB = int(os.getenv("MAX_STRING_DATA_MB", "100"))

# ──────────────────── Matching Defaults ─────────────────────────────
DEFAULT_DEDUPE_THRESHOLD = int(os.getenv("DEFAULT_DEDUPE_THRESHOLD", "85"))
DEFAULT_MATCH_THRESHOLD = int(os.getenv("DEFAULT_MATCH_THRESHOLD", "80"))
DEFAULT_BLOCK_KEY_LENGTH = int(os.getenv("DEFAULT_BLOCK_KEY_LENGTH", "3"))
DEFAULT_DUP_BLOCK_LIMIT = int(os.getenv("DEFAULT_DUP_BLOCK_LIMIT", "2000"))


# ──────────────────── API/Webhook Settings ─────────────────────────────
WEBHOOK_TIMEOUT = int(os.getenv("WEBHOOK_TIMEOUT", "30"))
WEBHOOK_MAX_RETRIES = int(os.getenv("WEBHOOK_MAX_RETRIES", "3"))
GOOGLE_SHEETS_RPM_LIMIT = int(os.getenv("GOOGLE_SHEETS_RPM_LIMIT", "15"))

# Rate limiting
RATE_LIMIT_SIMPLE_MATCH = os.getenv("RATE_LIMIT_SIMPLE_MATCH", "100/hour")
RATE_LIMIT_STATUS = os.getenv("RATE_LIMIT_STATUS", "1000/hour")

# ──────────────────── Salesforce Security Settings ─────────────────────────────
SALESFORCE_SECURITY = {
    "enforce_allowlist": os.getenv("SF_ENFORCE_ALLOWLIST", "true").lower() == "true",
    "allow_custom_objects": os.getenv("SF_ALLOW_CUSTOM_OBJECTS", "false").lower()
    == "true",
    "max_query_limit": int(os.getenv("SF_MAX_QUERY_LIMIT", "1000")),
    "require_admin_for_support": os.getenv("SF_REQUIRE_ADMIN_SUPPORT", "true").lower()
    == "true",
    "log_security_events": os.getenv("SF_LOG_SECURITY", "true").lower() == "true",
    "block_credential_patterns": os.getenv("SF_BLOCK_CREDENTIALS", "true").lower()
    == "true",
}

SFDC_BULK_MATCH_ENABLED = os.getenv("SFDC_BULK_MATCH_ENABLED", "false").lower() == "true"

# Salesforce AI Report Builder / SOQL Copilot
AI_REPORTS_ENABLED = os.getenv("AI_REPORTS_ENABLED", "true").lower() == "true"
AI_REPORTS_HOST = os.getenv(
    "AI_REPORTS_HOST", os.getenv("LLM_HOST", "http://localhost:11434")
)


def _get_ai_model(env_var: str, default: str) -> str:
    """Resolve AI Reports model, defaulting to shared Vertex AI config."""
    value = os.getenv(env_var)
    if value:
        return value
    if VERTEX_MODEL:
        return VERTEX_MODEL
    return default


AI_REPORTS_PLANNER_MODEL = _get_ai_model(
    "AI_REPORTS_PLANNER_MODEL",
    "gemini-3-flash-preview",
)
AI_REPORTS_RESEARCHER_MODEL = _get_ai_model(
    "AI_REPORTS_RESEARCHER_MODEL",
    AI_REPORTS_PLANNER_MODEL,
)
AI_REPORTS_CRITIC_MODEL = _get_ai_model(
    "AI_REPORTS_CRITIC_MODEL",
    AI_REPORTS_PLANNER_MODEL,
)
AI_REPORTS_VALIDATOR_MODEL = _get_ai_model(
    "AI_REPORTS_VALIDATOR_MODEL",
    AI_REPORTS_PLANNER_MODEL,
)
AI_REPORTS_PRO_MODEL = _get_ai_model(
    "AI_REPORTS_PRO_MODEL",
    "gemini-3-flash-preview",
)
AI_REPORTS_ENTERPRISE_MODEL = _get_ai_model(
    "AI_REPORTS_ENTERPRISE_MODEL",
    "gemini-3-flash-preview",
)
AI_REPORTS_TIMEOUT_S = int(
    os.getenv("AI_REPORTS_TIMEOUT_S", os.getenv("AI_REPORTS_TIMEOUT", "60"))
)
AI_REPORTS_RESEARCHER_TIMEOUT_S = int(
    os.getenv("AI_REPORTS_RESEARCHER_TIMEOUT_S", str(AI_REPORTS_TIMEOUT_S))
)
AI_REPORTS_PLANNER_TIMEOUT_S = int(
    os.getenv("AI_REPORTS_PLANNER_TIMEOUT_S", str(AI_REPORTS_TIMEOUT_S))
)
AI_REPORTS_PLANNER_MAX_OUTPUT_TOKENS = int(
    os.getenv("AI_REPORTS_PLANNER_MAX_OUTPUT_TOKENS", "2048")
)
AI_REPORTS_PREVIEW_LIMIT = int(os.getenv("AI_REPORTS_PREVIEW_LIMIT", "5"))
AI_REPORTS_MAX_FIELDS = int(os.getenv("AI_REPORTS_MAX_FIELDS", "60"))
AI_REPORTS_MAX_OBJECTS = int(os.getenv("AI_REPORTS_MAX_OBJECTS", "6"))
AI_REPORTS_RESEARCH_CACHE_TTL = int(
    os.getenv("AI_REPORTS_RESEARCH_CACHE_TTL", "300")
)
AI_REPORTS_SKIP_CRITIC_SIMPLE = (
    os.getenv("AI_REPORTS_SKIP_CRITIC_SIMPLE", "true").lower() == "true"
)
AI_REPORTS_SCHEMA_CACHE_TTL = int(
    os.getenv("AI_REPORTS_SCHEMA_CACHE_TTL", str(24 * 60 * 60))
)
AI_REPORTS_ROW_CAPS = {
    "FREE": int(os.getenv("AI_REPORTS_ROW_CAP_FREE", "5000")),
    "TEAM": int(os.getenv("AI_REPORTS_ROW_CAP_TEAM", "50000")),
    "PRO": int(os.getenv("AI_REPORTS_ROW_CAP_PRO", "50000")),
    "ENTERPRISE": int(os.getenv("AI_REPORTS_ROW_CAP_ENTERPRISE", "250000")),
}
AI_REPORTS_MAX_CONCURRENT_PER_ORG = int(
    os.getenv("AI_REPORTS_MAX_CONCURRENT_PER_ORG", "3")
)
AI_REPORTS_MAX_CONCURRENT_GLOBAL = int(
    os.getenv("AI_REPORTS_MAX_CONCURRENT_GLOBAL", "12")
)

# Gemini 3 thinking levels per AI Reports stage
# MINIMAL = fastest, LOW = some reasoning, MEDIUM = balanced, HIGH = deep reasoning
AI_REPORTS_THINKING_LEVELS = {
    "researcher": os.getenv("AI_REPORTS_THINKING_RESEARCHER", "MINIMAL"),
    "planner": os.getenv("AI_REPORTS_THINKING_PLANNER", "LOW"),
    "critic": os.getenv("AI_REPORTS_THINKING_CRITIC", "LOW"),
    "repair": os.getenv("AI_REPORTS_THINKING_REPAIR", "LOW"),
}
AI_REPORTS_RESEARCHER_MAX_OUTPUT_TOKENS = int(
    os.getenv("AI_REPORTS_RESEARCHER_MAX_OUTPUT_TOKENS", "1024")
)

AI_SCHEMA_INDEX_ON_CONNECT = (
    os.getenv("AI_SCHEMA_INDEX_ON_CONNECT", "true").lower() == "true"
)
AI_SCHEMA_INDEX_REPORTS_MAX = int(os.getenv("AI_SCHEMA_INDEX_REPORTS_MAX", "50"))
AI_SCHEMA_INDEX_SAMPLE_LIMIT = int(os.getenv("AI_SCHEMA_INDEX_SAMPLE_LIMIT", "1000"))
AI_SCHEMA_INDEX_TTL_HOURS = int(os.getenv("AI_SCHEMA_INDEX_TTL_HOURS", "24"))


def _parse_csv_list(env_name: str, default: list[str]) -> list[str]:
    raw = os.getenv(env_name)
    if not raw:
        return list(default)
    items = [item.strip() for item in raw.split(",") if item.strip()]
    return items or list(default)


AI_REPORTS_ALLOW_OBJECTS = _parse_csv_list(
    "AI_REPORTS_ALLOW_OBJECTS",
    [
        "Account",
        "Contact",
        "Lead",
        "Opportunity",
        "OpportunityContactRole",
        "CampaignMember",
        "User",
    ],
)
AI_REPORTS_DENY_OBJECTS = _parse_csv_list(
    "AI_REPORTS_DENY_OBJECTS",
    ["Attachment", "ContentDocument", "ContentVersion", "EmailMessage"],
)
try:
    AI_REPORTS_ALLOW_FIELDS = json.loads(
        os.getenv("AI_REPORTS_ALLOW_FIELDS", "{}") or "{}"
    )
    if not isinstance(AI_REPORTS_ALLOW_FIELDS, dict):
        AI_REPORTS_ALLOW_FIELDS = {}
except json.JSONDecodeError:
    AI_REPORTS_ALLOW_FIELDS = {}

# Security logging endpoints
SECURITY_LOG_ENDPOINT = os.getenv(
    "SECURITY_LOG_ENDPOINT"
)  # e.g., "https://your-siem.com/api/events"
SECURITY_ALERT_WEBHOOK = os.getenv("SECURITY_ALERT_WEBHOOK")  # e.g., Slack webhook URL

# Frontend origin for OAuth callbacks
FRONTEND_ORIGIN = os.getenv("FRONTEND_ORIGIN", "http://localhost:3004")
RATE_LIMIT_RESULTS = os.getenv("RATE_LIMIT_RESULTS", "500/hour")
RATE_LIMIT_DEFAULT = os.getenv("RATE_LIMIT_DEFAULT", "1000/hour")

# CORS
CORS_ORIGINS = os.getenv(
    "CORS_ORIGINS",
    ",".join(
        [
            "http://localhost:3000",
            "http://localhost:3004",
            "http://localhost:3001",
            "http://localhost:8080",
            "http://127.0.0.1:3000",
            "https://foundryops.io",
            "https://foundryops-site.pages.dev",
        ]
    ),
)

# ──────────────────── Security Settings ─────────────────────────────
# JWT Settings
_raw_jwt_secret = os.getenv("JWT_SECRET_KEY", None)
JWT_SECRET_KEY = _raw_jwt_secret.strip() if _raw_jwt_secret else None  # Will generate if not set
JWT_ALGORITHM = os.getenv("JWT_ALGORITHM", "HS256")
JWT_EXPIRATION_HOURS = int(os.getenv("JWT_EXPIRATION_HOURS", "24"))
# Agent license JWT secret (Next.js site) — strip to avoid trailing newlines from Secret Manager
_raw_agent_jwt = os.getenv("AGENT_JWT_SECRET", None)
AGENT_JWT_SECRET = _raw_agent_jwt.strip() if _raw_agent_jwt else None

# Admin Settings
ADMIN_EMAILS = os.getenv("ADMIN_EMAILS", "admin@foundrymatch.com").split(",")

# API Key settings
API_KEY_LENGTH = int(os.getenv("API_KEY_LENGTH", "32"))
API_KEY_PREFIX_LENGTH = int(os.getenv("API_KEY_PREFIX_LENGTH", "8"))
API_KEY_DEFAULT_RATE_LIMIT = int(os.getenv("API_KEY_DEFAULT_RATE_LIMIT", "1000"))

# Internal API key for webhook endpoints (seat management, etc.)
INTERNAL_API_KEY = os.getenv("INTERNAL_API_KEY", None)

# SSRF Protection
FORBIDDEN_HOSTS = {
    "localhost",
    "127.0.0.1",
    "0.0.0.0",
    "169.254.169.254",  # AWS metadata
    "::1",
    "[::1]",  # IPv6 localhost
    "metadata.google.internal",  # GCP metadata
}

FORBIDDEN_URL_PATTERNS = ["metadata", "userdata", ".internal"]

# ──────────────────── Storage/Path Settings ─────────────────────────────
# Storage configuration
STORAGE_BACKEND = os.getenv("STORAGE_BACKEND", "local")  # Default to local storage
LOCAL_STORAGE_PATH = os.getenv("LOCAL_STORAGE_PATH", "./uploads")
S3_BUCKET = os.getenv("S3_BUCKET", "foundrymatch-uploads")
AWS_REGION = os.getenv("AWS_REGION", "us-east-1")

DEFAULT_OUTPUT_DIR = Path(os.getenv("DEFAULT_OUTPUT_DIR", "/tmp/fmatch/jobs"))
TEMP_DIR = Path(os.getenv("TMPDIR", "/tmp"))

# Ensure local storage directory exists
if STORAGE_BACKEND == "local":
    os.makedirs(LOCAL_STORAGE_PATH, exist_ok=True)

# ──────────────────── Job Processing ─────────────────────────────
JOB_TIMEOUT_DEFAULT = os.getenv("JOB_TIMEOUT_DEFAULT", "1h")
JOB_RESULT_RETENTION_DAYS = int(os.getenv("JOB_RESULT_RETENTION_DAYS", "7"))
CHUNK_SIZE_CSV_READ = int(os.getenv("CHUNK_SIZE_CSV_READ", "50000"))

# ──────────────────── Documentation/Support ─────────────────────────────
DOCS_URL = os.getenv("DOCS_URL", "https://docs.foundrymatch.com/ipaas")
SUPPORT_EMAIL = os.getenv("SUPPORT_EMAIL", "support@foundrymatch.com")
API_VERSION = os.getenv("API_VERSION", "1.0.0")

# ──────────────────── Feature Flags ─────────────────────────────
ENABLE_TELEMETRY = os.getenv("ENABLE_TELEMETRY", "true").lower() == "true"
ENABLE_GOOGLE_SHEETS = os.getenv("ENABLE_GOOGLE_SHEETS", "false").lower() == "true"
REQUIRE_HTTPS_WEBHOOKS = (
    IS_PRODUCTION or os.getenv("REQUIRE_HTTPS_WEBHOOKS", "false").lower() == "true"
)
GREMLIN_V2_ENABLED = os.getenv("GREMLIN_V2_ENABLED", "true").lower() == "true"
GREMLIN_V2_FORMULA_FIXER = (
    os.getenv("GREMLIN_V2_FORMULA_FIXER", "true").lower() == "true"
)
GREMLIN_V2_EXPLAINER = (
    os.getenv("GREMLIN_V2_EXPLAINER", "true").lower() == "true"
)
GREMLIN_CHAT_DEMOTED = (
    os.getenv("GREMLIN_CHAT_DEMOTED", "false").lower() == "true"
)

# Google Sheets embed configuration
GSHEETS_EMBED_TOKEN_TTL_SECONDS = int(
    os.getenv("GSHEETS_EMBED_TOKEN_TTL_SECONDS", "600")
)
ALLOW_GSHEETS_EMBED_WITHOUT_TOKEN = (
    os.getenv(
        "ALLOW_GSHEETS_EMBED_WITHOUT_TOKEN",
        "false" if IS_PRODUCTION else "true",
    ).lower()
    == "true"
)

# Google OAuth (Sheets direct writes)
GOOGLE_CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
GOOGLE_CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET")
GOOGLE_OAUTH_REDIRECT_URI = os.getenv("GOOGLE_OAUTH_REDIRECT_URI", "")


def _get_env_stripped(name: str, default: str = "") -> str:
    value = os.getenv(name, default)
    if value is None:
        return default
    return value.strip() if isinstance(value, str) else str(value).strip()


# HubSpot OAuth
HUBSPOT_CLIENT_ID = _get_env_stripped("HUBSPOT_CLIENT_ID", "")
HUBSPOT_CLIENT_SECRET = _get_env_stripped("HUBSPOT_CLIENT_SECRET", "")
HUBSPOT_OAUTH_REDIRECT_URI = _get_env_stripped("HUBSPOT_OAUTH_REDIRECT_URI", "")

# Outreach OAuth (server-mediated)
OUTREACH_CLIENT_ID = _get_env_stripped("OUTREACH_CLIENT_ID", "")
OUTREACH_CLIENT_SECRET = _get_env_stripped("OUTREACH_CLIENT_SECRET", "")
OUTREACH_OAUTH_REDIRECT_URI = _get_env_stripped("OUTREACH_OAUTH_REDIRECT_URI", "")
HUBSPOT_OAUTH_SCOPES = _get_env_stripped(
    "HUBSPOT_OAUTH_SCOPES",
    ",".join(
        [
            "oauth",
            "crm.objects.contacts.read",
            "crm.objects.contacts.write",
            "crm.objects.companies.read",
            "crm.objects.companies.write",
        ]
    ),
)
# HubSpot write control plane (Phase A/B rollout)
HUBSPOT_WRITE_CONTROL_MODE = _get_env_stripped("HUBSPOT_WRITE_CONTROL_MODE", "shadow").lower()
HUBSPOT_WRITE_CONTROL_KILL_SWITCH = (
    _get_env_stripped("HUBSPOT_WRITE_CONTROL_KILL_SWITCH", "false").lower() == "true"
)
HUBSPOT_WRITE_GATE_TTL_SECONDS = int(_get_env_stripped("HUBSPOT_WRITE_GATE_TTL_SECONDS", "120"))
HUBSPOT_WRITE_GATE_REPLAY_TTL_SECONDS = int(
    _get_env_stripped("HUBSPOT_WRITE_GATE_REPLAY_TTL_SECONDS", "300")
)
HUBSPOT_WRITE_GATE_SIGNING_KEY_CURRENT = _get_env_stripped(
    "HUBSPOT_WRITE_GATE_SIGNING_KEY_CURRENT",
    "",
)
HUBSPOT_WRITE_GATE_SIGNING_KEY_CURRENT_KID = _get_env_stripped(
    "HUBSPOT_WRITE_GATE_SIGNING_KEY_CURRENT_KID",
    "current",
)
HUBSPOT_WRITE_GATE_SIGNING_KEY_PREVIOUS = _get_env_stripped(
    "HUBSPOT_WRITE_GATE_SIGNING_KEY_PREVIOUS",
    "",
)
HUBSPOT_WRITE_GATE_SIGNING_KEY_PREVIOUS_KID = _get_env_stripped(
    "HUBSPOT_WRITE_GATE_SIGNING_KEY_PREVIOUS_KID",
    "previous",
)
HUBSPOT_WRITE_GATE_ISSUE_RATE_LIMIT_MAX = int(
    _get_env_stripped("HUBSPOT_WRITE_GATE_ISSUE_RATE_LIMIT_MAX", "0")
)
HUBSPOT_WRITE_GATE_ISSUE_RATE_LIMIT_WINDOW_SECONDS = int(
    _get_env_stripped("HUBSPOT_WRITE_GATE_ISSUE_RATE_LIMIT_WINDOW_SECONDS", "60")
)
HUBSPOT_WRITE_GATE_REQUIRED_VERSION = _get_env_stripped("HUBSPOT_WRITE_GATE_REQUIRED_VERSION", "")
HUBSPOT_WRITE_GATE_ENABLE_426 = (
    _get_env_stripped("HUBSPOT_WRITE_GATE_ENABLE_426", "false").lower() == "true"
)
HUBSPOT_WRITE_GATE_UPGRADE_URL = _get_env_stripped(
    "HUBSPOT_WRITE_GATE_UPGRADE_URL",
    "https://foundryops.io/pricing",
)
HUBSPOT_WRITE_PROXY_IDEMPOTENCY_TTL_SECONDS = int(
    _get_env_stripped("HUBSPOT_WRITE_PROXY_IDEMPOTENCY_TTL_SECONDS", "900")
)
HUBSPOT_WRITE_PROXY_TIMEOUT_SECONDS = float(
    _get_env_stripped("HUBSPOT_WRITE_PROXY_TIMEOUT_SECONDS", "30")
)
ZOOMINFO_CLIENT_ID = _get_env_stripped("ZOOMINFO_CLIENT_ID", "")
ZOOMINFO_CLIENT_SECRET = _get_env_stripped("ZOOMINFO_CLIENT_SECRET", "")
ZOOMINFO_OAUTH_REDIRECT_URI = _get_env_stripped("ZOOMINFO_OAUTH_REDIRECT_URI", "")
ZOOMINFO_OAUTH_SCOPES = _get_env_stripped(
    "ZOOMINFO_OAUTH_SCOPES",
    "",
)
ZOOMINFO_OAUTH_AUTHORIZE_URL = _get_env_stripped(
    "ZOOMINFO_OAUTH_AUTHORIZE_URL",
    "",
)
ZOOMINFO_OAUTH_TOKEN_URL = _get_env_stripped(
    "ZOOMINFO_OAUTH_TOKEN_URL",
    "",
)
ZOOMINFO_OAUTH_REVOKE_URL = _get_env_stripped("ZOOMINFO_OAUTH_REVOKE_URL", "")
ZOOMINFO_OAUTH_USERINFO_URL = _get_env_stripped("ZOOMINFO_OAUTH_USERINFO_URL", "")
ZOOMINFO_OAUTH_PROMPT = _get_env_stripped("ZOOMINFO_OAUTH_PROMPT", "")

# Globalization v1 - Prevents data loss for non-Latin scripts (Russian, Korean, Japanese)
GLOBALIZATION_V1_ENABLED = (
    os.getenv("GLOBALIZATION_V1_ENABLED", "true").lower() == "true"
)

# ──────────────────── Logging ─────────────────────────────
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO" if IS_PRODUCTION else "DEBUG")
LOG_FORMAT = os.getenv(
    "LOG_FORMAT", "%(asctime)s %(levelname)8s [%(name)s] %(message)s"
)

# ──────────────────── Email Settings (for alerts) ─────────────────────────────
# Email provider: resend, sendgrid, ses, smtp (default: none/dev mode)
EMAIL_PROVIDER = os.getenv("EMAIL_PROVIDER", "")

# Resend (recommended - https://resend.com)
RESEND_API_KEY = os.getenv("RESEND_API_KEY", "")
RESEND_FROM_EMAIL = os.getenv("RESEND_FROM_EMAIL", "notifications@foundryops.io")

# SMTP fallback
SMTP_HOST = os.getenv("SMTP_HOST", "smtp.gmail.com")
SMTP_PORT = int(os.getenv("SMTP_PORT", "587"))
SMTP_USERNAME = os.getenv("SMTP_USERNAME", "")
SMTP_PASSWORD = os.getenv("SMTP_PASSWORD", "")
ALERT_FROM_EMAIL = os.getenv("ALERT_FROM_EMAIL", "alerts@foundrymatch.com")
SMTP_USE_TLS = os.getenv("SMTP_USE_TLS", "true").lower() == "true"

# App URL for emails
APP_URL = os.getenv("APP_URL", "https://app.foundrymatch.com")
PUBLIC_BASE_URL = os.getenv("PUBLIC_BASE_URL", APP_URL)

# ──────────────────── ML/Smart Detection ─────────────────────────────
MLFLOW_TRACKING_URI = os.getenv("MLFLOW_TRACKING_URI", None)

# ──────────────────── Salesforce Integration ─────────────────────────────
SF_CLIENT_ID = os.getenv("SF_CLIENT_ID")
SF_CLIENT_SECRET = os.getenv("SF_CLIENT_SECRET")
# Also check for SALESFORCE_ prefixed versions
SALESFORCE_CLIENT_ID = os.getenv("SALESFORCE_CLIENT_ID", SF_CLIENT_ID)
SALESFORCE_CLIENT_SECRET = os.getenv("SALESFORCE_CLIENT_SECRET", SF_CLIENT_SECRET)
SF_REDIRECT_URI = os.getenv(
    "SF_REDIRECT_URI", "http://localhost:8000/api/v2/integrations/salesforce/callback"
)
SALESFORCE_REDIRECT_URI = os.getenv("SALESFORCE_REDIRECT_URI", SF_REDIRECT_URI)
SF_API_VERSION = os.getenv("SF_API_VERSION", "v60.0")
SF_AUTH_HOST = os.getenv(
    "SF_AUTH_HOST", "https://login.salesforce.com"
)  # Use https://test.salesforce.com for sandbox
# FRONTEND_ORIGIN already defined above with correct default of localhost:3004

# ──────────────────── Apollo OAuth ─────────────────────────────
APOLLO_CLIENT_ID = os.getenv("APOLLO_CLIENT_ID", "")
APOLLO_CLIENT_SECRET = os.getenv("APOLLO_CLIENT_SECRET", "")
APOLLO_REDIRECT_URL = os.getenv(
    "APOLLO_REDIRECT_URL",
    os.getenv(
        "APOLLO_REDIRECT_URL_SHEETS",
        "https://api.foundrymatch.com/api/v1/oauth/apollo/callback",
    ),
)

# ──────────────────── Slack Integration ─────────────────────────────
SLACK_BOT_TOKEN = os.getenv("SLACK_BOT_TOKEN")
SLACK_WEBHOOK_URL = os.getenv(
    "SLACK_WEBHOOK_URL", ""
)  # optional; leave empty to disable
SLACK_SIGNING_SECRET = os.getenv("SLACK_SIGNING_SECRET")

# ──────────────────── SMTP Settings (updated) ─────────────────────────────
SMTP_USER = os.getenv("SMTP_USER", os.getenv("SMTP_USERNAME", ""))
SMTP_FROM = os.getenv("SMTP_FROM", "notifications@foundrymatch.com")

# ──────────────────── Encryption ─────────────────────────────
# Generate with: from cryptography.fernet import Fernet; print(Fernet.generate_key())
ENCRYPTION_KEY = os.getenv("ENCRYPTION_KEY")

# ================== L2A Feature Settings ==================
# Undo time-to-live for single and bulk undo (in hours)
L2A_UNDO_TTL_HOURS = int(os.getenv("L2A_UNDO_TTL_HOURS", "24"))

# Retention window for L2A bulk jobs and stale suggestions (in days)
L2A_JOB_RETENTION_DAYS = int(os.getenv("L2A_JOB_RETENTION_DAYS", "60"))

# ================== C2A Feature Settings ==================
# Separate knobs for Contact -> Account automation so we can tune independently
C2A_BULK_APPROVAL_THRESHOLD = int(os.getenv("C2A_BULK_APPROVAL_THRESHOLD", "2000"))
C2A_BULK_CONCURRENCY_LIMIT = int(os.getenv("C2A_BULK_CONCURRENCY_LIMIT", "1"))
SAFE_APPROVE_DEFAULT = float(os.getenv("SAFE_APPROVE_DEFAULT", "0.80"))

# Optional Redis for distributed locks / queues (reuse REDIS_URL defined above)
C2A_UNDO_TTL_HOURS = int(os.getenv("C2A_UNDO_TTL_HOURS", "24"))

# ──────────────────── Webhooks ─────────────────────────────
# Webhook processing
WEBHOOK_TIMEOUT = int(os.getenv("WEBHOOK_TIMEOUT", "30"))  # seconds
WEBHOOK_MAX_RETRIES = int(os.getenv("WEBHOOK_MAX_RETRIES", "5"))
WEBHOOK_BATCH_SIZE = int(os.getenv("WEBHOOK_BATCH_SIZE", "100"))
WEBHOOK_WORKER_CONCURRENCY = int(os.getenv("WEBHOOK_WORKER_CONCURRENCY", "10"))

# Webhook security
WEBHOOK_SECRET = os.getenv("WEBHOOK_SECRET")  # Default secret for HMAC signatures
CLERK_SECRET_KEY = os.getenv("CLERK_SECRET_KEY")
WEBHOOK_RATE_LIMIT_PER_MINUTE = int(os.getenv("WEBHOOK_RATE_LIMIT_PER_MINUTE", "100"))
WEBHOOK_RATE_LIMIT_GLOBAL = int(os.getenv("WEBHOOK_RATE_LIMIT_GLOBAL", "1000"))

# Webhook endpoints
WEBHOOK_INGEST_ENABLED = os.getenv("WEBHOOK_INGEST_ENABLED", "true").lower() == "true"
WEBHOOK_SLACK_ENABLED = os.getenv("WEBHOOK_SLACK_ENABLED", "true").lower() == "true"

# ----- Public API v1 limits and caching -----
# Sync/async split thresholds
API_SYNC_MAX_RECORDS = int(os.getenv("API_SYNC_MAX_RECORDS", "100"))
API_SYNC_MAX_BYTES = int(os.getenv("API_SYNC_MAX_BYTES", "262144"))  # 256KB

# Tier defaults for QPS
API_QPS_DEFAULT = {
    "FREE": int(os.getenv("API_QPS_FREE", "2")),
    "PRO": int(os.getenv("API_QPS_PRO", "10")),
    "ENT": int(os.getenv("API_QPS_ENT", "50")),
}

# Cache and artifact TTLs
API_CACHE_TTL_SEC = int(os.getenv("API_CACHE_TTL_SEC", "0"))
ARTIFACT_TTL_SEC = int(os.getenv("ARTIFACT_TTL_SEC", "86400"))
WEBHOOK_HMAC_SECRET = os.getenv("WEBHOOK_HMAC_SECRET", "")
B2B_DETECTION = {
    "enabled": os.getenv("B2B_DETECTION_ENABLED", "true").lower() == "true",
    "min_anchor_confidence": float(
        os.getenv("B2B_DETECTION_MIN_ANCHOR_CONFIDENCE", "0.80")
    ),
    "require_two_anchors": os.getenv(
        "B2B_DETECTION_REQUIRE_TWO_ANCHORS", "true"
    ).lower()
    == "true",
    "fingerprint_auto_threshold": float(
        os.getenv("B2B_DETECTION_FINGERPRINT_AUTO_THRESHOLD", "0.90")
    ),
    "fingerprint_confirm_threshold": float(
        os.getenv("B2B_DETECTION_FINGERPRINT_CONFIRM_THRESHOLD", "0.70")
    ),
    "strict_mode": os.getenv("B2B_DETECTION_STRICT_MODE", "aggressive"),
    "learning_db": os.getenv("B2B_LEARNING_DB", "learning.duckdb"),
    "alias_promotion_threshold": int(os.getenv("B2B_ALIAS_PROMOTION_THRESHOLD", "5")),
    "max_candidates_shown": int(os.getenv("B2B_MAX_CANDIDATES", "3")),
    # New enhancement features
    "enable_industry_hints": os.getenv("B2B_ENABLE_INDUSTRY_HINTS", "true").lower()
    == "true",
    "enable_company_size_signal": os.getenv("B2B_ENABLE_COMPANY_SIZE", "true").lower()
    == "true",
    "enable_linkedin_detector": os.getenv("B2B_ENABLE_LINKEDIN", "true").lower()
    == "true",
    "enable_business_email_score": os.getenv(
        "B2B_ENABLE_BUSINESS_EMAIL", "true"
    ).lower()
    == "true",
}

# ================== V1 Match Gateway Integration ==================
# Routes Google Sheets v1 match API through MatchGateway for unified matching.
# See: dynamic-purring-sutherland.md for full migration plan.

# Main feature flags
V1_MATCH_USE_GATEWAY = os.getenv("V1_MATCH_USE_GATEWAY", "false").lower() == "true"
V1_MATCH_GATEWAY_SHADOW_MODE = (
    os.getenv("V1_MATCH_GATEWAY_SHADOW_MODE", "false").lower() == "true"
)
V1_MATCH_GATEWAY_KILL_SWITCH = (
    os.getenv("V1_MATCH_GATEWAY_KILL_SWITCH", "false").lower() == "true"
)

# Shadow mode sampling - deterministic hash-based
# Default 25% for fast validation; set to 100 for full shadow before cutover
V1_MATCH_GATEWAY_SHADOW_SAMPLE_PCT = int(
    os.getenv("V1_MATCH_GATEWAY_SHADOW_SAMPLE_PCT", "25")
)
V1_MATCH_GATEWAY_SHADOW_ACCOUNTS = [
    a.strip()
    for a in os.getenv("V1_MATCH_GATEWAY_SHADOW_ACCOUNTS", "").split(",")
    if a.strip()
]

# Rollout allowlist - separate from shadow accounts to avoid semantic collision
# Use this for limited rollout phase before full enablement
V1_MATCH_GATEWAY_ROLLOUT_ACCOUNTS = [
    a.strip()
    for a in os.getenv("V1_MATCH_GATEWAY_ROLLOUT_ACCOUNTS", "").split(",")
    if a.strip()
]


# Threshold - use gateway-native 0.8 default (not v1's 0.65)
# Tune based on parity logs after shadow validation
def _parse_v1_gateway_threshold() -> float:
    try:
        return float(os.getenv("V1_MATCH_GATEWAY_THRESHOLD", "0.8"))
    except (ValueError, TypeError):
        return 0.8


V1_MATCH_GATEWAY_THRESHOLD = _parse_v1_gateway_threshold()

# ================== BYO Data Provider Locker (Unleashed Tier) ==================
# KMS envelope encryption for workspace secrets
KMS_PROJECT_ID = os.getenv("KMS_PROJECT_ID") or os.getenv("GOOGLE_CLOUD_PROJECT", "")
KMS_LOCATION = os.getenv("KMS_LOCATION", "us-central1")
KMS_KEY_RING = os.getenv("KMS_KEY_RING", "foundrymatch-secrets")
KMS_CRYPTO_KEY = os.getenv("KMS_CRYPTO_KEY", "workspace-secrets")

# BYO enrichment settings
BYO_ENRICHMENT_MAX_ROWS = int(os.getenv("BYO_ENRICHMENT_MAX_ROWS", "1000"))
BYO_ENRICHMENT_CHUNK_SIZE = int(os.getenv("BYO_ENRICHMENT_CHUNK_SIZE", "50"))
BYO_ENRICHMENT_LEASE_SECONDS = int(os.getenv("BYO_ENRICHMENT_LEASE_SECONDS", "120"))
BYO_CONCURRENT_JOBS_DEFAULT = int(os.getenv("BYO_CONCURRENT_JOBS_DEFAULT", "10"))
# How long to keep queued enrichment input rows/meta in Redis
BYO_ENRICHMENT_ROW_TTL_SECONDS = int(os.getenv("BYO_ENRICHMENT_ROW_TTL_SECONDS", "86400"))
# MVP: Process jobs inline (synchronously) if row count is below this threshold
# Set to 0 to disable inline processing and require background worker
BYO_ENRICHMENT_INLINE_THRESHOLD = int(os.getenv("BYO_ENRICHMENT_INLINE_THRESHOLD", "100"))

# BYO scraper settings
SCRAPER_MAX_ROWS = int(os.getenv("SCRAPER_MAX_ROWS", "10000"))
SCRAPER_CHUNK_SIZE = int(os.getenv("SCRAPER_CHUNK_SIZE", "25"))
SCRAPER_LEASE_SECONDS = int(os.getenv("SCRAPER_LEASE_SECONDS", "120"))
SCRAPER_DATA_TTL_SECONDS = int(os.getenv("SCRAPER_DATA_TTL_SECONDS", "86400"))
SCRAPER_DATA_TTL_MAX_SECONDS = int(os.getenv("SCRAPER_DATA_TTL_MAX_SECONDS", "604800"))
SCRAPER_DATA_TTL_SECONDS = min(SCRAPER_DATA_TTL_SECONDS, SCRAPER_DATA_TTL_MAX_SECONDS)
SCRAPER_DATA_BUCKET = os.getenv("FM_SCRAPER_DATA_BUCKET", "fm-scraper-data")

# Locker admin gating: When True, only workspace admins can manage API credentials
# Set to False for MVP (all authenticated workspace users can manage credentials)
STRICT_LOCKER_ADMIN = os.getenv("STRICT_LOCKER_ADMIN", "false").lower() in ("true", "1", "yes")
