import os
from os import environ, path
from datetime import timedelta
import redis
import ssl
import tempfile
from ipaddress import ip_address

from common.utils.prompt_loader import load_prompt_templates

basedir = path.abspath(path.dirname(__file__))


class Config(object):
    DEBUG = False
    DEVELOPMENT = False
    SECRET_KEY = environ.get('SECRET_KEY')
    COMPONENT_NAME = environ.get('COMPONENT_NAME')

    # Database Settings ---------------------------------------------------------------------------
    DB_HOST = environ.get('DB_HOST')
    DB_USER = environ.get('DB_USER')
    DB_PASS = environ.get('DB_PASS')
    DB_NAME = environ.get('DB_NAME')
    DB_PORT = environ.get('DB_PORT')
    SQLALCHEMY_DATABASE_URI = f'postgresql+psycopg://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/{DB_NAME}'
    SQLALCHEMY_BINDS = {'public': SQLALCHEMY_DATABASE_URI}

    # Database Engine Options (health checks and keepalives)
    PGSQL_CERT_DATA = environ.get('PGSQL_CERT')
    PGSQL_CA_CERT_PATH = None
    if PGSQL_CERT_DATA:
        _tmp = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.pem')
        _tmp.write(PGSQL_CERT_DATA)
        _tmp.flush()
        _tmp.close()
        PGSQL_CA_CERT_PATH = _tmp.name

    # Psycopg3 connect args (libpq parameters)
    _CONNECT_ARGS = {
        'connect_timeout': 5,
        'keepalives': 1,
        'keepalives_idle': 60,
        'keepalives_interval': 30,
        'keepalives_count': 5,
    }
    if PGSQL_CA_CERT_PATH:
        _CONNECT_ARGS.update({
            'sslmode': 'require',
            'sslrootcert': PGSQL_CA_CERT_PATH,
        })

    SQLALCHEMY_ENGINE_OPTIONS = {
        'pool_pre_ping': True,
        'pool_recycle': 180,
        'pool_use_lifo': True,
        'connect_args': _CONNECT_ARGS,
    }

    # Redis Settings ------------------------------------------------------------------------------
    REDIS_URL = environ.get('REDIS_URL')
    REDIS_PORT = environ.get('REDIS_PORT', '6379')
    REDIS_USER = environ.get('REDIS_USER')
    REDIS_PASS = environ.get('REDIS_PASS')
    REDIS_CERT_DATA = environ.get('REDIS_CERT')
    REDIS_SCHEME = None

    # Determine if REDIS_URL is an IP; use it to control hostname checking
    REDIS_IS_IP = False
    try:
        ip_address(REDIS_URL)
        REDIS_IS_IP = True
    except Exception:
        REDIS_IS_IP = False
    REDIS_SSL_CHECK_HOSTNAME = not REDIS_IS_IP

    # Write CA once to a file, expose path
    REDIS_CA_CERT_PATH = None
    if REDIS_CERT_DATA:
        _tmp = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.pem')
        _tmp.write(REDIS_CERT_DATA)
        _tmp.flush()
        _tmp.close()
        REDIS_CA_CERT_PATH = _tmp.name

    if not REDIS_CERT_DATA:   # We are in a simple dev/test environment
        REDIS_SCHEME = 'redis'
        REDIS_BASE_URI = f'redis://{REDIS_URL}:{REDIS_PORT}'
    else:   # We are in a scaleway environment, providing name, user and certificate
        REDIS_SCHEME = 'rediss'
        REDIS_BASE_URI = f'rediss://{REDIS_USER}:{REDIS_PASS}@{REDIS_URL}:{REDIS_PORT}'

    # Central SSL options dict for reuse (Celery/Dogpile/etc.)
    REDIS_SSL_OPTIONS = None
    if REDIS_CERT_DATA and REDIS_CA_CERT_PATH:
        REDIS_SSL_OPTIONS = {
            'ssl_cert_reqs': ssl.CERT_REQUIRED,
            'ssl_ca_certs': REDIS_CA_CERT_PATH,
            'ssl_check_hostname': REDIS_SSL_CHECK_HOSTNAME,
        }

    # PubSub/EPT specific configuration (dedicated pool)
    REDIS_SPECIALIST_EXEC_DB = environ.get('REDIS_SPECIALIST_EXEC_DB', '0')
    REDIS_PUBSUB_MAX_CONNECTIONS = int(environ.get('REDIS_PUBSUB_MAX_CONNECTIONS', '200'))
    REDIS_PUBSUB_SOCKET_TIMEOUT = float(environ.get('REDIS_PUBSUB_SOCKET_TIMEOUT', '10'))
    REDIS_PUBSUB_CONNECT_TIMEOUT = float(environ.get('REDIS_PUBSUB_CONNECT_TIMEOUT', '3'))

    REDIS_PREFIXES = {
        'celery_app': 'celery:app:',
        'celery_chat': 'celery:chat:',
        'session': 'session:',
        'cache_workers': 'cache:workers:',
        'pubsub_execution': 'pubsub:execution:',
        'startup_ops': 'startup:ops:',
    }

    # Celery Redis settings
    CELERY_BROKER_URL = f'{REDIS_BASE_URI}/0'
    CELERY_RESULT_BACKEND = f'{REDIS_BASE_URI}/0'
    CELERY_BROKER_URL_CHAT = f'{REDIS_BASE_URI}/0'
    CELERY_RESULT_BACKEND_CHAT = f'{REDIS_BASE_URI}/0'

    # SSE PubSub settings
    SPECIALIST_EXEC_PUBSUB = f"{REDIS_BASE_URI}/{REDIS_SPECIALIST_EXEC_DB}"

    # eveai_model cache Redis setting
    MODEL_CACHE_URL = f'{REDIS_BASE_URI}/0'

    # Session Settings with Redis -----------------------------------------------------------------
    SESSION_TYPE = 'redis'
    SESSION_PERMANENT = True
    SESSION_USE_SIGNER = True
    PERMANENT_SESSION_LIFETIME = timedelta(minutes=60)
    SESSION_REFRESH_EACH_REQUEST = True
    # Configure SESSION_REDIS with SSL when cert is provided
    if REDIS_CERT_DATA and REDIS_CA_CERT_PATH:
        SESSION_REDIS = redis.from_url(
            f'{REDIS_BASE_URI}/0',  # REDIS_BASE_URI is reeds rediss://user:pass@host:port
            ssl_cert_reqs=ssl.CERT_REQUIRED,
            ssl_ca_certs=REDIS_CA_CERT_PATH,
            ssl_check_hostname=REDIS_SSL_CHECK_HOSTNAME,
        )
    else:
        SESSION_REDIS = redis.from_url(f'{REDIS_BASE_URI}/0')
    SESSION_KEY_PREFIX = f'session_{COMPONENT_NAME}:'
    SESSION_COOKIE_NAME = f'{COMPONENT_NAME}_session'
    SESSION_COOKIE_DOMAIN = None  # Laat Flask dit automatisch bepalen
    SESSION_COOKIE_PATH = '/'
    SESSION_COOKIE_HTTPONLY = True
    SESSION_COOKIE_SECURE = False  # True voor production met HTTPS
    SESSION_COOKIE_SAMESITE = 'Lax'
    REMEMBER_COOKIE_SAMESITE = 'strict'

    WTF_CSRF_ENABLED = True
    WTF_CSRF_TIME_LIMIT = None
    WTF_CSRF_SSL_STRICT = False  # Set to True if using HTTPS

    # flask-security-too settings -----------------------------------------------------------------
    # SECURITY_URL_PREFIX = '/admin'
    SECURITY_LOGIN_URL = '/admin/login'
    SECURITY_LOGOUT_URL = '/admin/logout'
    # SECURITY_REGISTER_URL = '/admin/register'
    # SECURITY_RESET_URL = '/admin/reset'
    # SECURITY_CHANGE_URL = '/admin/change'
    # SECURITY_POST_LOGIN_VIEW = '/admin/user/tenant_overview'
    # SECURITY_POST_LOGOUT_VIEW = '/admin'
    # SECURITY_POST_REGISTER_VIEW = '/admin/user/tenant_overview'
    # SECURITY_POST_RESET_VIEW = '/admin/login'
    # SECURITY_POST_CHANGE_VIEW = '/admin/login'
    # SECURITY_BLUEPRINT_NAME = 'security_bp'
    SECURITY_PASSWORD_SALT = environ.get('SECURITY_PASSWORD_SALT')
    SECURITY_CONFIRMABLE = True
    SECURITY_TRACKABLE = True
    SECURITY_PASSWORD_COMPLEXITY_CHECKER = 'zxcvbn'
    SECURITY_POST_LOGIN_VIEW = '/admin/user/tenant_overview'
    SECURITY_RECOVERABLE = True
    SECURITY_EMAIL_SENDER = "eveai_super@flow-it.net"
    SECURITY_EMAIL_SUBJECT_PASSWORD_RESET = 'Reset Your Password'
    SECURITY_EMAIL_SUBJECT_PASSWORD_NOTICE = 'Your Password Has Been Reset'
    SECURITY_EMAIL_PLAINTEXT = False
    SECURITY_EMAIL_HTML = True
    SECURITY_SESSION_PROTECTION = 'basic'  # of 'basic' als 'strong' problemen geeft
    SECURITY_REMEMBER_TOKEN_VALIDITY = timedelta(minutes=60)  # Zelfde als session lifetime
    SECURITY_AUTO_LOGIN_AFTER_CONFIRM = True
    SECURITY_AUTO_LOGIN_AFTER_RESET = True

    # Ensure Flask-Security-Too is handling CSRF tokens when behind a proxy
    SECURITY_CSRF_PROTECT_MECHANISMS = ['session']
    SECURITY_CSRF_COOKIE_NAME = 'XSRF-TOKEN'
    SECURITY_CSRF_HEADER = 'X-XSRF-TOKEN'
    WTF_CSRF_CHECK_DEFAULT = False

    # file upload settings ------------------------------------------------------------------------
    MAX_CONTENT_LENGTH = 50 * 1024 * 1024

    # supported languages -------------------------------------------------------------------------
    SUPPORTED_LANGUAGE_DETAILS = {
        "English": {
            "iso 639-1": "en",
            "iso 639-2": "eng",
            "iso 639-3": "eng",
            "flag": "🇬🇧"
        },
        "French": {
            "iso 639-1": "fr",
            "iso 639-2": "fre",  # of 'fra'
            "iso 639-3": "fra",
            "flag": "🇫🇷"
        },
        "German": {
            "iso 639-1": "de",
            "iso 639-2": "ger",  # of 'deu'
            "iso 639-3": "deu",
            "flag": "🇩🇪"
        },
        "Spanish": {
            "iso 639-1": "es",
            "iso 639-2": "spa",
            "iso 639-3": "spa",
            "flag": "🇪🇸"
        },
        "Italian": {
            "iso 639-1": "it",
            "iso 639-2": "ita",
            "iso 639-3": "ita",
            "flag": "🇮🇹"
        },
        "Portuguese": {
            "iso 639-1": "pt",
            "iso 639-2": "por",
            "iso 639-3": "por",
            "flag": "🇵🇹"
        },
        "Dutch": {
            "iso 639-1": "nl",
            "iso 639-2": "dut",  # of 'nld'
            "iso 639-3": "nld",
            "flag": "🇳🇱"
        },
        "Russian": {
            "iso 639-1": "ru",
            "iso 639-2": "rus",
            "iso 639-3": "rus",
            "flag": "🇷🇺"
        },
        "Chinese": {
            "iso 639-1": "zh",
            "iso 639-2": "chi",  # of 'zho'
            "iso 639-3": "zho",
            "flag": "🇨🇳"
        },
        "Japanese": {
            "iso 639-1": "ja",
            "iso 639-2": "jpn",
            "iso 639-3": "jpn",
            "flag": "🇯🇵"
        },
        "Korean": {
            "iso 639-1": "ko",
            "iso 639-2": "kor",
            "iso 639-3": "kor",
            "flag": "🇰🇷"
        },
        "Arabic": {
            "iso 639-1": "ar",
            "iso 639-2": "ara",
            "iso 639-3": "ara",
            "flag": "🇸🇦"
        },
        "Hindi": {
            "iso 639-1": "hi",
            "iso 639-2": "hin",
            "iso 639-3": "hin",
            "flag": "🇮🇳"
        },
    }

    # Afgeleide taalconstanten
    SUPPORTED_LANGUAGES = [lang_details["iso 639-1"] for lang_details in SUPPORTED_LANGUAGE_DETAILS.values()]
    SUPPORTED_LANGUAGES_FULL = list(SUPPORTED_LANGUAGE_DETAILS.keys())
    SUPPORTED_LANGUAGE_ISO639_1_LOOKUP = {lang_details["iso 639-1"]: lang_name for lang_name, lang_details in SUPPORTED_LANGUAGE_DETAILS.items()}

    # supported currencies ------------------------------------------------------------------------
    SUPPORTED_CURRENCIES = ['€', '$']

    # supported LLMs & settings -------------------------------------------------------------------
    # SUPPORTED_EMBEDDINGS = ['openai.text-embedding-3-small', 'openai.text-embedding-3-large', 'mistral.mistral-embed']
    SUPPORTED_EMBEDDINGS = ['mistral.mistral-embed']
    SUPPORTED_LLMS = ['mistral.mistral-large-latest', 'mistral.mistral-medium_latest', 'mistral.mistral-small-latest']

    # Annotation text chunk length
    ANNOTATION_TEXT_CHUNK_LENGTH = 10000

    # Environemnt Loaders
    OPENAI_API_KEY = environ.get('OPENAI_API_KEY')
    MISTRAL_API_KEY = environ.get('MISTRAL_API_KEY')

    # Celery settings (see above for Redis settings) ----------------------------------------------
    CELERY_TASK_SERIALIZER = 'json'
    CELERY_RESULT_SERIALIZER = 'json'
    CELERY_ACCEPT_CONTENT = ['json']
    CELERY_TIMEZONE = 'UTC'
    CELERY_ENABLE_UTC = True

    # JWT settings --------------------------------------------------------------------------------
    JWT_SECRET_KEY = environ.get('JWT_SECRET_KEY')
    JWT_ACCESS_TOKEN_EXPIRES = timedelta(hours=1)  # Set token expiry to 1 hour
    JWT_ACCESS_TOKEN_EXPIRES_DEPLOY = timedelta(hours=24)   # Set long-lived token for deployment

    # API Encryption ------------------------------------------------------------------------------
    API_ENCRYPTION_KEY = environ.get('API_ENCRYPTION_KEY')

    # Email settings for API key notifications ----------------------------------------------------
    PROMOTIONAL_IMAGE_URL = 'https://askeveai.com/wp-content/uploads/2024/07/Evie-Call-scaled.jpg'  # Replace with your actual URL

    # Type Definitions ----------------------------------------------------------------------------
    TENANT_TYPES = ['Active', 'Demo', 'Inactive', 'Test']
    CONSENT_TYPES = ["Data Privacy Agreement", "Terms & Conditions"]
    # CONSENT_TYPE_MAP maps names with the actual base folders the consent documents are stored in
    CONSENT_TYPE_MAP = {
        "Data Privacy Agreement": "dpa",
        "Terms & Conditions": "terms",
    }

    # The maximum number of seconds allowed for audio compression (to save resources)
    MAX_COMPRESSION_DURATION = 60*10    # 10 minutes
    # The maximum number of seconds allowed for transcribing audio
    MAX_TRANSCRIPTION_DURATION = 60*10  # 10 minutes
    # Maximum CPU usage for a compression task
    COMPRESSION_CPU_LIMIT = 50
    # Delay between compressing chunks in seconds
    COMPRESSION_PROCESS_DELAY = 1

    # WordPress Integration Settings
    WORDPRESS_PROTOCOL = environ.get('WORDPRESS_PROTOCOL', 'http')
    WORDPRESS_HOST = environ.get('WORDPRESS_HOST', 'host.docker.internal')
    WORDPRESS_PORT = environ.get('WORDPRESS_PORT', '10003')
    WORDPRESS_BASE_URL = f"{WORDPRESS_PROTOCOL}://{WORDPRESS_HOST}:{WORDPRESS_PORT}"
    EXTERNAL_WORDPRESS_BASE_URL = 'localhost:10003'

    # Prometheus PUSH Gataway
    PUSH_GATEWAY_HOST = environ.get('PUSH_GATEWAY_HOST', 'pushgateway')
    PUSH_GATEWAY_PORT = environ.get('PUSH_GATEWAY_PORT', '9091')
    PUSH_GATEWAY_URL = f"{PUSH_GATEWAY_HOST}:{PUSH_GATEWAY_PORT}"

    # Scaleway parameters
    SW_EMAIL_ACCESS_KEY = environ.get('SW_EMAIL_ACCESS_KEY')
    SW_EMAIL_SECRET_KEY = environ.get('SW_EMAIL_SECRET_KEY')
    SW_EMAIL_SENDER = environ.get('SW_EMAIL_SENDER')
    SW_EMAIL_NAME = environ.get('SW_EMAIL_NAME')
    SW_PROJECT = environ.get('SW_PROJECT')

    # Entitlement Constants
    ENTITLEMENTS_MAX_PENDING_DAYS = 5        # Defines the maximum number of days a pending entitlement can be active

    # Content Directory for static content like the changelog, terms & conditions, dpa statement, ...
    CONTENT_DIR = '/app/content'

    # Ensure health check endpoints are exempt from CSRF protection
    SECURITY_EXEMPT_URLS = [
        r'^/healthz($|/.*)',
        r'^/_healthz($|/.*)',
    ]
    SECURITY_LOGIN_WITHOUT_VIEWS = True  # Dit voorkomt automatische redirects

    # Define the nginx prefix used for the specific apps
    CHAT_CLIENT_PREFIX = 'chat-client/chat/'
    EVEAI_APP_PREFIX = 'admin/'
    # Whether to use dynamic fallback (X-Forwarded-Prefix/Referer) when EVEAI_APP_PREFIX is empty
    EVEAI_USE_DYNAMIC_PREFIX_FALLBACK = False

    # Consent guard configuration (config-driven whitelist)
    # List of endpoint names to exempt from the global consent guard
    # Example: ['security_bp.login', 'security_bp.logout', 'user_bp.tenant_consent']
    CONSENT_GUARD_EXEMPT_ENDPOINTS = []
    # List of endpoint name prefixes; any endpoint starting with one of these is exempt
    # Example: ['security_bp.', 'healthz_bp.']
    CONSENT_GUARD_EXEMPT_PREFIXES = []
    # TTL for consent status stored in session (seconds)
    CONSENT_SESSION_TTL_SECONDS = int(environ.get('CONSENT_SESSION_TTL_SECONDS', '45'))


