"""Master configuration — single source of truth for the whole pipeline.

Every constant the steps consume (paths, Bedrock model IDs, timeouts, agent
limits, accepted file extensions, API defaults) is defined here. Each step
imports from this module rather than maintaining its own copies.

Environment variables override the defaults: load .env *before* any step is
imported (main.py / orchestrator.py do this) and the values below pick them
up at import time.
"""
from __future__ import annotations

import os
from pathlib import Path


def _env(name: str, default: str | None = None) -> str | None:
    v = os.getenv(name)
    return v.strip() if v and v.strip() else default


def _env_int(name: str, default: int) -> int:
    v = _env(name)
    try:
        return int(v) if v else default
    except ValueError:
        return default


def _env_float(name: str, default: float) -> float:
    v = _env(name)
    try:
        return float(v) if v else default
    except ValueError:
        return default


# ═══════════════════════════════════════════════════════════════════════════
#  PATHS
# ═══════════════════════════════════════════════════════════════════════════

# dpg/  →  parents[1] from dpg/shared/config.py
ROOT_DIR     = Path(__file__).resolve().parents[1]
PIPELINE_DIR = ROOT_DIR / "pipeline"

# Per-step root directories.
STEP_01_DIR  = PIPELINE_DIR / "step-01-input-ingestion"
STEP_02_DIR  = PIPELINE_DIR / "step-02-prd-generation"
STEP_03_DIR  = PIPELINE_DIR / "step-03-backend-generation"
STEP_04_DIR  = PIPELINE_DIR / "step-04-ir-generation"
STEP_05_DIR  = PIPELINE_DIR / "step-05-react-generation"
STEP_06_DIR  = PIPELINE_DIR / "step-06-edit"
SYNC_DIR     = PIPELINE_DIR / "management-code"
TENANT_DIR   = PIPELINE_DIR / "management-tenant"
DB_DIR       = PIPELINE_DIR / "management-db"
INFRA_DIR    = PIPELINE_DIR / "management-infra"
MASTER_DIR   = PIPELINE_DIR / "master-pipeline"

# Per-step pipeline directories — added to sys.path by the orchestrator.
INPUT_INGESTION_PIPELINE_DIR = STEP_01_DIR / "pipeline"
PRD_GENERATION_PIPELINE_DIR  = STEP_02_DIR / "pipeline"
BACKEND_GEN_PIPELINE_DIR     = STEP_03_DIR / "pipeline"
IR_GEN_PIPELINE_DIR          = STEP_04_DIR / "pipeline"
REACT_GEN_PIPELINE_DIR       = STEP_05_DIR / "pipeline"
EDIT_PIPELINE_DIR            = STEP_06_DIR / "pipeline"
SYNC_PIPELINE_DIR            = SYNC_DIR    / "pipeline"
TENANT_PIPELINE_DIR          = TENANT_DIR  / "pipeline"
DB_PIPELINE_DIR              = DB_DIR      / "pipeline"
INFRA_PIPELINE_DIR           = INFRA_DIR   / "pipeline"
MASTER_PIPELINE_DIR          = MASTER_DIR  / "pipeline"

# Keep old alias so any external code still referencing FRONTEND_GEN_PIPELINE_DIR works.
FRONTEND_GEN_PIPELINE_DIR    = IR_GEN_PIPELINE_DIR

# Per-step output roots — each run namespaced as output/<run_id>/.
STEP_01_OUTPUT = STEP_01_DIR / "output"
STEP_02_OUTPUT = STEP_02_DIR / "output"
STEP_03_OUTPUT = STEP_03_DIR / "output"
STEP_04_OUTPUT = STEP_04_DIR / "output"
STEP_05_OUTPUT = STEP_05_DIR / "output"
STEP_06_OUTPUT = STEP_06_DIR / "output"
MASTER_OUTPUT  = MASTER_DIR  / "output"

# Drop-in inputs (PRD / DDL / images).
INPUTS_DIR = ROOT_DIR / "specs"

# Final assembled projects: runs/outputs/<project_slug>/{backend,frontend,ir}
RUNS_DIR             = ROOT_DIR / "runs" / "outputs"
DEFAULT_OUTPUT_DIR   = RUNS_DIR

