from flask import Blueprint, jsonify, request
import subprocess
import os

system_bp = Blueprint('system', __name__, url_prefix='/api/system')

# =============================================================================
# 安全常數
# =============================================================================

# 內部 API 金鑰（必須透過環境變數設定，不可為空）
_INTERNAL_API_KEY = os.environ.get('INTERNAL_API_KEY', '')

# 輸入數值邊界（防止過大/負數值觸發危險行為）
_MAX_SIZE_MB_LIMIT = 10_000      # 最大 10GB
_MIN_HOURS = 1                   # 最少 1 小時
_MAX_HOURS = 8_760               # 最多 365 天


def _require_internal_key():
    """
    驗證 X-Internal-Key 標頭。
    系統維護路由（cleanup、health）僅限內部服務呼叫。
    回傳 (ok: bool, error_response | None)
    """
    if not _INTERNAL_API_KEY:
        # 未設定金鑰時，拒絕所有請求（fail-secure）
        return False, (jsonify({
            'success': False,
            'error': '伺服器未設定 INTERNAL_API_KEY，拒絕存取'
        }), 503)

    provided = request.headers.get('X-Internal-Key', '')
    if not provided or provided != _INTERNAL_API_KEY:
        return False, (jsonify({
            'success': False,
            'error': '未授權：缺少或無效的 X-Internal-Key'
        }), 401)

    return True, None


def _validate_int(value, min_val, max_val, default):
    """
    驗證整數輸入在安全範圍內。
    非整數、超出範圍一律回傳 default。
    """
    try:
        v = int(value)
        if min_val <= v <= max_val:
            return v
    except (TypeError, ValueError):
        pass
    return default


# =============================================================================
# 系统清理与维护
# =============================================================================


@system_bp.route('/cleanup/docker', methods=['POST'])
def cleanup_docker():
    """Docker 系统清理（安全加固版，需 X-Internal-Key）"""
    ok, err = _require_internal_key()
    if not ok:
        return err

    data = request.get_json() or {}
    dry_run = bool(data.get('dry_run', False))
    confirm = bool(data.get('confirm', False))

    if not confirm:
        return jsonify({
            'success': False,
            'error': '缺少确认标记，请明确传递 confirm=true',
            'dry_run': dry_run
        }), 400

    # 靜態指令，不含任何使用者輸入
    cmd_parts = [
        'docker', 'system', 'prune',
        '-f',
        '--filter', 'until=24h'
    ]

    try:
        result = subprocess.run(
            cmd_parts,
            capture_output=True,
            text=True,
            timeout=120
        )

        return jsonify({
            'success': True,
            'dry_run': dry_run,
            'command': ' '.join(cmd_parts),
            'stdout': result.stdout,
            'stderr': result.stderr,
            'returncode': result.returncode
        })
    except subprocess.TimeoutExpired:
        return jsonify({
            'success': False,
            'error': '清理命令超时（120秒）',
            'dry_run': dry_run
        }), 504
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e),
            'dry_run': dry_run
        }), 500


@system_bp.route('/cleanup/logs', methods=['POST'])
def cleanup_logs():
    """日志目录清理（需 X-Internal-Key；數值輸入已嚴格驗證）"""
    ok, err = _require_internal_key()
    if not ok:
        return err

    data = request.get_json() or {}

    # 驗證並限制使用者輸入數值
    max_size_mb = _validate_int(
        data.get('max_size_mb', 500),
        min_val=1, max_val=_MAX_SIZE_MB_LIMIT, default=500
    )
    older_than_hours = _validate_int(
        data.get('older_than_hours', 168),
        min_val=_MIN_HOURS, max_val=_MAX_HOURS, default=168
    )
    dry_run = bool(data.get('dry_run', False))
    confirm = bool(data.get('confirm', False))

    if not confirm:
        return jsonify({
            'success': False,
            'error': '缺少确认标记，请明确传递 confirm=true',
            'dry_run': dry_run
        }), 400

    log_dir = '/var/log'
    if not os.path.isdir(log_dir):
        return jsonify({
            'success': False,
            'error': f'日志目录不存在: {log_dir}'
        }), 404

    try:
        # find 命令：全部使用 list 形式，數值已驗證為安全整數
        cmd = [
            'find', log_dir,
            '-type', 'f',
            '-mmin', f'+{older_than_hours * 60}',   # 加 + 表示「超過」
            '-size', f'+{max_size_mb}M',
            '-print0'
        ]

        if dry_run:
            check_cmd = cmd + ['-print']
            res = subprocess.run(
                check_cmd,
                capture_output=True,
                text=True,
                timeout=60
            )
            matched = [p for p in res.stdout.strip().split('\0') if p]
            return jsonify({
                'success': True,
                'dry_run': True,
                'matched_count': len(matched),
                'matched_paths': matched,
                'message': f'找到 {len(matched)} 个符合条件的日志文件（dry-run，未删除）'
            })

        # 實際清理：使用 -delete（比 -exec rm 更安全）
        delete_cmd = cmd + ['-delete']
        result = subprocess.run(
            delete_cmd,
            capture_output=True,
            text=True,
            timeout=120
        )

        return jsonify({
            'success': True,
            'dry_run': False,
            'stdout': result.stdout,
            'stderr': result.stderr,
            'returncode': result.returncode
        })
    except subprocess.TimeoutExpired:
        return jsonify({
            'success': False,
            'error': '清理命令超时（120秒）',
            'dry_run': dry_run
        }), 504
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e),
            'dry_run': dry_run
        }), 500


