# client.py
from flask import Flask, request, Response, jsonify
import subprocess, requests, re
import win32gui, win32con, win32process
import time

app = Flask(__name__)

# 서버 HTML을 가져올 서버 URL을 정의합니다.
SERVER_URL = 'http://g.geomedical.kr/'

# 카카오워크 메시지 전송 함수
def send_kakao_message(name):
    """
    카카오워크로 메시지를 전송합니다. 여러 수신자에게 동일한 메시지를 보냅니다.
    """
    # 카카오워크 애플리케이션의 인증 토큰
    app_token = "9e248ca3.7a1a6159365f4a6385de8266ac655150"
    
    # API 엔드포인트 URL
    api_url = "https://api.kakaowork.com/v1/messages.send_by_email"
    
    # 수신자 이메일 목록
    recipients = [
        "topsj@geomedical.co.kr",
        "hg.bae@geomedical.co.kr"
    ]
    
    # 메시지 내용 설정
    message_text = f"{name}님이 계정 생성을 요청했습니다."
    
    # 요청 헤더 설정
    headers = {
        "Authorization": f"Bearer {app_token}",
        "Content-Type": "application/json"
    }
    
    # 모든 수신자에게 메시지 전송
    success_count = 0
    error_messages = []
    
    for recipient in recipients:
        # JSON 데이터 구성
        data = {
            "email": recipient,
            "text": "요청 확인",
            "blocks": [
                {
                    "type": "section",
                    "content": {
                        "type": "text",
                        "text": message_text
                    }
                }
            ]
        }
        
        # API 요청 보내기
        try:
            response = requests.post(api_url, json=data, headers=headers)
            # 응답 확인
            if response.status_code == 200:
                success_count += 1
            else:
                error_messages.append(f"{recipient}: 상태 코드 {response.status_code}")
        except Exception as e:
            error_messages.append(f"{recipient}: {str(e)}")
    
    # 결과 반환
    if success_count == len(recipients):
        return True, "모든 수신자에게 메시지가 성공적으로 전송되었습니다."
    elif success_count > 0:
        return True, f"{success_count}명의 수신자에게 메시지가 전송되었습니다. 오류: {', '.join(error_messages)}"
    else:
        return False, f"메시지 전송 실패. 오류: {', '.join(error_messages)}"

# --- 루트: 서버 HTML을 실시간으로 가져와 반환 (동기화) ---
@app.route('/', methods=['GET'])
def agent_index():
    try:
        r = requests.get(SERVER_URL, timeout=5)
        return Response(r.text, status=r.status_code, mimetype='text/html')
    except Exception as e:
        return f"서버({SERVER_URL})에 접속 실패: {e}", 500

# --- 계정 생성 요청 처리 ---
@app.route('/create_account_request', methods=['POST'])
def create_account_request():
    try:
        data = request.get_json()
        if not data:
            return jsonify({"success": False, "message": "유효하지 않은 요청 데이터"}), 400
            
        fullName = data.get('fullName')
        
        if not fullName:
            return jsonify({"success": False, "message": "이름이 누락되었습니다."}), 400
        
        # 카카오워크로 메시지 전송
        success, message = send_kakao_message(fullName)
        
        if success:
            return jsonify({"success": True, "message": message})
        else:
            return jsonify({"success": False, "message": message}), 500
    except Exception as e:
        return jsonify({"success": False, "message": f"서버 오류: {str(e)}"}), 500