# Edit pipeline operates on the React tree at <project>/frontend/src.
FRONTEND_SRC         = RUNS_DIR
FRONTEND_RUNS_OUTPUT = RUNS_DIR


# ═══════════════════════════════════════════════════════════════════════════
#  BEDROCK / AWS
# ═══════════════════════════════════════════════════════════════════════════

BEDROCK_REGION = (
    _env("BEDROCK_AWS_REGION")
    or _env("AWS_REGION")
    or _env("AWS_DEFAULT_REGION")
    or "us-east-1"
)

BEDROCK_CONNECT_TIMEOUT = _env_int("BEDROCK_CONNECT_TIMEOUT_SECONDS", 30)
BEDROCK_READ_TIMEOUT    = _env_int("BEDROCK_READ_TIMEOUT_SECONDS", 600)
BEDROCK_MAX_ATTEMPTS    = _env_int("BEDROCK_MAX_ATTEMPTS", 2)


# ═══════════════════════════════════════════════════════════════════════════
#  MODEL IDS  (Bedrock cross-region inference profiles)
# ═══════════════════════════════════════════════════════════════════════════

DEFAULT_SONNET_MODEL  = _env("BEDROCK_MODEL_ID",        "global.anthropic.claude-sonnet-4-5-20250929-v1:0")
DEFAULT_OPUS_MODEL    = _env("BEDROCK_OPUS_MODEL_ID",   "global.anthropic.claude-opus-4-6-v1")
DEFAULT_OPUS_47_MODEL = _env("BEDROCK_OPUS_47_MODEL_ID","global.anthropic.claude-opus-4-7")
DEFAULT_HAIKU_MODEL   = _env("BEDROCK_HAIKU_MODEL_ID",  "global.anthropic.claude-haiku-4-5-20251001-v1:0")

# Per-step model assignments.
IR_MODEL_ID          = _env("BEDROCK_IR_MODEL_ID",      DEFAULT_OPUS_MODEL)
REACT_MODEL_ID       = _env("BEDROCK_REACT_MODEL_ID",   DEFAULT_SONNET_MODEL)
PAGE_DETECTION_MODEL = _env("BEDROCK_PAGE_DETECT_MODEL",DEFAULT_OPUS_MODEL)
ROUTING_MODEL_ID     = _env("BEDROCK_ROUTING_MODEL",    DEFAULT_HAIKU_MODEL)
AGENT_MODEL_ID       = _env("AGENT_EDIT_MODEL",         DEFAULT_OPUS_MODEL)
BACKEND_MODEL_ID     = _env("BACKEND_BEDROCK_MODEL",    DEFAULT_OPUS_47_MODEL)


# ═══════════════════════════════════════════════════════════════════════════
#  AGENT (edit pipeline)
# ═══════════════════════════════════════════════════════════════════════════

AGENT_MAX_ITERATIONS = _env_int("AGENT_MAX_ITERATIONS", 40)
AGENT_MAX_TOKENS     = _env_int("AGENT_MAX_TOKENS",     8096)


# ═══════════════════════════════════════════════════════════════════════════
#  BACKEND GENERATION
# ═══════════════════════════════════════════════════════════════════════════

BACKEND_TEMPERATURE = _env_float("BEDROCK_TEMPERATURE", 0.0)


# ═══════════════════════════════════════════════════════════════════════════
#  INPUT INGESTION
# ═══════════════════════════════════════════════════════════════════════════

PRD_EXTENSIONS = {".pdf", ".pptx", ".docx", ".md", ".txt"}
DDL_EXTENSIONS = {".sql", ".ddl", ".txt"}
IMG_EXTENSIONS = {".png", ".jpg", ".jpeg", ".gif", ".webp"}


# ═══════════════════════════════════════════════════════════════════════════
#  FRONTEND
# ═══════════════════════════════════════════════════════════════════════════

DEFAULT_API_BASE_URL: str = _env("API_BASE_URL", "http://localhost:8000") or "http://localhost:8000"


# ═══════════════════════════════════════════════════════════════════════════
#  LOGGING
# ═══════════════════════════════════════════════════════════════════════════

