#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
模块名称：deploy_server.py
模块描述：部署目标服务器配置数据库模型定义

作者：czj
创建日期：2026-08-12
最后修改：2026-08-12
"""

import logging
from datetime import datetime
from typing import Optional

from cryptography.fernet import Fernet
from sqlalchemy import String, Integer, Text, DateTime
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.database import Base
from app.config import settings
from app.utils.id_generator import generate_id

logger = logging.getLogger(__name__)

# 加密密钥（与 device_sim_service.py 保持一致）
_ENCRYPTION_KEY = None


def _get_encryption_key() -> bytes:
    """获取 Fernet 加密密钥"""
    global _ENCRYPTION_KEY
    if _ENCRYPTION_KEY is None:
        key = getattr(settings, 'ENCRYPTION_KEY', None)
        if key:
            _ENCRYPTION_KEY = key.encode() if isinstance(key, str) else key
        else:
            _ENCRYPTION_KEY = Fernet.generate_key()
    return _ENCRYPTION_KEY


def encrypt_password(password: str) -> str:
    """加密密码"""
    f = Fernet(_get_encryption_key())
    return f.encrypt(password.encode()).decode()


def decrypt_password(password_encrypted: str) -> str:
    """解密密码"""
    try:
        f = Fernet(_get_encryption_key())
        return f.decrypt(password_encrypted.encode()).decode()
    except Exception as e:
        logger.error(f"密码解密失败: {e}")
        return ""


class DeployServer(Base):
    """
    部署目标服务器配置

    记录可 SSH 连接的远程服务器信息，用于自动化部署执行。

    Attributes:
        id (str): 服务器唯一标识
        name (str): 服务器名称
        host (str): 主机地址（IP 或域名）
        port (int): SSH 端口
        username (str): SSH 登录用户名
        password_encrypted (str): 加密后的密码
        auth_type (str): 认证类型 (password/key)
        private_key (str): SSH 私钥内容（key 认证时使用）
        status (str): 连接状态 (unknown/online/offline)
        description (str): 备注描述
        created_at (datetime): 创建时间
        updated_at (datetime): 更新时间
    """

    __tablename__ = "deploy_servers"

    id: Mapped[str] = mapped_column(
        String(64), primary_key=True, default=lambda: generate_id("srv"),
        comment="服务器唯一标识"
    )
    name: Mapped[str] = mapped_column(
        String(100), nullable=False, comment="服务器名称"
    )
    host: Mapped[str] = mapped_column(
        String(255), nullable=False, comment="主机地址"
    )
    port: Mapped[int] = mapped_column(
        Integer, default=22, comment="SSH 端口"
    )
    username: Mapped[str] = mapped_column(
        String(100), nullable=False, comment="SSH 登录用户名"
    )
    password_encrypted: Mapped[Optional[str]] = mapped_column(
        String(500), nullable=True, comment="加密后的密码"
    )
    auth_type: Mapped[str] = mapped_column(
        String(20), default="password", comment="认证类型: password/key"
    )
    private_key: Mapped[Optional[str]] = mapped_column(
        Text, nullable=True, comment="SSH 私钥内容"
    )
    status: Mapped[str] = mapped_column(
        String(20), default="unknown", comment="连接状态: unknown/online/offline"
    )
    description: Mapped[Optional[str]] = mapped_column(
        Text, nullable=True, comment="备注描述"
    )
    created_at: Mapped[datetime] = mapped_column(
        DateTime, default=datetime.utcnow, comment="创建时间"
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime, default=datetime.utcnow, onupdate=datetime.utcnow,
        comment="更新时间"
    )

    # 关联部署脚本（一对多）
    scripts: Mapped[list["DeployScript"]] = relationship(
        "DeployScript",
        back_populates="server",
        cascade="all, delete-orphan"
    )

    def __repr__(self) -> str:
        return f"<DeployServer(id={self.id}, name={self.name}, host={self.host}:{self.port})>"

    def to_dict(self) -> dict:
        return {
            "id": self.id,
            "name": self.name,
            "host": self.host,
            "port": self.port,
            "username": self.username,
            "auth_type": self.auth_type,
            "status": self.status,
            "description": self.description,
            "created_at": self.created_at.isoformat() if self.created_at else None,
            "updated_at": self.updated_at.isoformat() if self.updated_at else None,
        }