class DevConfig(Config):
    DEVELOPMENT = True
    DEBUG = True
    FLASK_DEBUG = True
    EXPLAIN_TEMPLATE_LOADING = False

    # Define the static path
    STATIC_URL = None

    # PATH settings
    ffmpeg_path = '/usr/bin/ffmpeg'

    # OBJECT STORAGE
    OBJECT_STORAGE_TYPE = 'MINIO'
    OBJECT_STORAGE_TENANT_BASE = 'folder'
    OBJECT_STORAGE_BUCKET_NAME = 'eveai-tenants'
    # MINIO
    MINIO_ENDPOINT = 'minio:9000'
    MINIO_ACCESS_KEY = 'minioadmin'
    MINIO_SECRET_KEY = 'minioadmin'
    MINIO_USE_HTTPS = False


class TestConfig(Config):
    DEVELOPMENT = True
    DEBUG = True
    FLASK_DEBUG = True
    EXPLAIN_TEMPLATE_LOADING = False

    # Define the static path
    STATIC_URL = None

    # PATH settings
    ffmpeg_path = '/usr/bin/ffmpeg'

    # OBJECT STORAGE
    OBJECT_STORAGE_TYPE = 'MINIO'
    OBJECT_STORAGE_TENANT_BASE = 'folder'
    OBJECT_STORAGE_BUCKET_NAME = 'eveai-tenants'
    # MINIO
    MINIO_ENDPOINT = 'minio:9000'
    MINIO_ACCESS_KEY = 'minioadmin'
    MINIO_SECRET_KEY = 'minioadmin'
    MINIO_USE_HTTPS = False