LOG_LEVEL_DEFAULT = _env("LOG_LEVEL", "INFO")
LOG_FORMAT        = "%(asctime)s | %(name)s | %(levelname)s | %(message)s"
LOG_DATE_FORMAT   = "%H:%M:%S"


# ═══════════════════════════════════════════════════════════════════════════
#  GITLAB  (optional — only needed when management-code step is enabled)
# ═══════════════════════════════════════════════════════════════════════════

GITLAB_URL         = _env("GITLAB_URL")
GITLAB_ADMIN_TOKEN = _env("GITLAB_ADMIN_TOKEN")
GITLAB_ORG_CODE    = _env("GITLAB_ORG_CODE")


# ═══════════════════════════════════════════════════════════════════════════
#  EFS  (optional — only needed when management-code step is enabled)
# ═══════════════════════════════════════════════════════════════════════════

EFS_MOUNT    = _env("EFS_MOUNT", "/mnt/efs")
EFS_ENV      = _env("EFS_ENV")
POSTGRES_URL = _env("POSTGRES_URL")

DB_ADMIN_USER      = _env("DB_ADMIN_USER")
DB_ADMIN_PASSWORD  = _env("DB_ADMIN_PASSWORD")
DB_ADMIN_HOST      = _env("DB_ADMIN_HOST")
DB_MIGRATION_HOST  = _env("DB_MIGRATION_HOST", "127.0.0.1")


# ═══════════════════════════════════════════════════════════════════════════
#  TENANT ORCHESTRATOR  (optional — enables management-tenant step)
# ═══════════════════════════════════════════════════════════════════════════

ORCHESTRATOR_URL = _env("ORCHESTRATOR_URL", "http://orchestrator.llmatica.dalfin.ai")
TENANT_PASSWORD  = _env("TENANT_PASSWORD")


# ═══════════════════════════════════════════════════════════════════════════
#  JENKINS  (optional — only needed when Jenkins pipeline setup is enabled)
# ═══════════════════════════════════════════════════════════════════════════

JENKINS_URL               = _env("JENKINS_URL")
JENKINS_ADMIN_USER        = _env("JENKINS_ADMIN_USER")
JENKINS_ADMIN_TOKEN       = _env("JENKINS_ADMIN_TOKEN")
JENKINS_USERNAME          = _env("JENKINS_USERNAME")
JENKINS_USER_PASSWORD     = _env("JENKINS_USER_PASSWORD")
JENKINS_GIT_CREDENTIALS_ID = _env("JENKINS_GIT_CREDENTIALS_ID")


# ═══════════════════════════════════════════════════════════════════════════
#  RUNNER  (optional — second Jenkins-compatible pipeline instance)
# ═══════════════════════════════════════════════════════════════════════════

RUNNER_URL               = _env("RUNNER_URL")
RUNNER_ADMIN_USER        = _env("RUNNER_ADMIN_USER")
RUNNER_ADMIN_TOKEN       = _env("RUNNER_ADMIN_TOKEN")
RUNNER_GIT_CREDENTIALS_ID = _env("RUNNER_GIT_CREDENTIALS_ID")

