import csv
import os
import logging
import re
from datetime import datetime
from tkinter import filedialog, Tk

# --- 설정 ---
TARGET_PO_FILE = 'LocalExport.po'
LOG_FILE = 'update_po.log'
MSGCTXT_COLUMN = 'msgctxt'
EXCLUDE_COLUMNS = ['SourceLocation', 'msgid']

# --- 로깅 설정 ---
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(logging.Formatter('%(asctime)s [%(levelname)s] - %(message)s'))
logging.basicConfig(
    level=logging.INFO,
    handlers=[stream_handler]
)

def get_paths_from_user():
    """
    Tkinter를 사용하여 사용자로부터 입력 파일 경로와 루트 폴더 경로를 받습니다.
    """
    root = Tk()
    root.withdraw()

    logging.info("번역 데이터가 포함된 원본 파일을 선택해주세요 (.csv 또는 .txt)")
    input_file_path = filedialog.askopenfilename(
        title="번역 원본 파일 선택",
        filetypes=(("Text files", "*.txt"), ("CSV files", "*.csv"), ("All files", "*.*"))
    )
    if not input_file_path:
        logging.warning("파일을 선택하지 않았습니다. 스크립트를 종료합니다.")
        return None, None

    logging.info(f"선택된 파일: {input_file_path}")
    logging.info("업데이트할 PO 파일이 들어있는 언어 폴더들의 상위 폴더(루트 폴더)를 선택해주세요.")
    
    root_folder_path = filedialog.askdirectory(
        title="언어 폴더들의 루트 폴더 선택 (예: LocalExport 폴더)"
    )
    if not root_folder_path:
        logging.warning("폴더를 선택하지 않았습니다. 스크립트를 종료합니다.")
        return None, None
        
    logging.info(f"선택된 루트 폴더: {root_folder_path}")
    
    return input_file_path, root_folder_path


def parse_po_file(file_path):
    """
    PO 파일을 파싱하여 msgctxt를 키로 하는 딕셔너리를 반환합니다.
    """
    normalized_path = os.path.normpath(file_path)
    if not os.path.exists(normalized_path):
        logging.warning(f"PO 파일을 찾을 수 없습니다: {normalized_path}")
        return None
        
    translations = {}
    try:
        with open(normalized_path, 'r', encoding='utf-8') as f:
            content = f.read()
            
        pattern = re.compile(
            r'(?:#.KEY:\s*"(.*)"\s*\n)?'
            r'msgctxt\s+"((?:[^"\\]|\\.)*)"\s*\n'
            r'msgid\s+"((?:[^"\\]|\\.)*)"\s*\n'
            r'msgstr\s+"((?:[^"\\]|\\.)*)"',
            re.DOTALL
        )

        for match in pattern.finditer(content):
            msgctxt_key = match.group(1) or match.group(2)
            msgctxt_value = match.group(2).replace('\\"', '"')
            msgid_value = match.group(3).replace('\\"', '"')
            msgstr_value = match.group(4).replace('\\"', '"')
            
            translations[msgctxt_value] = {
                "msgid": msgid_value,
                "msgstr": msgstr_value,
                "original_block": match.group(0)
            }
            
    except Exception as e:
        logging.error(f"PO 파일 파싱 중 오류 발생: {normalized_path} - {e}")
        return None
        
    return translations

def update_po_file(file_path, translations_to_update):
    """
    PO 파일의 내용을 업데이트합니다.
    """
    po_data = parse_po_file(file_path)
    if po_data is None:
        return 0

    update_count = 0
    normalized_path = os.path.normpath(file_path)
    
    try:
        with open(normalized_path, 'r', encoding='utf-8') as f:
            content = f.read()

        for msgctxt, new_msgstr in translations_to_update.items():
            if msgctxt in po_data:
                entry = po_data[msgctxt]
                current_msgstr = entry['msgstr']

                # 먼저 현재 값과 새 값이 다른지 확인
                if current_msgstr == new_msgstr:
                    # 변경사항 없음 - 건너뜀 (로그 출력 안함)
                    continue

                original_block = entry['original_block']

                # 백슬래시와 쌍따옴표를 올바르게 이스케이프
                escaped_new_msgstr = new_msgstr.replace('\\', '\\\\').replace('"', '\\"')

                # msgstr 라인 전체를 교체하는 정규식
                updated_block = re.sub(
                    r'^(msgstr\s+)\".*\"$',
                    r'\1"' + escaped_new_msgstr + '"',
                    original_block,
                    flags=re.MULTILINE
                )

                if updated_block != original_block:
                    content = content.replace(original_block, updated_block)
                    logging.info(f"  > 업데이트: msgctxt='{msgctxt}'")
                    update_count += 1
                else:
                    logging.warning(f"  > 정규식 교체 실패: msgctxt='{msgctxt}'")
            else:
                logging.warning(f"  > msgctxt 찾기 실패: '{msgctxt}'를 PO 파일에서 찾을 수 없습니다.")

        if update_count > 0:
            with open(normalized_path, 'w', encoding='utf-8') as f:
                f.write(content)
                
    except Exception as e:
        logging.error(f"PO 파일 업데이트 중 오류 발생: {normalized_path} - {e}")
        return 0

    return update_count


