#!/usr/bin/env python3
"""
Obsidian LiveSync CouchDB 配置生成器

此脚本用于生成运行 CouchDB 所需的配置文件，特别针对 Obsidian LiveSync 插件进行了优化配置。
"""

import os
import sys
import re
import getpass
import platform
from typing import Dict, Any, Optional, Tuple
from pathlib import Path

def get_current_user_ids() -> Tuple[str, str]:
    """
    获取当前用户的 UID 和 GID。
    在 Windows 上返回默认值，在 Linux/macOS 上返回实际值。
    """
    if platform.system() in ['Linux', 'Darwin']:  # Linux 或 macOS
        try:
            return str(os.getuid()), str(os.getgid())
        except AttributeError:
            pass
    # Windows 或其他系统返回默认值
    return "99", "100"

# 常量定义
DEFAULT_CONFIG = {
    "couchdb_user": "obsidian_user",
    "host_port": "5984",
    "volume_name": "couchdb_data",
    "config_host_path": "./etc/couchdb/local.d",
    "single_node": "true",
    "cors_origins": "app://obsidian.md,capacitor://localhost,http://localhost",
    "puid": get_current_user_ids()[0],  # 自动获取当前用户的 UID
    "pgid": get_current_user_ids()[1],  # 自动获取当前用户的 GID
    "tz": "Asia/Shanghai"
}

COMMON_TIMEZONES = {
    "1": "Asia/Shanghai",
    "2": "Asia/Singapore",
    "3": "America/New_York",
    "4": "Europe/London",
    "5": "Asia/Tokyo"
}

def validate_port(port: str) -> bool:
    """验证端口号是否有效。"""
    try:
        port_num = int(port)
        return 1 <= port_num <= 65535
    except ValueError:
        return False

def validate_username(username: str) -> bool:
    """验证用户名是否有效。"""
    return bool(re.match(r'^[a-zA-Z0-9_-]{3,32}$', username))

def validate_password(password: str) -> bool:
    """验证密码是否满足最低安全要求。"""
    if len(password) < 8:
        return False
    if not re.search(r'[A-Z]', password):
        return False
    if not re.search(r'[a-z]', password):
        return False
    if not re.search(r'[0-9]', password):
        return False
    return True

def get_input_with_default(prompt: str, default: str, validator: Optional[callable] = None) -> str:
    """获取用户输入，支持默认值和验证。"""
    while True:
        value = input(f"{prompt} [{default}]：").strip()
        if not value:
            value = default
        if validator and not validator(value):
            print(f"输入无效，请重试。")
            continue
        return value

def get_password() -> str:
    """安全地获取密码输入并进行确认。"""
    while True:
        password = getpass.getpass("请输入密码：").strip()
        if not validate_password(password):
            print("密码必须至少8个字符，并包含大写字母、小写字母和数字。")
            continue
        confirm = getpass.getpass("请确认密码：").strip()
        if password == confirm:
            return password
        print("两次输入的密码不一致，请重试。")

def get_timezone() -> str:
    """获取用户选择的时区。"""
    print("\n可用的时区：")
    for key, value in COMMON_TIMEZONES.items():
        print(f"   {key}. {value}")
    print("   （或输入自定义时区名称，例如：'Asia/Kuala_Lumpur'）")
    
    while True:
        choice = input(f"选择时区 [{DEFAULT_CONFIG['tz']}]：").strip()
        if not choice:
            return DEFAULT_CONFIG['tz']
        if choice in COMMON_TIMEZONES:
            return COMMON_TIMEZONES[choice]
        # 基本验证自定义时区
        if '/' in choice and all(part.isalnum() or part in '_-' for part in choice.split('/')):
            return choice
        print("时区格式无效，请重试。")

def generate_docker_compose(config: Dict[str, Any]) -> str:
    """生成 docker-compose.yml 内容。"""
    return f"""# 为 Obsidian LiveSync 生成的 docker-compose.yml
# 使用最新的 Docker Compose 规范

services:
  couchdb-obsidian-livesync:
    container_name: couchdb-obsidian-livesync
    image: couchdb:3.3.3
    environment:
      PUID: {config['puid']}
      PGID: {config['pgid']}
      UMASK: 0022
      TZ: {config['tz']}
      COUCHDB_USER: {config['couchdb_user']}
      COUCHDB_PASSWORD: {config['couchdb_password']}
    volumes:
      - {config['volume_name']}:/opt/couchdb/data
      - {config['config_host_path']}:/opt/couchdb/etc/local.d
    ports:
      - "{config['host_port']}:5984"
    restart: unless-stopped
    networks:
      - couchdb_network

volumes:
  {config['volume_name']}:

networks:
  couchdb_network:
    driver: bridge
"""