# Runner frontend pipeline job template
RUNNER_FRONTEND_AGENT_LABEL      = _env("RUNNER_FRONTEND_AGENT_LABEL",      "agent-01")
RUNNER_FRONTEND_REPO_URL         = _env("RUNNER_FRONTEND_REPO_URL",         "git@gitlab.braintip.ai:braintip-ai/dalfin-ai/application/low-code/application.git")
RUNNER_FRONTEND_CHECKOUT_REF     = _env("RUNNER_FRONTEND_CHECKOUT_REF",     "move#dalfin-v.28")
RUNNER_FRONTEND_ENV_DIR          = _env("RUNNER_FRONTEND_ENV_DIR",          "packages/shell/src/.env")
RUNNER_FRONTEND_ENV_FILE         = _env("RUNNER_FRONTEND_ENV_FILE",         ".env.uat")
RUNNER_FRONTEND_ENV_SOURCE_DIR   = _env("RUNNER_FRONTEND_ENV_SOURCE_DIR",   "~/configs/dalfin/application/.env")
RUNNER_FRONTEND_PNPM_VERSION     = _env("RUNNER_FRONTEND_PNPM_VERSION",     "latest")
RUNNER_FRONTEND_INSTALL_COMMAND  = _env("RUNNER_FRONTEND_INSTALL_COMMAND",  "npx pnpm install --frozen-lockfile")
RUNNER_FRONTEND_BUILD_COMMAND    = _env("RUNNER_FRONTEND_BUILD_COMMAND",    "npx pnpm build:full:uat")
RUNNER_FRONTEND_S3_BUCKET        = _env("RUNNER_FRONTEND_S3_BUCKET",        "s3://app-uat-llmatica-917593900241-us-east-1-an")
RUNNER_FRONTEND_AWS_PROFILE      = _env("RUNNER_FRONTEND_AWS_PROFILE",      "917593900241-dev")
RUNNER_FRONTEND_MFE_SCRIPT_REPO  = _env("RUNNER_FRONTEND_MFE_SCRIPT_REPO",  "git@gitlab.braintip.ai:braintip-ai/dalfin-ai/application/low-code/scripts/mfe.git")
RUNNER_FRONTEND_MFE_SCRIPT_FILE  = _env("RUNNER_FRONTEND_MFE_SCRIPT_FILE",  "integrate-mfe.py")

# Runner backend pipeline job template
RUNNER_BACKEND_AGENT_LABEL      = _env("RUNNER_BACKEND_AGENT_LABEL",      "agent-01")
RUNNER_BACKEND_AWS_REGION       = _env("RUNNER_BACKEND_AWS_REGION",       "ap-southeast-4")
RUNNER_BACKEND_AWS_PROFILE      = _env("RUNNER_BACKEND_AWS_PROFILE",      "917593900241-dev")
RUNNER_BACKEND_AWS_ACCOUNT_ID   = _env("RUNNER_BACKEND_AWS_ACCOUNT_ID",   "917593900241")
RUNNER_BACKEND_IMAGE_TAG_PREFIX       = _env("RUNNER_BACKEND_IMAGE_TAG_PREFIX",       "uat")
RUNNER_BACKEND_K8S_NAMESPACE          = _env("RUNNER_BACKEND_K8S_NAMESPACE",          "uat")
RUNNER_BACKEND_K8S_CONTEXT            = _env("RUNNER_BACKEND_K8S_CONTEXT")
RUNNER_BACKEND_K8S_IMAGE_PULL_SECRET  = _env("RUNNER_BACKEND_K8S_IMAGE_PULL_SECRET")
RUNNER_BACKEND_API_DOMAIN             = _env("RUNNER_BACKEND_API_DOMAIN", "api.llmatica.dalfin.ai")
# ═══════════════════════════════════════════════════════════════════════════
#  INFRA  (management-infra step — S3 + CloudFront + ACM + Route53)
# ═══════════════════════════════════════════════════════════════════════════

# AWS region where S3 buckets are created (ACM cert is always us-east-1 for CF).
INFRA_AWS_REGION       = _env("INFRA_AWS_REGION", "us-east-1")
# 12-digit AWS account ID — used in bucket naming for global uniqueness.
INFRA_AWS_ACCOUNT_ID   = _env("INFRA_AWS_ACCOUNT_ID") or _env("RUNNER_BACKEND_AWS_ACCOUNT_ID", "917593900241")
# Route53 hosted zone ID for the app subdomain (e.g. the zone for app.llmatica.dalfin.ai).
INFRA_ROUTE53_HOSTED_ZONE_ID = _env("INFRA_ROUTE53_HOSTED_ZONE_ID")
# Base app domain; tenant subdomains are {tenant}.{INFRA_APP_DOMAIN}.
INFRA_APP_DOMAIN       = _env("INFRA_APP_DOMAIN", "app.llmatica.dalfin.ai")
# Path to the SQLite database that stores provisioned resource info.
INFRA_SQLITE_DB        = Path(_env("INFRA_SQLITE_DB") or "/mnt/efs/tenant_infra.db")


# ═══════════════════════════════════════════════════════════════════════════
#  PROMPT CACHING
# ═══════════════════════════════════════════════════════════════════════════

ENABLE_PROMPT_CACHING: bool = (os.environ.get("ENABLE_PROMPT_CACHING", "true") or "true").lower() == "true"
