# [2023/10/16 빌드] ~ [2026/02/09 최종 통합 버전]
import subprocess
import sys
import os
import shutil
import json
import requests
import pyperclip
import pandas as pd
import pytesseract
import ctypes
from datetime import datetime
from time import sleep

# [자동 설치 로직] 필요한 라이브러리가 없으면 자동으로 pip install을 실행합니다.
required_libraries = [
    'colorama', 'selenium', 'pytesseract', 'pyperclip', 
    'requests', 'jira', 'pandas', 'openpyxl'
]

for lib in required_libraries:
    try:
        if lib == 'jira':
            import jira
        elif lib == 'openpyxl':
            import openpyxl
        else:
            __import__(lib)
    except ImportError:
        print(f"[RPA System] '{lib}' 모듈이 없어 설치를 진행합니다...")
        subprocess.check_call([sys.executable, "-m", "pip", "install", lib])
        print(f"[RPA System] '{lib}' 설치가 완료되었습니다.")

from myinfo import *
from colorama import init, Back, Fore, Style
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.alert import Alert
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait, Select
from selenium.webdriver.chrome.options import Options
from selenium.common.exceptions import NoAlertPresentException, TimeoutException
from selenium.webdriver.common.keys import Keys
from jira import JIRA

# colorama 초기화
init(autoreset=True)

# --- 전역 변수 및 초기화 ---
now = datetime.now()
today = datetime.today().strftime('%Y-%m-%d')
date_mdhm = now.strftime('%m%d%H%M')
temp_folder = 'c:/rpa_temp/'

if os.path.isdir(temp_folder):
    for file in os.listdir(temp_folder):
        try:
            shutil.rmtree(temp_folder)
        except:
            pass
if not os.path.exists(temp_folder):
    os.makedirs(temp_folder)

# --- 공통 유틸리티 함수 ---

def current_time():
    now = datetime.now()
    return now.strftime("[%Y-%m-%d %H:%M:%S]")

def handle_alert(driver):
    while True:
        try:
            alert = driver.switch_to.alert
            alert.accept()
            break
        except:
            sleep(1)

def handle_loading(driver):
    while True:
        try:
            alert = driver.switch_to.alert
            break
        except NoAlertPresentException:
            try:
                WebDriverWait(driver, 1).until_not(EC.presence_of_element_located((By.ID, 'loading')))
                sleep(1)
                break
            except TimeoutException:
                continue

def get_text_after_pattern(pattern):
    patterns = {"PRD": "", "QA": "qt-", "QA2": "qt2-"}
    return patterns.get(pattern)

def get_common_chrome_options():
    options = Options()
    options.add_argument("--start-maximized")
    options.add_argument("--disable-features=InsecureDownloadWarnings")
    options.add_experimental_option('detach', True)
    options.add_experimental_option('excludeSwitches', ['enable-logging'])
    options.add_experimental_option('prefs', {
        'download.default_directory': r'C:\rpa_temp',
        'safebrowsing.enabled': 'False'
    })
    return options

# --- 로그인 엔진 (리팩토링 핵심) ---