def generate_couchdb_config(config: Dict[str, Any]) -> str:
    """生成 CouchDB local.ini 内容。"""
    return f"""[couchdb]
single_node={config['single_node']}
max_document_size = 50000000

[chttpd]
require_valid_user = true
max_http_request_size = 4294967296

[chttpd_auth]
require_valid_user = true
authentication_redirect = /_utils/session.html

[httpd]
WWW-Authenticate = Basic realm="couchdb"
enable_cors = true

[cors]
origins = {config['cors_origins']}
credentials = true
headers = accept, authorization, content-type, origin, referer
methods = GET, PUT, POST, HEAD, DELETE
max_age = 3600
"""

def save_config_files(config: Dict[str, Any]) -> None:
    """保存所有配置文件。"""
    # 创建配置目录
    config_dir = Path(config['config_host_path'])
    config_dir.mkdir(parents=True, exist_ok=True)

    # 保存 docker-compose.yml
    with open('docker-compose.yml', 'w') as f:
        f.write(generate_docker_compose(config))
    print(f"\n✓ 已生成 docker-compose.yml")

    # 保存 CouchDB 配置
    config_path = config_dir / "my_obsidian_config.ini"
    with open(config_path, 'w') as f:
        f.write(generate_couchdb_config(config))
    print(f"✓ 已生成 {config_path}")

def main():
    """主函数，运行配置生成器。"""
    print("=== Obsidian LiveSync CouchDB 配置生成器 ===")
    print("此脚本将帮助您设置用于 Obsidian LiveSync 的 CouchDB。")
    print("随时可以按 Ctrl+C 退出。\n")

    try:
        config = DEFAULT_CONFIG.copy()

        # 获取用户名
        config['couchdb_user'] = get_input_with_default(
            "请输入 CouchDB 管理员用户名",
            DEFAULT_CONFIG['couchdb_user'],
            validate_username
        )

        # 获取密码
        print("\n请输入 CouchDB 管理员密码（必须至少8个字符，包含大写字母、小写字母和数字）")
        config['couchdb_password'] = get_password()

        # 获取时区
        config['tz'] = get_timezone()

        # 获取端口
        config['host_port'] = get_input_with_default(
            "请输入 CouchDB 主机端口",
            DEFAULT_CONFIG['host_port'],
            validate_port
        )

        # 获取卷名称
        config['volume_name'] = get_input_with_default(
            "请输入 CouchDB 数据卷名称",
            DEFAULT_CONFIG['volume_name']
        )

        # 获取配置路径
        config['config_host_path'] = get_input_with_default(
            "请输入配置文件目录路径",
            DEFAULT_CONFIG['config_host_path']
        )

        # 获取单节点模式
        single_node = get_input_with_default(
            "是否使用单节点模式（true/false）",
            DEFAULT_CONFIG['single_node']
        ).lower()
        config['single_node'] = "true" if single_node in ["true", "t", "yes", "y"] else "false"

        # 获取 CORS 源
        config['cors_origins'] = get_input_with_default(
            "请输入允许的 CORS 源（用逗号分隔）",
            DEFAULT_CONFIG['cors_origins']
        )

        # 获取 PUID/PGID
        current_uid, current_gid = get_current_user_ids()
        if platform.system() in ['Linux', 'Darwin']:
            print(f"\n检测到当前用户 ID：UID={current_uid}, GID={current_gid}")
            print("这些值将用于设置容器内的文件权限。")
            use_current = get_input_with_default(
                "是否使用当前用户的 UID/GID（推荐）",
                "yes"
            ).lower() in ["yes", "y", "true", "t"]
            
            if use_current:
                config['puid'] = current_uid
                config['pgid'] = current_gid
            else:
                print("\n请输入自定义的 PUID/PGID")
                config['puid'] = get_input_with_default(
                    "请输入 PUID",
                    current_uid,
                    lambda x: x.isdigit()
                )
                config['pgid'] = get_input_with_default(
                    "请输入 PGID",
                    current_gid,
                    lambda x: x.isdigit()
                )
        else:
            print("\n当前系统不支持自动获取用户 ID，将使用默认值（99/100）")
            print("如果需要自定义，请手动输入。")
            config['puid'] = get_input_with_default(
                "请输入 PUID",
                DEFAULT_CONFIG['puid'],
                lambda x: x.isdigit()
            )
            config['pgid'] = get_input_with_default(
                "请输入 PGID",
                DEFAULT_CONFIG['pgid'],
                lambda x: x.isdigit()
            )

        # 保存所有配置文件
        save_config_files(config)

        print("\n=== 配置完成 ===")
        print(f"✓ CouchDB 将在 http://localhost:{config['host_port']}/_utils/ 访问")
        print(f"✓ 用户名：{config['couchdb_user']}")
        print(f"✓ 文件权限：UID={config['puid']}, GID={config['pgid']}")
        print("\n要启动 CouchDB，请运行：")
        print("  docker-compose up -d")
        print("\n要查看日志，请运行：")
        print("  docker-compose logs -f")

    except KeyboardInterrupt:
        print("\n\n配置已取消。")
        sys.exit(1)
    except Exception as e:
        print(f"\n错误：{str(e)}")
        sys.exit(1)

if __name__ == "__main__":
    main()