from functools import lru_cache
from typing import List

from pydantic import SecretStr, field_validator, model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
	model_config = SettingsConfigDict(
		env_file=".env",
		env_file_encoding="utf-8",
		case_sensitive=False,
		extra="ignore",
	)

	# security
	secret_key: str = "devsecret-change-me"

	# comma separated list of frontend origins allowed to call this api
	cors_origins: str = "*"

	# URL
	base_url: str = "https://experience.imaginxavr.com"
	root_url: str = "/edmentor-acc"

	# openai / llm
	# base url points at the internal inference server in production
	llm_base_url: str = "http://168.93.48.135:8080/v1"
	llm_bearer_token: SecretStr = SecretStr("ixgenie")
	llm_model: str = "llama3.1"
	llm_embed_model: str = "nomic-embed-text"
	llm_temperature: float = 0.3
	llm_timeout: int = 120

	# rag / chromadb
	chroma_persist_dir: str = "./data/chroma_db"
	kb_dir: str = "./data/knowledge_base"
	rag_top_k: int = 4
	rag_score_threshold: float = 0.25

	# session storage
	db_type: str = "file"
	data_dir: str = "./data/chat_sessions"
	mysql_url: str = "mysql+pymysql://user:password@localhost/chatbot"

	# app behaviour
	debug: bool = True
	min_response_seconds: float = 0.8

	# logging
	log_level: str = "INFO"
	log_file: str = ""

	# student api
	# external student api base url used for live lookup
	# leave blank to run in demo mode with the local fallback
	student_api_url: str = ""
	student_api_token: str = ""
	student_api_timeout: int = 5

	# email backend selector
	# which mailer to use, set EMAIL_BACKEND in .env
	#   "gmail_api" -> gmail api, oauth user flow
	#   "smtp"      -> smtp
	#   ""          -> disabled
	email_backend: str = "gmail_api"

	# gmail api settings
	gmail_credentials_file: str = "./secrets/credentials.json"
	gmail_token_file: str = "./secrets/token.json"
	gmail_tmp_state: str = "./secrets/gmail-state.txt"
	gmail_sender: str = ""

	# smtp settings
	smtp_host: str = ""
	smtp_port: int = 587
	smtp_user: str = ""
	smtp_password: str = ""
	smtp_from: str = ""

	@property
	def is_production(self) -> bool:
		return not self.debug

	@property
	def cors_origin_list(self) -> List[str]:
		# turn the comma separated string into a clean list
		if self.cors_origins.strip() == "*":
			return ["*"]
		return [o.strip() for o in self.cors_origins.split(",") if o.strip()]

	@field_validator("log_level")
	@classmethod
	def _normalise_log_level(cls, value: str) -> str:
		level = value.strip().upper()
		allowed = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}
		if level not in allowed:
			return "INFO"
		return level

	@model_validator(mode="after")
	def _check_production_safety(self) -> "Settings":
		# block insecure defaults when running outside debug mode
		if not self.debug:
			if self.secret_key == "devsecret-change-me" or not self.secret_key.strip():
				raise ValueError(
					"SECRET_KEY must be set to a strong value when DEBUG is off"
				)
			if self.cors_origins.strip() == "*":
				raise ValueError(
					"CORS_ORIGINS must list explicit frontend origins when DEBUG is off"
				)
		return self


@lru_cache(maxsize=1)
def get_settings() -> Settings:
	return Settings()