def _core_prd_login_process(driver, final_destination):
    """PRD 서버 전용 복잡한 OTP 로그인 통합 엔진"""
    ep_url = 'http://newep.lge.com/portal/main/portalMain.do'
    driver.get(ep_url)
    
    WebDriverWait(driver, 30).until(EC.presence_of_element_located((By.ID, 'USER')))
    sleep(1)
    driver.find_element(By.ID, 'USER').send_keys(EPID)
    driver.find_element(By.ID, 'LDAPPASSWORD').send_keys(EPPW)
    driver.implicitly_wait(2)
    driver.find_element(By.ID, 'OTP').click()

    WebDriverWait(driver, 10).until(EC.number_of_windows_to_be(2))
    driver.switch_to.window(driver.window_handles[1])
    driver.find_element(By.ID, 'pw').send_keys(EPPW)
    driver.find_element(By.ID, 'myButton').click()

    sleep(1)
    WebDriverWait(driver, 10).until(EC.number_of_windows_to_be(2))
    driver.switch_to.window(driver.window_handles[1])
    driver.find_element(By.XPATH, '//*[@id="TA_01"]/div[4]/div[1]').click()
    
    WebDriverWait(driver, 10).until(EC.number_of_windows_to_be(3))
    driver.switch_to.window(driver.window_handles[2])

    while True:
        try:
            driver.refresh()
            element1 = driver.find_element(By.ID, 'photo_imageK')
            element1.screenshot("otpimg.png")
            pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract'
            otpimg = pytesseract.image_to_string(r'otpimg.png').replace(" ", "").strip()
            
            driver.find_element(By.ID, 'bizidK').send_keys(XID)
            driver.find_element(By.ID, 'pcodeK').send_keys(BDAY)
            driver.find_element(By.ID, 'answerK').send_keys(otpimg)
            driver.find_element(By.XPATH, '//*[@id="form1"]/div[1]/table/tbody/tr[8]/td/input[1]').click()
            sleep(2)
            try:
                alert = driver.switch_to.alert
                print("[RPA] OTP 인식 오류, 재시도 합니다.")
                alert.accept()
            except:
                print("[RPA] OTP 정상 입력.")
                break
        except:
            pass

    WebDriverWait(driver, 10).until(EC.number_of_windows_to_be(4))
    driver.switch_to.window(driver.window_handles[3])
    OTPD = driver.find_element(By.XPATH, '//*[@id="loadingK"]/b').text
    
    for i in range(3, 0, -1):
        driver.switch_to.window(driver.window_handles[i])
        driver.close()
    
    driver.switch_to.window(driver.window_handles[0])
    driver.find_element(By.ID, 'OTPPASSWORD').send_keys(OTPD)
    driver.find_element(By.ID, 'loginSsobtn').click()
    sleep(1)
    
    while True:
        try:
            driver.get(ep_url)
            sleep(0.5)
            driver.find_element(By.ID, 'USER').is_displayed()
            input(f'{Fore.RED}[ERROR] 로그인 오류, 수동 로그인 후 엔터키 입력..{Style.RESET_ALL}')
        except:
            driver.get(final_destination)
            break
    return driver

# --- 외부 호출 함수 (함수명 유지 및 옵션 추가 기능 반영) ---

def sdp_login(target_server, target_url=None, add_options=None):
    """
    sdp_login 함수 수정: add_options 인자를 통해 추가 크롬 옵션 설정 가능
    사용 예: sdp_login('QA', add_options=["--force-device-scale-factor=0.8"])
    """
    server_url = get_text_after_pattern(target_server)
    url = f'http://{server_url}kic.smartdesk.lge.com/admin/main.lge'
    final_destination = target_url if target_url else url
    options = get_common_chrome_options()

    # 추가 옵션이 리스트 형태로 들어올 경우 반복문으로 추가
    if add_options:
        for opt in add_options:
            options.add_argument(opt)

    if target_server == 'PRD':
        print(f'[RPA] 운영 서버에 {EPID} 계정으로 로그인 합니다.')
        options.page_load_strategy = 'none'
        driver = webdriver.Chrome(options=options)
        return _core_prd_login_process(driver, final_destination)
    else:
        print(f'[RPA] {target_server} 서버에 {QAID} 계정으로 로그인 합니다.')
        driver = webdriver.Chrome(options=options)
        driver.get(url)
        handle_alert(driver)
        driver.find_element(By.ID, 'USER').send_keys(QAID)
        driver.find_element(By.ID, 'LDAPPASSWORD').send_keys(QAPW)
        driver.find_element(By.ID, 'loginSsobtn').click()
        try:
            WebDriverWait(driver, 3).until(EC.alert_is_present())
            driver.switch_to.alert.dismiss()
        except:
            pass
        driver.get(final_destination)
        return driver

def ep_prd_login(target_url):
    options = get_common_chrome_options()
    options.page_load_strategy = 'none'
    driver = webdriver.Chrome(options=options)
    return _core_prd_login_process(driver, target_url)

def create_webbrowser():
    options = get_common_chrome_options()
    return webdriver.Chrome(options=options)

# --- 데이터 조작 및 JIRA 함수 ---