class StagingConfig(Config):
    DEVELOPMENT = False
    DEBUG = True
    FLASK_DEBUG = True
    EXPLAIN_TEMPLATE_LOADING = False

    # Define the static path
    STATIC_URL = 'https://evie-staging-static.askeveai.com/'

    # PATH settings
    ffmpeg_path = '/usr/bin/ffmpeg'

    # OBJECT STORAGE
    OBJECT_STORAGE_TYPE = 'SCALEWAY'
    OBJECT_STORAGE_TENANT_BASE = 'folder'
    OBJECT_STORAGE_BUCKET_NAME = 'eveai-staging'
    # MINIO
    MINIO_ENDPOINT = environ.get('MINIO_ENDPOINT')
    MINIO_ACCESS_KEY = environ.get('MINIO_ACCESS_KEY')
    MINIO_SECRET_KEY = environ.get('MINIO_SECRET_KEY')
    MINIO_USE_HTTPS = True

    # Push gateway grouping elements
    pod_name = os.getenv('POD_NAME')
    pod_namespace = os.getenv('POD_NAMESPACE')


class ProdConfig(Config):
    DEVELOPMENT = False
    DEBUG = False
    FLASK_DEBUG = False
    EXPLAIN_TEMPLATE_LOADING = False

    # SESSION SETTINGS
    SESSION_COOKIE_SECURE = True

    WTF_CSRF_SSL_STRICT = True  # Set to True if using HTTPS

    # Define the nginx prefix used for the specific apps
    EVEAI_CHAT_LOCATION_PREFIX = 'EVEAI_APP_LOCATION_PREFIX'

    # Define the static path
    STATIC_URL = 'https://evie-prod-static.askeveai.com'

    # PATH settings
    ffmpeg_path = '/usr/bin/ffmpeg'

    # MINIO
    MINIO_ENDPOINT = environ.get('MINIO_ENDPOINT')
    MINIO_ACCESS_KEY = environ.get('MINIO_ACCESS_KEY')
    MINIO_SECRET_KEY = environ.get('MINIO_SECRET_KEY')
    MINIO_USE_HTTPS = True


def get_config(config_name='dev'):
    configs = {
        'dev': DevConfig,
        'test': TestConfig,
        'staging': StagingConfig,
        'prod': ProdConfig,
        'default': DevConfig,
    }
    return configs.get(config_name)
