from flask import Blueprint, render_template, request, jsonify
from backend.models.user import db
from backend.models.idrac_server import IdracServer
from backend.models.server_config import ServerConfigSnapshot
from backend.services.idrac_redfish_client import DellRedfishClient
import concurrent.futures
import json
import hashlib
from datetime import datetime

server_config_bp = Blueprint('server_config', __name__, url_prefix='/server-config')

@server_config_bp.route('/')
def index():
    """서버 설정 분석 및 비교 페이지"""
    return render_template('server_config.html')

def calculate_checksum(data):
    """JSON 데이터의 SHA256 체크섬 계산"""
    json_str = json.dumps(data, sort_keys=True)
    return hashlib.sha256(json_str.encode('utf-8')).hexdigest()

@server_config_bp.route('/api/sync', methods=['POST'])
def sync_configs():
    """
    선택한 서버들의 설정을 iDRAC에서 가져와 DB에 저장 (동기화)
    """
    try:
        data = request.json
        server_ids = data.get('server_ids', [])
        categories = data.get('categories', ['bios', 'idrac', 'system'])
        
        if not server_ids:
            return jsonify({'success': False, 'message': '서버를 선택해주세요.'})

        results = {'success': [], 'failed': []}
        
        def process_server(server_id):
            server = IdracServer.query.get(server_id)
            if not server:
                return {'id': server_id, 'status': 'failed', 'message': 'Server not found'}
            
            client = DellRedfishClient(server.ip_address, server.username, server.password)
            if not client.check_connection():
                return {'id': server.id, 'name': server.name, 'status': 'failed', 'message': 'Connection failed'}
            
            sync_results = []
            
            # System Info
            if 'system' in categories:
                sys_data = client.get_system_info_detailed()
                if sys_data:
                    save_snapshot(server.id, 'system', sys_data)
                    sync_results.append('system')
            
            # BIOS Attributes
            if 'bios' in categories:
                bios_data = client.get_bios_attributes_all()
                if bios_data:
                    save_snapshot(server.id, 'bios', bios_data)
                    sync_results.append('bios')
            
            # iDRAC Attributes
            if 'idrac' in categories:
                idrac_data = client.get_idrac_attributes_all()
                if idrac_data:
                    save_snapshot(server.id, 'idrac', idrac_data)
                    sync_results.append('idrac')
                    
            return {'id': server.id, 'name': server.name, 'status': 'success', 'synced': sync_results}

        def save_snapshot(server_id, config_type, data):
            # 기존 스냅샷 확인
            snapshot = ServerConfigSnapshot.query.filter_by(
                server_id=server_id, 
                config_type=config_type
            ).first()
            
            new_hash = calculate_checksum(data)
            
            if snapshot:
                # 변경사항이 있는 경우에만 업데이트
                if snapshot.hash_value != new_hash:
                    snapshot.data = data
                    snapshot.hash_value = new_hash
                    snapshot.updated_at = datetime.utcnow() # 명시적 업데이트 시간 갱신
            else:
                snapshot = ServerConfigSnapshot(
                    server_id=server_id,
                    config_type=config_type,
                    data=data,
                    hash_value=new_hash
                )
                db.session.add(snapshot)
            
            # 트랜잭션은 개별 commit 혹은 묶어서 commit. 
            # 여기서는 스레드 안전성을 위해 함수 내에서는 session 조작만 하고
            # 실제 commit은 메인 스레드나 별도 처리 필요하지만, Flask-SQLAlchemy는 scoped_session이므로
            # 각 요청(스레드)마다 세션이 다를 수 있음. 
            # 하지만 ThreadPoolExecutor 사용 시 app context 문제가 발생할 수 있음.
            # safe way: return data -> main thread saves. OR push app context.
            
            # NOTE: For simplicity in this refactor with ThreadPoolExecutor, 
            # we will handle DB operations inside the thread but need app context.
            # See below for implementation adjustment.
            return True

        # DB 저장을 위해 app context 필요
        from flask import current_app
        app = current_app._get_current_object()
        
        def process_with_context(server_id):
            with app.app_context():
                try:
                    res = process_server(server_id)
                    db.session.commit() # Commit per server
                    return res
                except Exception as e:
                    db.session.rollback()
                    return {'id': server_id, 'status': 'failed', 'message': str(e)}

        with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
            future_to_id = {executor.submit(process_with_context, sid): sid for sid in server_ids}
            
            for future in concurrent.futures.as_completed(future_to_id):
                try:
                    res = future.result()
                    if res['status'] == 'success':
                        results['success'].append(res)
                    else:
                        results['failed'].append(res)
                except Exception as e:
                    results['failed'].append({'error': str(e)})

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

    except Exception as e:
        return jsonify({'success': False, 'message': f"Sync Error: {str(e)}"})

@server_config_bp.route('/api/fetch', methods=['POST'])
def fetch_configs():
    """
    DB에 저장된 서버 설정 스냅샷 조회
    """
    try:
        data = request.json
        server_ids = data.get('server_ids', [])
        categories = data.get('categories', ['bios', 'idrac', 'system'])
        
        if not server_ids:
            return jsonify({'success': False, 'message': '서버를 선택해주세요.'})

        results = {}
        
        for server_id in server_ids:
            server = IdracServer.query.get(server_id)
            if not server:
                continue
                
            server_data = {'id': server.id, 'name': server.name, 'ip': server.ip_address}
            
            for cat in categories:
                snapshot = ServerConfigSnapshot.query.filter_by(
                    server_id=server_id, 
                    config_type=cat
                ).first()
                
                if snapshot:
                    server_data[cat] = snapshot.data
                    server_data[f'{cat}_updated'] = snapshot.updated_at.isoformat()
                else:
                    server_data[cat] = None
            
            results[server_id] = server_data

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

    except Exception as e:
        return jsonify({'success': False, 'message': f"Fetch Error: {str(e)}"})