def get_dataframe(doc, sheet_name):
    """구글 시트/엑셀 데이터 프레임 가져오기"""
    worksheet = doc.worksheet(sheet_name)
    values = worksheet.get_all_values()
    return pd.DataFrame(values[1:], columns=values[0]) if values else pd.DataFrame()

def check_alert(driver, expected_message):
    try:
        WebDriverWait(driver, 60).until(EC.alert_is_present())
        alert = driver.switch_to.alert
        msg = alert.text
        print(f"[ALERT] 얼럿 발견: {msg}")
        alert.accept()
        return msg
    except TimeoutException:
        print(f"[ALERT] 60초 동안 '{expected_message}' 얼럿이 나타나지 않았습니다.")
        return ""
    except Exception as e:
        print(f"[ALERT] 오류: {e}")
        return ""

def find_e(driver, locator, action, value=None, index=0, timeout=10, max_tries=3):
    for i in range(max_tries):
        try:
            elements = WebDriverWait(driver, timeout).until(EC.presence_of_all_elements_located(locator))
            el = elements[index] if index is not None else elements[0]
            if action == 'click': el.click()
            elif action == 'send_keys': el.send_keys(value)
            elif action == 'clear': el.clear()
            return
        except:
            print(current_time(), f"Retry {locator} {i+1}/{max_tries}")
    print(current_time(), f"Failed: {locator}")

# --- 시스템 제어 (화면보호기 등) ---

def rpa_progress(status):
    file_name = os.path.basename(sys.argv[0])
    print('\n[RPA] ' + current_time() + ' ' + file_name + ' ' + status)
    return file_name

ES_CONTINUOUS = 0x80000000
ES_DISPLAY_REQUIRED = 0x00000002
SetThreadExecutionState = ctypes.windll.kernel32.SetThreadExecutionState

def prevent_screensaver():
    return SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED)

def allow_screensaver():
    return SetThreadExecutionState(ES_CONTINUOUS)

def disable_power_settings():
    os.system("powercfg -change -monitor-timeout-ac 0")
    os.system("powercfg -change -standby-timeout-ac 0")
    os.system("powercfg -change -hibernate-timeout-ac 0")

def enable_power_settings(monitor_timeout, standby_timeout, hibernate_timeout):
    os.system(f"powercfg -change -monitor-timeout-ac {monitor_timeout}")
    os.system(f"powercfg -change -standby-timeout-ac {standby_timeout}")
    os.system(f"powercfg -change -hibernate-timeout-ac {hibernate_timeout}")

# --- 메시징 및 JIRA 자동화 ---

def print_webhook(webhook_url, webhook_data, n_print=None):
    styled_data = (webhook_data
                    .replace('<table border="1" class="dataframe">', '<table border="1" style="border-collapse: collapse;">')
                    .replace('<tr style="text-align: right;">', '<tr style="text-align: center;">')
                    .replace('<th>', '<th style="padding: 4px;color:white;background-color:#000000;text-align:center;font-size:13px;max-width:300px;">')
                    .replace('<td>', '<td style="padding: 4px;font-size:12px;max-width:300px;word-break:break-all;">')
                    .replace('&lt;', '<').replace('&gt;', '>'))
    data = {
        "type": "message",
        "attachments": [{"contentType": "application/vnd.microsoft.card.adaptive",
                         "content": {"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
                                     "type": "AdaptiveCard", "version": "1.0",
                                     "body": [{"type": "TextBlock", "text": '🕹 ' + styled_data, "wrap": True}]}}]
    }
    res = requests.post(webhook_url, headers={"Content-Type": "application/json"}, data=json.dumps(data))
    if not n_print: print(styled_data)
    return res

def select_jira_issue(jira, jql_query):
    jira_issues = [issue.key for issue in jira.search_issues(jql_query, maxResults=100)]
    if not jira_issues:
        print(f"{Back.RED}{Fore.WHITE}🚨 검색된 JIRA 이슈가 없습니다.{Style.RESET_ALL}")
        return None
    print(f'\n{Back.BLUE}{Fore.WHITE}▪ JIRA 요청 리스트 {Style.RESET_ALL}')
    for idx, key in enumerate(jira_issues, 1):
        print(f'{Fore.BLUE} {idx}. {key} {jira.issue(key).fields.summary} {Style.RESET_ALL}')
    s = "1" if len(jira_issues) == 1 else input(f' >> 순번 입력 : ')
    selected_key = jira_issues[int(s)-1]
    print(f'\n{Fore.YELLOW}{selected_key} 설정을 시작합니다.\n{Style.RESET_ALL}')
    return selected_key

