# Kemoverse - a gacha-style bot for the Fediverse.
# Copyright © 2025 Waifu, VD15, Moon and contributors.

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.

# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see https://www.gnu.org/licenses/.

'''Essentials for the bot to function'''
import configparser
import json
import re
from os import environ, path


class ConfigError(Exception):
    '''Could not find config file'''


def get_config_file() -> str:
    '''Gets the path to the config file in the current environment'''
    env: str | None = environ.get('KEMOVERSE_ENV')
    if not env:
        raise ConfigError('Error: KEMOVERSE_ENV is unset')
    
    # Validate environment name contains only alphanumeric, dash, and underscore
    if not re.match(r'^[a-zA-Z0-9_-]+$', env):
        raise ValueError(f'KEMOVERSE_ENV "{env}" contains invalid characters. Only alphanumeric, dash (-), and underscore (_) are allowed.')

    config_path: str = f'config_{env}.ini'

    if not path.isfile(config_path):
        raise ConfigError(f'Could not find {config_path}')
    return config_path


def normalize_user(user_string: str) -> str:
    """
    Normalizes a user string to the format @user@domain.tld where domain is lowercase and user is case-sensitive
    
    Args:
        user_string: User string in various formats
        
    Returns:
        Normalized user string
        
    Raises:
        ValueError: If the user string is invalid or domain is malformed
    """
    if not user_string or not user_string.strip():
        raise ValueError("User string cannot be empty")
    
    user_string = user_string.strip()
    
    # Add leading @ if missing
    if not user_string.startswith('@'):
        user_string = '@' + user_string
    
    # Split into user and domain parts
    parts = user_string[1:].split('@', 1)  # Remove leading @ and split
    if len(parts) != 2:
        raise ValueError(f"Invalid user format: {user_string}. Expected @user@domain.tld")
    
    username, domain = parts
    
    if not username:
        raise ValueError("Username cannot be empty")
    
    if not domain:
        raise ValueError("Domain cannot be empty")
    
    # Validate domain format (basic check for valid domain structure)
    domain_pattern = r'^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$'
    if not re.match(domain_pattern, domain):
        raise ValueError(f"Invalid domain format: {domain}")
    
    # Return normalized format: @user@domain.tld (domain lowercase, user case-sensitive)
    return f"@{username}@{domain.lower()}"


def get_rarity_to_weight(
        config_section: configparser.SectionProxy) -> dict[int, float]:
    """Parses Rarity_X keys from config and returns a {rarity: weight} dict."""
    rarity_weights = {}
    for key, value in config_section.items():
        if key.startswith("rarity_"):
            rarity = int(key.removeprefix("rarity_"))
            rarity_weights[rarity] = float(value)
    return rarity_weights


config = configparser.ConfigParser()
config.read(get_config_file())

# Username for the bot
if 'User' not in config['credentials'] or not config['credentials']['User'].strip():
    raise ConfigError("User must be specified in config.ini under [credentials]")

USER = normalize_user(config['credentials']['User'])
# API key for the bot
KEY = config['credentials']['Token']
# Bot's Misskey/Pleroma instance URL
INSTANCE = config['credentials']['Instance'].lower()

# Instance type validation
if 'InstanceType' not in config['application']:
    raise ValueError("InstanceType must be specified in config.ini")

instance_type = config['application']['InstanceType'].lower()
if instance_type not in ('misskey', 'pleroma'):
    raise ValueError("InstanceType must be either 'misskey' or 'pleroma'")

INSTANCE_TYPE = instance_type

# Web server port
WEB_PORT = config['application'].getint('WebPort', 5000)
BIND_ADDRESS = config['application'].get('BindAddress', '127.0.0.1')

# Fedi handles in the traditional 'user@domain.tld' style, allows these users
# to use extra admin exclusive commands with the bot
ADMINS = json.loads(config['application']['DefaultAdmins'])
# SQLite Database location
DB_PATH = config['application'].get('DatabaseLocation', './gacha_game.db')
# Whether to enable the instance whitelist
USE_WHITELIST = config['application'].getboolean('UseWhitelist', True)

NOTIFICATION_POLL_INTERVAL = int(config['notification']['PollInterval'])
NOTIFICATION_BATCH_SIZE = int(config['notification']['BatchSize'])

GACHA_ROLL_INTERVAL = int(config['gacha']['RollInterval'])

RARITY_TO_WEIGHT = get_rarity_to_weight(config['gacha'])
