from __future__ import annotations

from functools import lru_cache
from pathlib import Path
from uuid import uuid4

from pydantic import AliasChoices, Field, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict

from app.utils.cluster_table_names import call_history_table

# Load campaign_port/.env first, then repo backend/.env (same keys as Django).
_REPO_ROOT = Path(__file__).resolve().parent.parent.parent
_BACKEND_ENV = _REPO_ROOT / "backend" / ".env"
_ENV_FILES = (".env", str(_BACKEND_ENV)) if _BACKEND_ENV.is_file() else (".env",)


class Settings(BaseSettings):
    model_config = SettingsConfigDict(
        env_file=_ENV_FILES,
        env_file_encoding="utf-8",
        extra="ignore",
    )

    app_name: str = "campaign-port"
    debug: bool = False

    # Master DB (businesses, did_numbers, scheduled_campaigns) — same as Django DATABASE_MASTER_URL
    database_url: str = Field(
        default="mysql+pymysql://user:pass@127.0.0.1:3306/livekitvoicebot_master",
        validation_alias=AliasChoices("DATABASE_MASTER_URL", "database_url"),
    )

    # Cluster DB (livekitvoicebot_cluster): same names as backend/apps/cluster/dynamic_tables.py
    cluster_database_url: str = Field(
        default="mysql+pymysql://user:pass@127.0.0.1:3306/livekitvoicebot_cluster",
        validation_alias=AliasChoices("DATABASE_CLUSTER_URL", "cluster_database_url"),
    )

    timezone: str = "Asia/Kolkata"

    # Outbound API (matches Laravel cURL)
    outbound_call_url: str = "https://config.mcube.com/Restmcube-api/outbound-calls-superdash"
    outbound_bearer_token: str = Field(
        default="",
        validation_alias=AliasChoices(
            "HTTP_AUTHORIZATION",
            "OUTBOUND_BEARER_TOKEN",
            "MCUBE_HTTP_AUTHORIZATION",
        ),
    )

    # Public base URL used for MCube callbacks (webhook).
    mcube_public_base_url: str = Field(
        default="",
        validation_alias=AliasChoices("MCUBE_PUBLIC_BASE_URL", "PUBLIC_BASE_URL"),
    )

    # MCube outbound helper: public URL (Apache/Nginx -> :8088) or http://127.0.0.1:8088 for local-only tests.
    mcube_local_outbound_url: str = Field(
        default="https://app3.syntheon.in/api/mcube/outbound-call",
        validation_alias=AliasChoices("MCUBE_LOCAL_OUTBOUND_URL", "MCUBE_OUTBOUND_CALL_URL"),
    )

    # WebSocket base for refurl (per backend .env); refurl = {base}{prefix}/{uuid}
    mcube_public_ws_url_base: str = Field(
        default="",
        validation_alias=AliasChoices("MCUBE_PUBLIC_WS_URL_BASE", "PUBLIC_WS_URL_BASE"),
    )
    mcube_ws_path_prefix: str = Field(
        default="/ws",
        validation_alias="MCUBE_WS_PATH_PREFIX",
    )

    celery_broker_url: str = Field(
        default="redis://127.0.0.1:6379/0",
        validation_alias=AliasChoices("CELERY_BROKER_URL", "REDIS_URL"),
    )
    celery_result_backend: str = Field(
        default="redis://127.0.0.1:6379/1",
        validation_alias="CELERY_RESULT_BACKEND",
    )

    # RabbitMQ (optional; mirrors Laravel publisher)
    rabbitmq_url: str = Field(default="", validation_alias="RABBITMQ_URL")
    rabbitmq_exchange: str = "campaign_runs"

    # Queue names
    queue_first_campaign: str = "firstCampaigncall"
    queue_priority: str = "priorityQueue"
    queue_retry_calls: str = "retryCalls"

    # Dev auth: header overrides when no JWT
    dev_business_id: int | None = None
    dev_user_id: int | None = None
    dev_user_name: str | None = None

    @field_validator("database_url", "cluster_database_url", mode="before")
    @classmethod
    def normalize_mysql_driver(cls, v: object) -> object:
        """Allow mysql:// URLs from .env (Django style) with PyMySQL driver."""
        if isinstance(v, str) and v.startswith("mysql://"):
            return "mysql+pymysql://" + v[len("mysql://") :]
        return v


# Alias for older imports; same as `livekitvoicebot_cluster.{bid}_call_history`.
call_history_table_name = call_history_table


def build_mcube_refurl(settings: Settings | None = None) -> str:
    """
    refurl for outbound payload: {MCUBE_PUBLIC_WS_URL_BASE}{MCUBE_WS_PATH_PREFIX}/{uuid}
    Matches backend .env (no separate outbound_ws_base).
    """
    s = settings or get_settings()
    base = (s.mcube_public_ws_url_base or "").strip().rstrip("/")
    prefix = (s.mcube_ws_path_prefix or "/ws").strip()
    if not prefix.startswith("/"):
        prefix = "/" + prefix
    if not base:
        raise ValueError(
            "MCUBE_PUBLIC_WS_URL_BASE is not set (needed to build refurl for outbound calls). "
            "Set it in campaign_port .env or export from backend .env."
        )
    return f"{base}{prefix}/{uuid4()}"


def build_mcube_refurl_for_call(call_id: str, settings: Settings | None = None) -> str:
    """
    refurl for outbound payload that points to the WS bridge for a specific call id:
    {MCUBE_PUBLIC_WS_URL_BASE}{MCUBE_WS_PATH_PREFIX}/{call_id}
    """
    s = settings or get_settings()
    base = (s.mcube_public_ws_url_base or "").strip().rstrip("/")
    prefix = (s.mcube_ws_path_prefix or "/ws").strip()
    if not prefix.startswith("/"):
        prefix = "/" + prefix
    if not base:
        raise ValueError("MCUBE_PUBLIC_WS_URL_BASE is not set (needed to build refurl).")
    return f"{base}{prefix}/{call_id}"


def build_mcube_callback_url(settings: Settings | None = None) -> str:
    """
    callback_url for outbound payload:
    {MCUBE_PUBLIC_BASE_URL}/webhooks/mcube
    """
    s = settings or get_settings()
    base = (s.mcube_public_base_url or "").strip().rstrip("/")
    if not base:
        raise ValueError("MCUBE_PUBLIC_BASE_URL is not set (needed to build callback_url).")
    return f"{base}/webhooks/mcube"


@lru_cache
def get_settings() -> Settings:
    return Settings()