# --- 네트워크 드라이브 연결 (로컬 실행) ---
@app.route('/connect', methods=['POST'])
def connect_drive():
    data = request.get_json()
    username = data.get('username')
    password = data.get('password')
    drive    = data.get('drive')
    external_access = data.get('externalAccess', False)  # 외부 접속 상태 받기
    
    if not username or not password or not drive:
        return "누락된 정보가 있습니다.", 400

    # 외부 접속 여부에 따라 네트워크 경로 결정
    if external_access:
        # WebDAV 방식 (외부 접속)
        if drive.upper() == 'G':
            network_path = r'https://geomedical.kr:5006/GDRIVE'
        elif drive.upper() == 'Q':
            network_path = r'https://geomedical.kr:5006/home'
        elif drive.upper() == 'R':
            # R 드라이브는 G 드라이브와 동일한 경로로 설정
            network_path = r'https://geomedical.kr:5006/GDRIVE'
        else:
            network_path = r'https://geomedical.kr:5006/GDRIVE'
    else:
        # 기존 방식 (내부 접속)
        if drive.upper() == 'G':
            network_path = r'\\GDRIVE2\GDRIVE'
        elif drive.upper() == 'Q':
            network_path = r'\\GDRIVE2\home'
        elif drive.upper() == 'R':
            network_path = r'\\GDRIVE\gdrive'
        else:
            network_path = r'\\GDRIVE2\GDRIVE'
    
    # 1. 먼저 기존 연결 상태 확인
    check_cmd = "net use"
    check_result = subprocess.run(check_cmd, shell=True, capture_output=True, text=True)
    
    # 2. 선택한 드라이브(G:, Q: 또는 R:)에 대해 이미 연결이 있으면 먼저 해제
    if f"{drive}:" in check_result.stdout:
        disconnect_cmd = f'net use {drive}: /delete /yes'
        subprocess.run(disconnect_cmd, shell=True, capture_output=True, text=True)
    
    # 3. 연결 방식에 따라 다른 명령 사용
    if external_access:
        # WebDAV 방식을 위한 추가 옵션 (SSL 인증서 검증 무시, 제한 시간 연장)
        cmd = f'net use {drive}: {network_path} /user:{username} {password} /persistent:no'
    else:
        # 내부 네트워크 연결 방식
        cmd = f'net use {drive}: {network_path} /user:{username} {password} /persistent:no'
    
    try:
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
        
        # 연결 성공한 경우 - 바로 반환
        if result.returncode == 0:
            # 수정된 부분: 탐색기로 열기 대신 함수를 사용하여 최대화된 탐색기 표시
            open_explorer_maximized(f"{drive}:")
            connection_type = "WebDAV(외부)" if external_access else "내부 네트워크"
            return f"[{drive}:] 드라이브 연결 성공! ({connection_type})\n{result.stdout}"
        else:
            # 연결 실패 - 오류 분석
            error_msg = result.stderr.strip() if result.stderr else "알 수 없는 오류"
            error_code = re.search(r'시스템 오류 (\d+)', error_msg)
            
            if error_code:
                error_num = error_code.group(1)
                
                # 오류 1219, 85, 67(네트워크 이름을 찾을 수 없음), 53(네트워크 경로를 찾을 수 없음) 일 경우 처리
                if error_num in ["1219", "85", "67", "53"]:
                    # 로그 메시지 
                    log_msg = f"오류 {error_num} 발생: 모든 네트워크 연결을 해제하고 재시도합니다..."
                    
                    # 서버 경로에 대한 모든 연결 확인 (192.168.2.x로 시작하는)
                    server_connections = re.findall(r'\\\\192\.168\.2\.\d+\\[^\s]+', check_result.stdout)
                    
                    # 각 서버 연결에 대한 처리
                    for connection in server_connections:
                        # 연결된 드라이브 문자 확인 (있는 경우)
                        drive_matches = re.findall(fr'([A-Z]:)\s+{re.escape(connection)}', check_result.stdout)
                        for drive_letter in drive_matches:
                            disconnect_cmd = f'net use {drive_letter} /delete /yes'
                            subprocess.run(disconnect_cmd, shell=True, capture_output=True, text=True)
                        
                        # 네트워크 경로 자체에 대한 연결도 해제
                        disconnect_cmd = f'net use {connection} /delete /yes'
                        subprocess.run(disconnect_cmd, shell=True, capture_output=True, text=True)
                    
                    # 추가로 모든 연결 해제 (더 강력한 방법)
                    all_connections_cmd = "net use * /delete /yes"
                    subprocess.run(all_connections_cmd, shell=True, capture_output=True, text=True)
                    
                    # 다시 연결 시도
                    retry_result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
                    if retry_result.returncode == 0:
                        # 수정된 부분: 탐색기로 열기 대신 함수를 사용하여 최대화된 탐색기 표시
                        open_explorer_maximized(f"{drive}:")
                        connection_type = "WebDAV(외부)" if external_access else "내부 네트워크"
                        return f"[{drive}:] 드라이브 연결 성공 (재시도 후)! ({connection_type})\n{retry_result.stdout}"
                    else:
                        retry_error = retry_result.stderr.strip() if retry_result.stderr else "알 수 없는 오류"
                        # WebDAV 연결 실패 시 추가 안내 메시지
                        error_message = f"[{drive}:] 드라이브 연결 실패 (재시도 후)!\n에러: {retry_error}"
                        if external_access:
                            error_message += "\n\n외부 접속(WebDAV) 연결 실패 가능성:\n1. 인터넷 연결을 확인하세요.\n2. VPN 연결이 필요할 수 있습니다.\n3. 외부 접속이 허용된 계정인지 확인하세요."
                        return error_message, 400
                else:
                    # 다른 오류 코드의 경우 상세 정보 반환
                    error_message = f"[{drive}:] 드라이브 연결 실패! 오류 코드: {error_num}\n{error_msg}"
                    if external_access:
                        error_message += "\n\n외부 접속(WebDAV) 연결 실패 가능성:\n1. 인터넷 연결을 확인하세요.\n2. VPN 연결이 필요할 수 있습니다.\n3. 외부 접속이 허용된 계정인지 확인하세요."
                    return error_message, 400
            
            # 오류 코드가 명확하지 않은 경우
            error_message = f"[{drive}:] 드라이브 연결 실패!\n{error_msg}"
            if external_access:
                error_message += "\n\n외부 접속(WebDAV) 연결 실패 가능성:\n1. 인터넷 연결을 확인하세요.\n2. VPN 연결이 필요할 수 있습니다.\n3. 외부 접속이 허용된 계정인지 확인하세요."
            return error_message, 400
            
    except Exception as e:
        return f"예외 발생: {e}", 500