load_df = None
load_filename = None
load_sheetname = None

def download_jira_attachment(jira, issue_key, temp_folder, extensions=None):
    global load_df, load_filename, load_sheetname
    jira_issue = jira.issue(issue_key)
    attachments = [(att.filename, att.content) for att in jira_issue.fields.attachment]
    if extensions:
        attachments = [(fn, url) for fn, url in attachments if any(fn.endswith(ext) for ext in extensions)]
    if not attachments: return None, None, None
    load_filename, url = attachments[0] if len(attachments) == 1 else attachments[int(input("파일번호:"))-1]
    path = os.path.join(temp_folder, load_filename)
    r = jira._session.get(url, stream=True)
    with open(path, 'wb') as f:
        for chunk in r.iter_content(chunk_size=1024): f.write(chunk)
    if load_filename.endswith('.xlsx'):
        xls = pd.ExcelFile(path)
        load_sheetname = xls.sheet_names[0]
        load_df = pd.read_excel(xls, sheet_name=load_sheetname)
        return load_df, load_filename, load_sheetname
    return None, None, None

# SDP App display 다운로드
def sdp_appdisplay_download(env_type='PRD'):
    env_upper = env_type.upper()
    
    # QA2 환경 차단 및 스크립트 종료
    if env_upper == 'QA2':
        print("❌ QA2는 업데이트를 지원 하지 않습니다.")
        sys.exit()
        
    # 설정 및 경로 (rpa_config 내 전역변수 활용)
    PAGE_ID = "3449827286"
    CONFLUENCE_URL = 'http://collab.lge.com/main'
    save_folder = f'{pyfolder}My Settings/'
    
    if not os.path.exists(save_folder):
        os.makedirs(save_folder)
        
    if env_upper == 'QA':
        file_name = 'AppDisplayInfo_qa.xlsx'
    else:
        file_name = 'AppDisplayInfo.xlsx'
        
    save_path = os.path.join(save_folder, file_name)
    
    # 로컬 파일 날짜 체크 (오늘 날짜면 스킵)
    # 상단의 from datetime import datetime 을 활용
    today_date = datetime.today().date()
    if os.path.exists(save_path):
        mod_time = os.path.getmtime(save_path)
        local_date = datetime.fromtimestamp(mod_time).date()
        if local_date == today_date:
            print(f"⏭️ [최신] 로컬 파일이 이미 오늘 날짜입니다. 다운로드를 생략합니다. ({save_path})")
            return True
    
    # 1. 파일 정보 및 다운로드 링크 조회
    search_url = f"{CONFLUENCE_URL}/rest/api/content/{PAGE_ID}/child/attachment"
    res_info = requests.get(search_url, auth=(JID, JPW), params={"filename": file_name}, timeout=30)
    
    if res_info.status_code == 200:
        results = res_info.json().get("results", [])
        
        if not results:
            # 직접 검색 실패 시 전체 목록에서 조회 (for문 대신 filter 사용)
            all_res = requests.get(search_url, auth=(JID, JPW), params={"limit": 100}, timeout=30).json().get("results", [])
            results = list(filter(lambda r: r['title'].lower() == file_name.lower(), all_res))
            
        if results:
            download_url = CONFLUENCE_URL + results[0]['_links']['download']
            
            # 2. 파일 다운로드 및 저장
            res_dl = requests.get(download_url, auth=(JID, JPW))
            if res_dl.status_code == 200:
                with open(save_path, 'wb') as f:
                    f.write(res_dl.content)
                print(f"✅ [Collab] {file_name} 다운로드 완료 ({save_path})")
                return True
            else:
                print(f"❌ 다운로드 실패 (상태 코드: {res_dl.status_code})")
                return False
                
    print(f"❌ {file_name}을(를) 콜렙 서버에서 찾을 수 없거나 통신에 실패했습니다.")
    return False