from pydantic import model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict

_DEV_JWT_SECRET = "dev-only-insecure-secret-change-me"
_DEV_ENCRYPTION_KEY = "Mme1PORtZpZdxhhlPz-7mJ1EG8tr3vWyju7aDzyZ8wk="


class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", extra="ignore")

    # "production" refuses to start with the dev-only secrets below
    # (jwt_secret, workspace_encryption_key) — the Docker image sets it.
    environment: str = "development"

    # Whether this API process also runs background jobs and the scheduler
    # (app/core/jobs.py). False for API-only instances when separate
    # `python -m app.worker` processes do that work.
    run_jobs: bool = True
    # The queue this process's jobs go to and its worker takes them from —
    # separate worker pools (or a test run sharing a dev database) use their own.
    job_queue: str = "default"

    # Logs and metrics (app/core/observability.py). LOG_FORMAT "json" for a
    # log pipeline (the Docker image), "text" for a terminal. /metrics
    # needs this bearer token when it's set, and is off in production
    # until it is.
    log_format: str = "text"
    log_level: str = "INFO"
    metrics_token: str = ""

    database_url: str = "postgresql+asyncpg://flowfabric:flowfabric@localhost:5432/flowfabric"
    # The longest one SQL statement may run for a web request; 0 turns it off.
    # Background jobs and storage changes (app/crud/entity_storage.py) have no limit.
    request_statement_timeout_ms: int = 30_000
    # How long a dashboard summary of a very large source is reused before it's
    # refreshed in the background (app/crud/summary_cache.py); 0 always reads live.
    summary_cache_seconds: int = 900
    ollama_base_url: str = "http://localhost:11434"
    ollama_model: str = "llama3.1"
    cors_origins: str = "http://localhost:5173,http://localhost:3000"

    # Real JWT auth (see app/core/auth.py, app/core/security.py). No safe
    # default for jwt_secret — it must be overridden via .env/env vars
    # outside local dev, since anyone holding it can mint valid tokens.
    jwt_secret: str = _DEV_JWT_SECRET
    jwt_algorithm: str = "HS256"
    jwt_expiry_minutes: int = 480

    # AI Assistant inference provider (see app/routers/ai_assistant.py).
    # "ollama" (default) is the self-hosted path FR-AIA-006 requires — no
    # conversation or drafted schema leaves the network. "cloud" is an
    # explicit opt-in for any OpenAI-compatible /chat/completions host
    # (OpenRouter, Groq, Together, Fireworks, ...), useful when a
    # developer's machine can't run local inference; it is a deliberate
    # deviation from FR-AIA-006, never the shipped default.
    ai_provider: str = "ollama"
    cloud_provider_label: str = "OpenRouter"
    cloud_api_key: str = ""
    cloud_api_base_url: str = "https://openrouter.ai/api/v1"
    cloud_model: str = "meta-llama/llama-3.1-8b-instruct"

    # Page Designer AI Assist's screenshot-to-layout step (app/core/ocr.py) —
    # runs BEFORE the model ever sees anything, turning the attached image
    # into plain-text JSON, since ai_provider above is a text reasoning
    # model, not a vision one. "tesseract" (default) needs the `tesseract`
    # binary installed; tesseract_cmd only needs setting when it isn't on
    # PATH (e.g. its default Windows install location). "easyocr" is a
    # heavier, PyTorch-based alternative — install it separately (see
    # requirements.txt) before selecting it.
    ocr_engine: str = "tesseract"
    tesseract_cmd: str = ""
    ocr_languages: str = "eng"  # comma-separated (e.g. "eng,hin") — same format for either engine

    # Google reCAPTCHA v2 ("I'm not a robot") on the login screen
    # (app/core/recaptcha.py, POST /auth/login). Get a site key + secret key
    # at https://www.google.com/recaptcha/admin for the domain(s) FlowFabric
    # is served from — the site key is meant to be public (it ships to the
    # browser either way); only the secret key must stay server-side.
    # Enabled automatically once BOTH are set (see is_recaptcha_configured
    # below); both blank (the default) leaves every login unchanged.
    recaptcha_site_key: str = ""
    recaptcha_secret_key: str = ""
    # The sign-in / registration screens list every active tenant by name
    # (GET /auth/tenants) so people pick theirs instead of typing its ID.
    # Set PUBLIC_TENANT_LIST=false when tenant names shouldn't be visible to
    # anyone who opens the login page — the screens then ask for the ID.
    public_tenant_list: bool = True

    # Data Queries (app/crud/data_queries.py) call REST APIs from the server.
    # Addresses on this machine's own network (localhost, 10.x, 192.168.x, ...)
    # are refused unless this is set — so a query can't be pointed at internal
    # services. Cloud metadata addresses (169.254.x) are always refused.
    data_query_allow_private_hosts: bool = False

    # Public URL of the web app (e.g. https://app.example.com), used to
    # build the link in a new user's verification email (app/crud/
    # user_verification.py). Blank = the Origin of the browser request that
    # triggered the email, which is right whenever the UI and API are served
    # to users from the same place they're reached from.
    app_base_url: str = ""
    # The public website (app/routers/public_site.py). The site whose landing
    # page "/" shows to anyone not signed in, on a host no workspace has
    # claimed as its own domain (a workspace's site address, e.g. "acme").
    public_site_default: str = ""
    # Where the API fetches the web app's index.html from, to serve a public
    # page with the scripts that draw it. Blank: APP_BASE_URL/index.html,
    # else the Vite dev server (http://localhost:5173/index.html).
    public_site_app_index_url: str = ""
    # A TrueType font for PDFs made from form submissions (app/crud/
    # form_documents.py), so text in any script prints — e.g. Noto Sans
    # Malayalam, or Nirmala UI on Windows. Blank: the first of a few common
    # system fonts found, else the PDF base font (Latin letters only).
    pdf_font_path: str = ""

    # This API's own public origin, for URLs a payment gateway sends the
    # payer back to (/payments/callback/…, /payments/launch/…). Blank = the
    # origin each request reached the API on — right unless a proxy rewrites it.
    public_api_url: str = ""

    # Google Maps JavaScript API key for the "geolocation" field's map picker
    # (Datasources and Forms — frontend/src/components/GeoLocationInput.tsx).
    # A browser key, public by design (restrict it to your domains in Google
    # Cloud Console → Credentials); enable the "Maps JavaScript API" for it,
    # plus "Geocoding API" for the picker's address search. Blank = no map:
    # the picker falls back to typed coordinates / the browser's own location.
    google_maps_api_key: str = ""

    # Workspace provisioning (see app/core/db_provisioning.py): one
    # admin-configured Postgres identity with CREATE ROLE/CREATE DATABASE
    # privilege, reused against whatever host/port a Create Workspace
    # request specifies — the person creating a Workspace never types
    # superuser credentials themselves. No safe default outside dev, same
    # reasoning as jwt_secret above.
    provisioning_superuser: str = "postgres"
    provisioning_superuser_password: str = "postgres"

    # Fernet key (cryptography.fernet.Fernet.generate_key()) encrypting
    # Workspace.db_password_encrypted at rest (see app/core/crypto.py). This
    # dev-only default is a fixed, valid Fernet key so local dev works out
    # of the box — it MUST be overridden via .env/env vars outside local
    # dev, since anyone holding it can decrypt every stored Workspace
    # password.
    workspace_encryption_key: str = _DEV_ENCRYPTION_KEY

    @model_validator(mode="after")
    def _no_dev_secrets_in_production(self) -> "Settings":
        if self.environment == "production":
            dev = [name for name, value, default in (
                ("JWT_SECRET", self.jwt_secret, _DEV_JWT_SECRET),
                ("WORKSPACE_ENCRYPTION_KEY", self.workspace_encryption_key, _DEV_ENCRYPTION_KEY),
            ) if not value or value == default]
            if dev:
                raise ValueError(f"ENVIRONMENT=production needs its own {' and '.join(dev)} — the built-in values are for local development only")
        return self

    @property
    def cors_origin_list(self) -> list[str]:
        return [o.strip() for o in self.cors_origins.split(",") if o.strip()]

    @property
    def is_recaptcha_configured(self) -> bool:
        return bool(self.recaptcha_site_key.strip() and self.recaptcha_secret_key.strip())


settings = Settings()