# --- 네트워크 드라이브 해제 (로컬 실행) ---
@app.route('/logout', methods=['POST'])
def logout_drive():
    data = request.get_json()
    drive = data.get('drive')
    if not drive:
        return "드라이브 문자가 누락되었습니다.", 400
    
    # 로그아웃 명령에 /yes 옵션 추가
    cmd = f'net use {drive}: /delete /yes'
    try:
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
        if result.returncode == 0:
            return f"[{drive}:] 로그아웃 성공!\n{result.stdout}"
        else:
            return f"[{drive}:] 로그아웃 실패!\n에러: {result.stderr}", 400
    except Exception as e:
        return f"예외 발생: {e}", 500

# --- 드라이브 경로를 웹에서 여는 새 엔드포인트 ---
@app.route('/open_drive/<drive_letter>', methods=['GET'])
def open_drive(drive_letter):

    drive_letter = drive_letter.upper()
    if drive_letter not in ['G', 'Q', 'R']:
        return jsonify({"success": False, "message": "유효하지 않은 드라이브 문자"}), 400
    
    try:
        # 탐색기 열기 및 최대화
        open_explorer_maximized(f"{drive_letter}:")
        
        # 성공 응답
        return jsonify({
            "success": True, 
            "message": f"{drive_letter}: 드라이브를 탐색기에서 열었습니다."
        })
    except Exception as e:
        return jsonify({
            "success": False, 
            "message": f"드라이브 열기 실패: {str(e)}"
        }), 500

# --- 현재 연결 상태 확인 (G, Q, R 드라이브 체크) ---
def get_connected_drives():
    try:
        result = subprocess.run("net use", shell=True, capture_output=True, text=True)
        if result.returncode != 0:
            return None, result.stderr
        output = result.stdout
        drives_status = {}
        for drive in ['G', 'Q', 'R']:
            pattern = rf'^OK\s+{drive}:'
            if re.search(pattern, output, re.MULTILINE):
                drives_status[drive] = True
            else:
                drives_status[drive] = False
        return drives_status, None
    except Exception as e:
        return None, str(e)

@app.route('/status', methods=['GET'])
def status():
    drives_status, error = get_connected_drives()
    if error:
        return jsonify({"error": error}), 500
    return jsonify(drives_status)

# 최대화된 탐색기 열기 함수 추가
def open_explorer_maximized(drive_path):
    """
    지정된 드라이브 경로로 Explorer 창을 열고 최대화합니다.
    
    Args:
        drive_path: 열 드라이브 경로 (예: "G:")
    """
    try:
        # Explorer 실행
        subprocess.Popen(f'explorer {drive_path}')
        
        # Win32 API를 사용하여 창을 찾고 최대화
        time.sleep(0.5)  # Explorer가 시작할 시간을 줍니다
        
        def enum_windows_callback(hwnd, result_list):
            if win32gui.IsWindowVisible(hwnd):
                # 창 제목 가져오기
                window_text = win32gui.GetWindowText(hwnd)
                # Explorer 창인지 확인 (드라이브 경로 또는 클래스 이름으로)
                if drive_path in window_text or "내 PC" in window_text:
                    result_list.append(hwnd)
                
                # 또는 창 클래스가 Explorer인지 확인
                class_name = win32gui.GetClassName(hwnd)
                if class_name == "CabinetWClass":  # Explorer 창의 클래스 이름
                    result_list.append(hwnd)
        
        found_windows = []
        win32gui.EnumWindows(enum_windows_callback, found_windows)
        
        for hwnd in found_windows:
            try:
                # 최소화되어 있으면 복원
                if win32gui.IsIconic(hwnd):
                    win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)
                
                # 창 최대화
                win32gui.ShowWindow(hwnd, win32con.SW_MAXIMIZE)
                
                # 창을 앞으로 가져오기
                win32gui.SetForegroundWindow(hwnd)
                
                # 창 깜빡이기
                win32gui.FlashWindow(hwnd, True)
                
                # 첫 번째 창을 찾아 처리했으면 종료
                return
            except Exception as e:
                print(f"Explorer 창 처리 중 오류: {e}")
        
    except Exception as e:
        print(f"Explorer 실행 실패: {e}")

def run_app():
    app.run(debug=False)

# 클라이언트에서 원격 모듈로 로드할 때는 __name__=='__main__' 블록을 제거하거나 run_app() 호출 대신 정의만 남겨두세요.
if __name__ == '__main__':
    run_app()