def main():
    """
    메인 실행 함수
    """
    input_file, root_folder = get_paths_from_user()
    if not input_file or not root_folder:
        input("\n엔터 키를 눌러 프로그램을 종료하세요...")
        return
    
    log_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), LOG_FILE)
    file_handler = logging.FileHandler(log_file_path, 'w', encoding='utf-8')
    file_handler.setFormatter(logging.Formatter('%(asctime)s [%(levelname)s] - %(message)s'))
    logging.getLogger().addHandler(file_handler)

    logging.info("="*50)
    logging.info("PO 파일 업데이트 스크립트를 시작합니다.")
    logging.info(f"시작 시간: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    logging.info(f"선택된 입력 파일: '{os.path.normpath(input_file)}'")
    logging.info(f"선택된 루트 폴더: '{os.path.normpath(root_folder)}'")
    logging.info("="*50)

    translations_by_lang = {}
    try:
        with open(input_file, 'r', encoding='utf-8-sig') as f:
            # **수정된 부분**: Sniffer 대신, 탭(\t)을 구분자로 명시적으로 지정
            reader = csv.DictReader(f, delimiter='\t')
            
            # 안전장치: 필드가 1개 이하면 구분자 문제를 경고
            if len(reader.fieldnames) <= 1:
                logging.warning("파일에서 컬럼을 2개 미만으로 감지했습니다. 입력 파일이 탭(tab)으로 구분된 것이 맞는지 확인해주세요.")
            
            lang_codes = [h for h in reader.fieldnames if h not in EXCLUDE_COLUMNS and h != MSGCTXT_COLUMN]

            for lang in lang_codes:
                translations_by_lang[lang] = {}

            for row in reader:
                msgctxt = row.get(MSGCTXT_COLUMN)
                if not msgctxt:
                    continue
                
                for lang in lang_codes:
                    if row.get(lang):
                        translations_by_lang[lang][msgctxt] = row[lang]

    except Exception as e:
        logging.error(f"입력 파일 처리 중 오류 발생: {e}")
        input("\n엔터 키를 눌러 프로그램을 종료하세요...")
        return

    logging.info(f"성공적으로 '{os.path.basename(input_file)}' 파일을 읽었습니다.")
    logging.info(f"제외 컬럼({EXCLUDE_COLUMNS})을 제외한 업데이트 대상 언어: {list(translations_by_lang.keys())}")

    total_updated_files = 0
    total_updated_entries = 0
    
    for lang_code in list(translations_by_lang.keys()):
        lang_folder_path = os.path.join(root_folder, lang_code)
        
        if not os.path.isdir(lang_folder_path):
            logging.warning(f"언어 폴더를 찾을 수 없습니다: '{os.path.normpath(lang_folder_path)}'. 건너뜁니다.")
            continue
            
        po_file_path = os.path.join(lang_folder_path, TARGET_PO_FILE)
        
        if os.path.exists(po_file_path):
            logging.info(f"\n--- '{os.path.normpath(po_file_path)}' 파일 처리 시작 ---")
            
            if lang_code in translations_by_lang and translations_by_lang[lang_code]:
                update_count = update_po_file(po_file_path, translations_by_lang[lang_code])
                
                if update_count > 0:
                    logging.info(f"'{os.path.normpath(po_file_path)}' 파일에 총 {update_count}개의 항목을 업데이트했습니다.")
                    total_updated_files += 1
                    total_updated_entries += update_count
                else:
                    logging.info(f"'{os.path.normpath(po_file_path)}' 파일에 업데이트할 내용이 없거나, 업데이트에 실패했습니다.")
        else:
            logging.warning(f"대상 PO 파일을 찾을 수 없습니다: '{os.path.normpath(po_file_path)}'.")

    logging.info("\n" + "="*50)
    logging.info("모든 작업이 완료되었습니다.")
    logging.info(f"총 {total_updated_files}개의 PO 파일을 업데이트했습니다.")
    logging.info(f"총 {total_updated_entries}개의 번역 항목을 업데이트했습니다.")
    logging.info(f"종료 시간: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    logging.info(f"자세한 내용은 '{os.path.normpath(log_file_path)}' 파일을 확인해주세요.")
    logging.info("="*50)

if __name__ == '__main__':
    main()