@system_bp.route('/cleanup/temp', methods=['POST'])
def cleanup_temp():
    """临时文件清理（需 X-Internal-Key；數值輸入已嚴格驗證）"""
    ok, err = _require_internal_key()
    if not ok:
        return err

    data = request.get_json() or {}
    older_than_hours = _validate_int(
        data.get('older_than_hours', 48),
        min_val=_MIN_HOURS, max_val=_MAX_HOURS, default=48
    )
    dry_run = bool(data.get('dry_run', False))
    confirm = bool(data.get('confirm', False))

    if not confirm:
        return jsonify({
            'success': False,
            'error': '缺少确认标记，请明确传递 confirm=true',
            'dry_run': dry_run
        }), 400

    temp_dirs = ['/tmp', '/var/tmp']
    results = []

    for base_dir in temp_dirs:
        if not os.path.isdir(base_dir):
            results.append({'dir': base_dir, 'skipped': True, 'reason': '目录不存在'})
            continue

        try:
            cmd = [
                'find', base_dir,
                '-type', 'f',
                '-mmin', f'+{older_than_hours * 60}',
                '-print0'
            ]

            if dry_run:
                check_cmd = cmd + ['-print']
                res = subprocess.run(check_cmd, capture_output=True, text=True, timeout=60)
                matched = [p for p in res.stdout.strip().split('\0') if p]
                results.append({
                    'dir': base_dir,
                    'dry_run': True,
                    'matched_count': len(matched),
                    'matched_paths': matched
                })
            else:
                delete_cmd = cmd + ['-delete']
                res = subprocess.run(
                    delete_cmd,
                    capture_output=True,
                    text=True,
                    timeout=120
                )
                results.append({
                    'dir': base_dir,
                    'dry_run': False,
                    'stdout': res.stdout,
                    'stderr': res.stderr,
                    'returncode': res.returncode
                })
        except subprocess.TimeoutExpired:
            results.append({
                'dir': base_dir,
                'error': '超时（120秒）',
                'dry_run': dry_run
            })
        except Exception as e:
            results.append({
                'dir': base_dir,
                'error': str(e),
                'dry_run': dry_run
            })

    return jsonify({
        'success': True,
        'results': results
    })


@system_bp.route('/health', methods=['GET'])
def health_check():
    """系统健康检查端点（需 X-Internal-Key）"""
    ok, err = _require_internal_key()
    if not ok:
        return err

    checks = {
        'disk': _check_disk_space,
        'docker': _check_docker,
        'temp_dirs': _check_temp_dirs
    }

    results = {}
    for name, checker in checks.items():
        results[name] = checker()

    overall = all(r.get('status') == 'ok' for r in results.values())

    # 使用 Python datetime 而非 subprocess date（避免不必要的 subprocess 呼叫）
    from datetime import datetime, timezone
    timestamp = datetime.now(timezone.utc).isoformat()

    return jsonify({
        'status': 'ok' if overall else 'degraded',
        'checks': results,
        'timestamp': timestamp
    })


# =============================================================================
# 内部辅助函数
# =============================================================================

def _check_disk_space():
    try:
        result = subprocess.run(
            ['df', '-h', '/'],
            capture_output=True,
            text=True,
            timeout=10
        )
        lines = result.stdout.strip().split('\n')
        if len(lines) < 2:
            return {'status': 'error', 'message': '无法解析磁盘信息'}
        usage = lines[1].split()
        pct = int(usage[4].replace('%', ''))
        status = 'ok' if pct < 80 else 'warning' if pct < 90 else 'critical'
        return {
            'status': status,
            'usage_percent': pct,
            'output': lines[1]
        }
    except Exception as e:
        return {'status': 'error', 'message': str(e)}


def _check_docker():
    try:
        result = subprocess.run(
            ['docker', 'info'],
            capture_output=True,
            text=True,
            timeout=10
        )
        ok = result.returncode == 0
        return {
            'status': 'ok' if ok else 'critical',
            'docker_available': ok
        }
    except Exception as e:
        return {'status': 'critical', 'error': str(e)}


def _check_temp_dirs():
    dirs = ['/tmp', '/var/tmp']
    results = []
    for d in dirs:
        exists = os.path.isdir(d) and os.access(d, os.R_OK | os.W_OK)
        results.append({
            'path': d,
            'exists': exists,
            'writable': os.access(d, os.W_OK) if exists else False
        })
    all_ok = all(r['exists'] and r['writable'] for r in results)
    return {
        'status': 'ok' if all_ok else 'critical',
        'details': results
    }
