# print_label.py

from PIL import Image, ImageDraw, ImageFont
import io
import os
import logging
from brother_ql.raster import BrotherQLRaster
from brother_ql.conversion import convert
from brother_ql.backends.helpers import send
import datetime  # 날짜 처리를 위해 추가
import pytz
import re
import qrcode
import json

# 프린터 기본 설정 (프린터 정보가 전달되지 않을 때 사용)
DEFAULT_PRINTER_IP = "192.168.0.121"  # QL-710W 프린터의 IP 주소
DEFAULT_PRINTER_MODEL = "QL-710W"
DEFAULT_LABEL_TYPE = "29"  # 29mm 연속 출력 용지

# 로깅 설정
logging.basicConfig(level=logging.DEBUG, format='%(levelname)s:%(message)s')

# KOR 시간대 설정
KOR_TZ = pytz.timezone('Asia/Seoul')


def format_total_amount(dosage, frequency, duration):
    """
    1회 복용량, 복용 횟수, 총 복용 일수를 기반으로 총량을 계산하고,
    1/4 단위로 반올림하거나 그대로 반환합니다.
    
    Parameters:
        dosage (float): 1회 복용량 (소수 넷째 자리까지 포함 가능)
        frequency (int): 1일 복용 횟수
        duration (int): 복용 일수
    
    Returns:
        str: 포맷팅된 총량 문자열
    """
    if frequency > 0 and duration > 0:
        # 1일 복용량 = 1회 복용량 * 1일 복용 횟수
        daily_dosage = dosage * frequency
        # 총량 = 1일 복용량 * 총 복용 일수
        total_amount = daily_dosage * duration

        # 1회 복용량이 소수 넷째 자리까지 있는 경우 1/4 단위로 반올림
        if round(dosage, 4) != round(dosage, 3):  # 소수 넷째 자리 여부 확인
            total_amount = round(total_amount * 4) / 4

        # 정수인 경우 소수점 없이 표시, 소수가 있는 경우 둘째 자리까지 표시
        return str(int(total_amount)) if total_amount.is_integer() else f"{total_amount:.2f}".rstrip('0').rstrip('.')
    return "0"  # 복용 횟수나 복용 일수가 0인 경우


def format_dosage(dosage):
    """
    1회 복용량을 포맷팅합니다.
    
    Parameters:
        dosage (float): 1회 복용량
    
    Returns:
        str: 포맷팅된 복용량 문자열
    """
    if dosage.is_integer():
        return str(int(dosage))
    else:
        # 최대 4자리 소수까지 표시, 불필요한 0 제거
        return f"{dosage:.4f}".rstrip('0').rstrip('.')


def format_converted_total(dosage, frequency, duration, conversion_factor):
    """
    총량을 계산하고 환산계수를 곱한 변환된 총량을 포맷팅합니다.

    Parameters:
        dosage (float): 1회 복용량 (소수 넷째 자리까지 포함 가능)
        frequency (int): 1일 복용 횟수
        duration (int): 복용 일수
        conversion_factor (float): 환산계수

    Returns:
        str: 변환된 총량을 포함한 포맷팅된 문자열
    """
    if frequency > 0 and duration > 0 and conversion_factor is not None:
        total_amount = dosage * frequency * duration
        if round(dosage, 4) != round(dosage, 3):
            total_amount = round(total_amount * 4) / 4
        converted_total = total_amount * conversion_factor
        if converted_total.is_integer():
            return str(int(converted_total))
        else:
            return f"{converted_total:.2f}".rstrip('0').rstrip('.')
    return None


def draw_scissor_border(draw, width, height, edge_size=5, steps=230):
    """
    라벨 이미지의 테두리에 톱니 모양의 절취선을 그립니다.

    Parameters:
        draw (ImageDraw.Draw): 이미지에 그리기 위한 Draw 객체
        width (int): 라벨 너비
        height (int): 라벨 높이
        edge_size (int): 톱니 크기
        steps (int): 톱니 반복 횟수
    """
    top_points = []
    step_x = width / (steps * 2)
    for i in range(steps * 2 + 1):
        x = i * step_x
        y = 0 if i % 2 == 0 else edge_size
        top_points.append((int(x), int(y)))
    draw.line(top_points, fill="black", width=2)

    bottom_points = []
    for i in range(steps * 2 + 1):
        x = i * step_x
        y = height if i % 2 == 0 else height - edge_size
        bottom_points.append((int(x), int(y)))
    draw.line(bottom_points, fill="black", width=2)

    left_points = []
    step_y = height / (steps * 2)
    for i in range(steps * 2 + 1):
        y = i * step_y
        x = 0 if i % 2 == 0 else edge_size
        left_points.append((int(x), int(y)))
    draw.line(left_points, fill="black", width=2)

    right_points = []
    for i in range(steps * 2 + 1):
        y = i * step_y
        x = width if i % 2 == 0 else width - edge_size
        right_points.append((int(x), int(y)))
    draw.line(right_points, fill="black", width=2)


def split_med_name(med_name):
    """
    약품 이름을 표시용 이름과 시그니처 정보로 분리합니다.

    Parameters:
        med_name (str): 약품 이름

    Returns:
        tuple: (표시용 약품 이름, 시그니처 정보, 분리 여부)
    """
    units = ['mg', 'g', 'ml', '%']
    pattern = r'(\d+(?:\.\d+)?(?:/\d+(?:\.\d+)?)*)\s*(' + '|'.join(units) + r')(?:/(' + '|'.join(units) + r'))?$'
    korean_only = re.fullmatch(r'[가-힣]+', med_name) is not None
    korean_and_num_eng = re.fullmatch(r'[가-힣a-zA-Z0-9/\.]+', med_name) is not None and not korean_only
    med_name_display = med_name
    signature_info = "청 춘 약 국"
    split_occurred = False
    if korean_only:
        if len(med_name) >= 10:
            match = re.search(pattern, med_name)
            if match and match.start() >= 10:
                med_name_display = med_name[:match.start()].strip()
                signature_info = match.group(1) + match.group(2) + (f"/{match.group(3)}" if match.group(3) else "")
                split_occurred = True
            else:
                med_name_display = med_name[:10]
        # else 그대로 사용
    elif korean_and_num_eng:
        if len(med_name) >= 13:
            match = re.search(pattern, med_name)
            if match:
                med_name_display = med_name[:match.start()].strip()
                signature_info = match.group(1) + match.group(2) + (f"/{match.group(3)}" if match.group(3) else "")
                split_occurred = True
            else:
                med_name_display = med_name[:12]
    return med_name_display, signature_info, split_occurred


def should_left_align(med_name_display):
    """
    약품 이름의 길이와 구성을 기반으로 좌측 정렬 여부를 결정합니다.

    Parameters:
        med_name_display (str): 분리된 약품 이름 표시 부분

    Returns:
        bool: 좌측 정렬 여부
    """
    korean_only = re.fullmatch(r'[가-힣]+', med_name_display) is not None
    korean_and_num_eng = re.fullmatch(r'[가-힣a-zA-Z0-9/\.]+', med_name_display) is not None and not korean_only

    if korean_only and len(med_name_display) >= 10:         # 10글자부터 좌측정렬 (한글단독)
        return True
    if korean_and_num_eng and len(med_name_display) >= 13:   # 13글자부터 좌측정렬 (한글+숫자+영문)
        return True
    return False


def normalize_medication_name(med_name):
    """
    약품 이름을 정제하여 라벨에 표시할 형태로 변환
    - 괄호 안 내용 제거: "디오탄정80밀리그램(발사르탄)" → "디오탄정80mg"
    - 밀리그램 계열: "밀리그램", "밀리그람", "미리그램" → "mg"
    - 마이크로그램 계열: "마이크로그램", "마이크로그람" → "μg"
    - 그램 계열: "그램", "그람" → "g"
    - 대괄호 제거: "[애엽이소프]" → ""
    """
    if not med_name:
        return med_name

    # 1. 대괄호 및 내용 제거 (예: "오티렌F정[애엽이소프]" → "오티렌F정")
    med_name = re.sub(r'\[.*?\]', '', med_name)  # 완전한 대괄호 쌍 제거
    med_name = re.sub(r'\[.*$', '', med_name)    # 여는 괄호부터 끝까지 제거

    # 2. 소괄호 및 내용 제거 (예: "디오탄정80밀리그램(발사르탄)" → "디오탄정80밀리그램")
    med_name = re.sub(r'\(.*?\)', '', med_name)  # 완전한 소괄호 쌍 제거
    med_name = re.sub(r'\(.*$', '', med_name)    # 여는 괄호부터 끝까지 제거

    # 2-1. 언더스코어 뒤 내용 제거 (예: "리피토정10mg_(10.85mg/1정)" → "리피토정10mg")
    med_name = re.sub(r'_.*$', '', med_name)     # 언더스코어부터 끝까지 제거

    # 3. 밀리그램 변환 (숫자와 함께 있는 경우 포함)
    # "80밀리그램" → "80mg", "밀리그램" → "mg"
    med_name = re.sub(r'밀리그램|밀리그람|미리그램|미리그람', 'mg', med_name)

    # 4. 마이크로그램 변환
    med_name = re.sub(r'마이크로그램|마이크로그람', 'μg', med_name)

    # 5. 그램 변환 (단, mg/μg로 이미 변환된 것은 제외)
    med_name = re.sub(r'(?<!m)(?<!μ)그램|그람', 'g', med_name)

    # 6. 공백 정리 (연속된 공백을 하나로)
    med_name = re.sub(r'\s+', ' ', med_name).strip()

    return med_name


def create_label_image(patient_name, med_name, add_info, frequency, dosage, duration,
                       formulation_type, main_ingredient_code, dosage_form, administration_route,
                       label_name, unit, conversion_factor=None, storage_condition="실온보관",
                       custom_dosage_instruction=""):
    """
    라벨 이미지를 생성합니다.

    Parameters:
        patient_name (str): 환자 이름
        med_name (str): 약품 이름
        add_info (str): 약품 효능 정보
        frequency (int): 복용 횟수
        dosage (float): 복용량
        duration (int): 복용 일수
        formulation_type (str): 제형 타입
        main_ingredient_code (str): 주성분 코드 
        dosage_form (str): 복용 형태
        administration_route (str): 투여 경로
        label_name (str): 라벨 명칭
        unit (str): 복용 단위
        conversion_factor (float, optional): 환산계수
        storage_condition (str, optional): 보관 조건
        custom_dosage_instruction (str, optional): 커스텀 용법 텍스트

    Returns:
        PIL.Image: 생성된 라벨 이미지
    """
    # 약품 이름 정제 (밀리그램 → mg 등)
    med_name = normalize_medication_name(med_name)

    # 라벨 이미지 설정
    label_width = 306  # 29mm 용지에 해당하는 너비 픽셀 수 (300 dpi 기준)
    label_height = 380  # 라벨 높이를 380으로 확장하여 추가 정보 포함  (Glabel 기준 380 적당)
    image = Image.new("1", (label_width, label_height), "white")
    draw = ImageDraw.Draw(image)

    # 폰트 설정 (여기서 폰트를 규정하고 요소에서 불러서 사용)
    font_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fonts", "malgunbd.ttf")
    try:
        patient_name_font = ImageFont.truetype(font_path, 44)
        drug_name_font = ImageFont.truetype(font_path, 32)
        info_font = ImageFont.truetype(font_path, 30)
        signature_font = ImageFont.truetype(font_path, 32)
        print_date_font = ImageFont.truetype(font_path, 20)  # 조제일 폰트 추가
        additional_info_font = ImageFont.truetype(font_path, 27)  # 추가 정보 폰트
        storage_condition_font = ImageFont.truetype(font_path, 27)  # 보관 조건 폰트
    except IOError:
        patient_name_font = ImageFont.load_default()
        drug_name_font = ImageFont.load_default()
        info_font = ImageFont.load_default()
        signature_font = ImageFont.load_default()
        print_date_font = ImageFont.load_default()  # 조제일 폰트 기본값 사용
        additional_info_font = ImageFont.load_default()  # 추가 정보 폰트 기본값 사용
        storage_condition_font = ImageFont.load_default()  # 보관 조건 폰트 기본값 사용
        logging.warning("폰트 로드 실패. 기본 폰트 사용.")

    # 중앙 정렬된 텍스트 출력 함수
    def draw_centered_text(draw, text, y, font, max_width=None):
        if not text:
            return y
        lines = []
        if max_width:
            words = re.findall(r'\S+', text)
            current_line = ""
            for word in words:
                test_line = f"{current_line} {word}".strip()
                bbox = draw.textbbox((0, 0), test_line, font=font)
                w = bbox[2] - bbox[0]
                if w <= max_width:
                    current_line = test_line
                else:
                    if current_line:
                        lines.append(current_line)
                    current_line = word
            if current_line:
                lines.append(current_line)
        else:
            lines = [text]
        for line in lines:
            bbox = draw.textbbox((0, 0), line, font=font)
            w, h = bbox[2] - bbox[0], bbox[3] - bbox[1]
            draw.text(((label_width - w) / 2, y), line, font=font, fill="black")
            y += h + 5
        return y

    def draw_left_aligned_text(draw, text, y, font, max_width=None):
        if not text:
            return y
        lines = []
        if max_width:
            words = re.findall(r'\S+', text)
            current_line = ""
            for word in words:
                test_line = f"{current_line} {word}".strip()
                bbox = draw.textbbox((0, 0), test_line, font=font)
                w = bbox[2] - bbox[0]
                if w <= max_width:
                    current_line = test_line
                else:
                    if current_line:
                        lines.append(current_line)
                    current_line = word
            if current_line:
                lines.append(current_line)
        else:
            lines = [text]
        for line in lines:
            bbox = draw.textbbox((0, 0), line, font=font)
            w, h = bbox[2] - bbox[0], bbox[3] - bbox[1]
            draw.text((10, y), line, font=font, fill="black")
            y += h + 5
        return y

    def draw_fitted_single_line(draw, text, y, font, max_width, min_font_size=24):
        """
        텍스트를 1줄로 강제 표시하되, 폰트 크기 자동 축소 → 잘라내기 순으로 처리

        Parameters:
            draw: ImageDraw.Draw 객체
            text (str): 표시할 텍스트
            y (int): Y 좌표
            font: 기본 폰트
            max_width (int): 최대 너비
            min_font_size (int): 최소 폰트 크기 (기본값: 24)

        Returns:
            int: 다음 줄의 Y 좌표
        """
        if not text:
            return y

        font_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fonts", "malgunbd.ttf")
        original_font_size = 30  # info_font 기본 크기

        # 1단계: 폰트 크기 자동 축소 (30px → 24px)
        for font_size in range(original_font_size, min_font_size - 1, -1):
            try:
                test_font = ImageFont.truetype(font_path, font_size)
            except IOError:
                test_font = ImageFont.load_default()

            bbox = draw.textbbox((0, 0), text, font=test_font)
            w, h = bbox[2] - bbox[0], bbox[3] - bbox[1]

            if w <= max_width:
                # 크기가 맞으면 중앙 정렬로 그리기
                draw.text(((label_width - w) / 2, y), text, font=test_font, fill="black")
                return y + h + 5

        # 2단계: 최소 폰트에도 안 맞으면 텍스트 잘라내기
        try:
            final_font = ImageFont.truetype(font_path, min_font_size)
        except IOError:
            final_font = ImageFont.load_default()

        original_text = text
        while len(text) > 3:
            ellipsized = text + "..."
            bbox = draw.textbbox((0, 0), ellipsized, font=final_font)
            w, h = bbox[2] - bbox[0], bbox[3] - bbox[1]

            if w <= max_width:
                draw.text(((label_width - w) / 2, y), ellipsized, font=final_font, fill="black")
                return y + h + 5

            text = text[:-1]

        # 최악의 경우: "..." 만 표시
        bbox = draw.textbbox((0, 0), "...", font=final_font)
        w, h = bbox[2] - bbox[0], bbox[3] - bbox[1]
        draw.text(((label_width - w) / 2, y), "...", font=final_font, fill="black")
        return y + h + 5

    # 환자 이름 처리: 한글은 띄워쓰기, 영문은 동적 크기 조정
    is_korean_name = all(ord('가') <= ord(char) <= ord('힣') or char.isspace() for char in patient_name if char.strip())

    if is_korean_name:
        # 한글 이름: 한 글자씩 띄우기 (기존 방식)
        y_position = 10
        formatted_patient_name = " ".join(patient_name)
        y_position = draw_centered_text(draw, formatted_patient_name, y_position, patient_name_font, max_width=label_width - 40)
    else:
        # 영문 이름: 1줄에 맞추기 위해 폰트 크기 동적 조정 (44px → 최소 28px)
        max_width_for_name = label_width - 40
        min_font_size_1line = 28  # 1줄일 때 최소 폰트
        min_font_size_2line = 20  # 2줄일 때 최소 폰트 (18px → 20px)
        original_font_size = 44

        # 폰트 크기를 줄여가며 1줄에 맞는 크기 찾기
        fitted_font = patient_name_font
        for font_size in range(original_font_size, min_font_size_1line - 1, -1):
            try:
                test_font = ImageFont.truetype(font_path, font_size)
            except IOError:
                test_font = ImageFont.load_default()

            bbox = draw.textbbox((0, 0), patient_name, font=test_font)
            w = bbox[2] - bbox[0]

            if w <= max_width_for_name:
                fitted_font = test_font
                break

        # 28px에도 안 맞으면 띄어쓰기 기준으로 2줄 처리 (22px 최소 폰트로)
        bbox = draw.textbbox((0, 0), patient_name, font=fitted_font)
        w = bbox[2] - bbox[0]

        if w > max_width_for_name and ' ' in patient_name:
            # 2줄 처리: 29px ~ 20px 범위에서 동적 조정
            y_position = 5  # 2줄일 때는 위에서 시작

            max_font_size_2line = 29  # 2줄일 때 최대 폰트
            fitted_font_2line = patient_name_font

            for font_size in range(max_font_size_2line, min_font_size_2line - 1, -1):
                try:
                    test_font = ImageFont.truetype(font_path, font_size)
                except IOError:
                    test_font = ImageFont.load_default()

                # 각 줄이 라벨 너비에 맞는지 확인
                words = patient_name.split(' ')
                line1 = words[0]
                line2 = ' '.join(words[1:]) if len(words) > 1 else ''

                bbox1 = draw.textbbox((0, 0), line1, font=test_font)
                w1 = bbox1[2] - bbox1[0]

                bbox2 = draw.textbbox((0, 0), line2, font=test_font)
                w2 = bbox2[2] - bbox2[0]

                if w1 <= max_width_for_name and w2 <= max_width_for_name:
                    fitted_font_2line = test_font
                    break

            # 띄어쓰기로 분리하여 2줄로 처리
            words = patient_name.split(' ')
            line1 = words[0]
            line2 = ' '.join(words[1:]) if len(words) > 1 else ''

            # 첫 번째 줄
            bbox1 = draw.textbbox((0, 0), line1, font=fitted_font_2line)
            w1, h1 = bbox1[2] - bbox1[0], bbox1[3] - bbox1[1]
            draw.text(((label_width - w1) / 2, y_position), line1, font=fitted_font_2line, fill="black")
            y_position += h1 + 1  # 줄 간격 축소 (2 → 1)

            # 두 번째 줄
            if line2:
                bbox2 = draw.textbbox((0, 0), line2, font=fitted_font_2line)
                w2, h2 = bbox2[2] - bbox2[0], bbox2[3] - bbox2[1]
                draw.text(((label_width - w2) / 2, y_position), line2, font=fitted_font_2line, fill="black")
                y_position += h2 + 5
        else:
            # 1줄로 표시: 한글과 동일한 위치에서 시작
            y_position = 10
            h = bbox[3] - bbox[1]
            draw.text(((label_width - w) / 2, y_position), patient_name, font=fitted_font, fill="black")
            y_position += h + 5

    # 약품명 시작 위치 고정 (이름 길이와 관계없이 일정한 위치 보장)
    DRUG_NAME_START_Y = 60  # 약품명 시작 위치를 y=60으로 고정
    if y_position < DRUG_NAME_START_Y:
        y_position = DRUG_NAME_START_Y

    # 약품명 정제 (괄호 제거, 단위 변환 등)
    med_name = normalize_medication_name(med_name)

    med_name_display, signature_info, split_occurred = split_med_name(med_name)
    if should_left_align(med_name_display):
        y_position = draw_left_aligned_text(draw, med_name_display, y_position, drug_name_font, max_width=label_width - 40)
        y_position = draw_fitted_single_line(draw, f"({add_info})", y_position, info_font, max_width=label_width - 40)
    else:
        y_position = draw_centered_text(draw, med_name_display, y_position, drug_name_font, max_width=label_width - 40)
        y_position = draw_fitted_single_line(draw, f"({add_info})", y_position, info_font, max_width=label_width - 40)
    if dosage and frequency and duration and unit:
        formatted_dosage = format_dosage(dosage)
        daily_dosage = dosage * frequency
        total_amount = daily_dosage * duration
        if round(dosage, 4) != round(dosage, 3):
            total_amount = round(total_amount * 4) / 4
        formatted_total_amount = str(int(total_amount)) if total_amount.is_integer() else f"{total_amount:.2f}".rstrip('0').rstrip('.')
        converted_total = format_converted_total(dosage, frequency, duration, conversion_factor)
        if converted_total is not None:
            total_label = f"총{formatted_total_amount}{unit}/{duration}일분({converted_total})"
        else:
            total_label = f"총{formatted_total_amount}{unit}/{duration}일분"
        y_position = draw_centered_text(draw, total_label, y_position, additional_info_font, max_width=label_width - 40)
        box_height = 75  # 70 → 75로 증가 (하단 여백 확보)
        box_margin = 10
        box_width = label_width - 40
        box_x1 = (label_width - box_width) // 2
        box_x2 = box_x1 + box_width
        box_y1 = y_position + box_margin
        box_y2 = box_y1 + box_height
        draw.rectangle([box_x1, box_y1, box_x2, box_y2], outline="black", width=2)
        box_padding = 10
        line_spacing = 5
        box_text1 = f"{formatted_dosage}{unit}"
        text1_size = info_font.getbbox(box_text1)
        text1_height = text1_size[3] - text1_size[1]
        frequency_text = custom_dosage_instruction.strip()
        if not frequency_text:
            if frequency == 1:
                frequency_text = "아침"
            elif frequency == 2:
                frequency_text = "아침, 저녁"
            elif frequency == 3:
                frequency_text = "아침, 점심, 저녁"
            elif frequency == 4:
                frequency_text = "아침, 점심, 저녁, 취침"

        # 4회 복용일 때는 작은 폰트 사용 (30px -> 24px)
        frequency_font = info_font
        if frequency == 4 and not custom_dosage_instruction.strip():
            try:
                frequency_font = ImageFont.truetype(font_path, 24)
            except IOError:
                frequency_font = info_font

        text2_height = 0
        if frequency_text:
            text2_size = frequency_font.getbbox(frequency_text)
            text2_height = text2_size[3] - text2_size[1]
        total_text_height = text1_height + line_spacing + text2_height
        center_y = (box_y1 + box_y2) // 2
        adjustment = 7
        start_y = center_y - (total_text_height // 2) - adjustment
        y_temp = draw_centered_text(draw, box_text1, start_y, info_font, max_width=box_width)
        if frequency_text:
            text2_y = y_temp + line_spacing
            draw_centered_text(draw, frequency_text, text2_y, frequency_font, max_width=box_width)
        y_position = box_y2 + box_margin
    if storage_condition:
        storage_condition_text = f"{storage_condition}"
        y_position = draw_centered_text(draw, storage_condition_text, y_position, storage_condition_font, max_width=label_width - 40)

    # === 동적 레이아웃 시스템 ===
    # 상단 컨텐츠의 최종 y_position과 하단 고정 영역의 공간을 계산하여 겹침 방지

    # 1. 시그니처 텍스트 및 크기 계산
    signature_text = signature_info if signature_info else "청 춘 약 국"
    margin_val = int(0.1 * label_width)
    box_width_sig = label_width - 2 * margin_val
    try:
        bbox = draw.textbbox((0, 0), signature_text, font=signature_font)
        w_sig, h_sig = bbox[2] - bbox[0], bbox[3] - bbox[1]
        scale_factor = box_width_sig / w_sig if w_sig != 0 else 1
        scaled_font_size = max(1, int(22 * scale_factor))
        scaled_font = ImageFont.truetype(font_path, scaled_font_size)
    except IOError:
        scaled_font = ImageFont.load_default()
        logging.warning("시그니처 폰트 로드 실패. 기본 폰트 사용.")
    bbox = draw.textbbox((0, 0), signature_text, font=scaled_font)
    w_sig, h_sig = bbox[2] - bbox[0], bbox[3] - bbox[1]

    # 2. 조제일 텍스트 크기 계산
    print_date_text = f"조제일 : {datetime.datetime.now(KOR_TZ).strftime('%Y-%m-%d')}"
    bbox = draw.textbbox((0, 0), print_date_text, font=print_date_font)
    date_w, date_h = bbox[2] - bbox[0], bbox[3] - bbox[1]

    # 3. 하단 고정 영역 필요 공간 계산
    # 기본 패딩 설정
    padding_top = int(h_sig * 0.1)
    padding_bottom = int(h_sig * 0.5)
    padding_sides = int(h_sig * 0.2)

    # 시그니처 박스 높이
    signature_box_height = h_sig + padding_top + padding_bottom

    # 조제일과 시그니처 사이 간격
    date_signature_gap = 5

    # 하단 고정 영역 전체 높이 (조제일 + 간격 + 시그니처 + 하단 여백)
    bottom_fixed_height = date_h + date_signature_gap + signature_box_height + 10

    # 4. 충돌 감지 및 조정
    # 상단 컨텐츠 하단 (y_position) + 최소 여백(2px)
    content_bottom = y_position + 2

    # 하단 고정 영역 시작점
    bottom_fixed_start = label_height - bottom_fixed_height

    # 겹침 여부 확인 (10px 이상 겹칠 때만 조정)
    overlap = content_bottom - bottom_fixed_start

    if overlap > 10:
        # 겹침 발생! 조제일 제거로 공간 확보
        logging.info(f"레이아웃 충돌 감지: {overlap}px 겹침. 조제일 제거로 조정.")

        # 조제일 없이 하단 고정 영역 재계산
        bottom_fixed_height = signature_box_height + 10
        bottom_fixed_start = label_height - bottom_fixed_height

        # 조제일 표시 안 함
        show_date = False

        # 여전히 겹치면 시그니처 패딩 축소
        overlap = content_bottom - bottom_fixed_start
        if overlap > 0:
            logging.info(f"추가 충돌 감지: {overlap}px 겹침. 시그니처 패딩 축소.")
            padding_top = max(2, int(h_sig * 0.05))
            padding_bottom = max(2, int(h_sig * 0.2))
            signature_box_height = h_sig + padding_top + padding_bottom
            bottom_fixed_height = signature_box_height + 5
            bottom_fixed_start = label_height - bottom_fixed_height
    else:
        # 여유 공간 충분
        show_date = True

    # 5. 조제일 그리기 (공간이 충분한 경우에만)
    if show_date:
        print_date_x = (label_width - date_w) / 2
        print_date_y = label_height - bottom_fixed_height
        draw.text((print_date_x, print_date_y), print_date_text, font=print_date_font, fill="black")

        # 시그니처는 조제일 아래에 배치
        signature_y_start = print_date_y + date_h + date_signature_gap
    else:
        # 시그니처는 하단 고정 시작점에 배치
        signature_y_start = bottom_fixed_start

    # 6. 시그니처 박스 그리기
    box_x = (label_width - w_sig) / 2 - padding_sides
    box_y = signature_y_start
    box_x2 = box_x + w_sig + 2 * padding_sides
    box_y2 = box_y + h_sig + padding_top + padding_bottom
    draw.rectangle([(box_x, box_y), (box_x2, box_y2)], outline="black")
    draw.text(((label_width - w_sig) / 2, box_y + padding_top), signature_text, font=scaled_font, fill="black")

    draw_scissor_border(draw, label_width, label_height, edge_size=10, steps=20)
    return image


def print_label(patient_name, med_name, add_info, frequency, dosage, duration,
                formulation_type, main_ingredient_code, dosage_form, administration_route, label_name,
                unit=None, conversion_factor=None, storage_condition="실온보관", custom_dosage_instruction="",
                printer_ip=None, printer_model=None, label_type=None):
    """
    라벨 이미지를 생성하여 프린터로 인쇄합니다.

    Parameters:
        patient_name (str): 환자 이름
        med_name (str): 약품 이름
        add_info (str): 약품 효능 정보
        frequency (int): 복용 횟수
        dosage (float): 복용량
        duration (int): 복용 일수
        formulation_type (str): 제형 타입
        main_ingredient_code (str): 주성분 코드
        dosage_form (str): 복용 형태
        administration_route (str): 투여 경로
        label_name (str): 라벨 명칭
        unit (str, optional): 복용 단위
        conversion_factor (float, optional): 환산계수
        storage_condition (str, optional): 보관 조건
        custom_dosage_instruction (str, optional): 커스텀 용법 텍스트
        printer_ip (str, optional): 프린터 IP 주소 (기본값: DEFAULT_PRINTER_IP)
        printer_model (str, optional): 프린터 모델 (기본값: DEFAULT_PRINTER_MODEL)
        label_type (str, optional): 라벨 타입 (기본값: DEFAULT_LABEL_TYPE)
    """
    try:
        # 프린터 설정 적용 (전달되지 않으면 기본값 사용)
        printer_ip = printer_ip or DEFAULT_PRINTER_IP
        printer_model = printer_model or DEFAULT_PRINTER_MODEL
        label_type = label_type or DEFAULT_LABEL_TYPE

        if not unit:
            if "캡슐" in med_name:
                unit = "캡슐"
            elif "정" in med_name:
                unit = "정"
            elif "시럽" in med_name:
                unit = "ml"
            elif "과립" in med_name or "시럽" in med_name:
                unit = "g"
            else:
                unit = "개"
        label_image = create_label_image(
            patient_name=patient_name,
            med_name=med_name,
            add_info=add_info,
            frequency=frequency,
            dosage=dosage,
            duration=duration,
            formulation_type=formulation_type,
            main_ingredient_code=main_ingredient_code,
            dosage_form=dosage_form,
            administration_route=administration_route,
            label_name=label_name,
            unit=unit,
            conversion_factor=conversion_factor,
            storage_condition=storage_condition,
            custom_dosage_instruction=custom_dosage_instruction
        )
        image_stream = io.BytesIO()
        label_image.save(image_stream, format="PNG")
        image_stream.seek(0)
        from brother_ql.raster import BrotherQLRaster
        from brother_ql.conversion import convert
        from brother_ql.backends.helpers import send

        qlr = BrotherQLRaster(printer_model)
        instructions = convert(
            qlr=qlr,
            images=[Image.open(image_stream)],
            label=label_type,
            rotate="0",
            threshold=70.0,
            dither=False,
            compress=False,
            lq=True,
            red=False
        )
        send(instructions, printer_identifier=f"tcp://{printer_ip}:9100")
        logging.info(f"라벨 인쇄 성공: 환자={patient_name}, 약품={med_name}, 커스텀 용법={custom_dosage_instruction}")
        print(f"[SUCCESS] 라벨 인쇄 성공: 환자={patient_name}, 약품={med_name}, 커스텀 용법={custom_dosage_instruction}")
    except Exception as e:
        logging.error(f"라벨 인쇄 실패: {e}")
        print(f"[ERROR] 라벨 인쇄 실패: {e}")


def print_custom_image(pil_image, printer_ip=None, printer_model=None, label_type=None):
    """
    PIL 이미지를 받아 Brother QL 프린터로 인쇄합니다.

    Parameters:
        pil_image (PIL.Image): 인쇄할 이미지
        printer_ip (str, optional): 프린터 IP 주소 (기본값: DEFAULT_PRINTER_IP)
        printer_model (str, optional): 프린터 모델 (기본값: DEFAULT_PRINTER_MODEL)
        label_type (str, optional): 라벨 타입 (기본값: DEFAULT_LABEL_TYPE)
    """
    try:
        # 프린터 설정 적용 (전달되지 않으면 기본값 사용)
        printer_ip = printer_ip or DEFAULT_PRINTER_IP
        printer_model = printer_model or DEFAULT_PRINTER_MODEL
        label_type = label_type or DEFAULT_LABEL_TYPE

        logging.info(f"이미지 모드: {pil_image.mode}")
        if pil_image.mode in ('RGBA', 'LA'):
            logging.info("알파 채널 있음 (RGBA 또는 LA 모드)")
        elif pil_image.mode == 'P' and 'transparency' in pil_image.info:
            logging.info("알파 채널 있음 (팔레트 모드, transparency 키 확인됨)")
        else:
            logging.info("알파 채널 없음")
        pil_image = pil_image.rotate(90, expand=True)
        width, height = pil_image.size
        new_height = int((306 / width) * height)
        pil_image = pil_image.resize((306, new_height), Image.LANCZOS)
        if pil_image.mode in ('RGBA', 'LA') or (pil_image.mode == 'P' and 'transparency' in pil_image.info):
            background = Image.new("RGB", pil_image.size, "white")
            background.paste(pil_image, mask=pil_image.split()[-1])
            pil_image = background
        image_stream = io.BytesIO()
        pil_image.convert('1').save(image_stream, format="PNG")
        image_stream.seek(0)
        from brother_ql.raster import BrotherQLRaster
        from brother_ql.conversion import convert
        from brother_ql.backends.helpers import send

        # Brother QL 프린터로 전송
        qlr = BrotherQLRaster(printer_model)
        instructions = convert(
            qlr=qlr,
            images=[Image.open(image_stream)],
            label=label_type,
            rotate="0",  # 라벨 회전 없음
            threshold=70.0,  # 흑백 변환 임계값
            dither=False,
            compress=False,
            lq=True,   # 저화질 인쇄 옵션
            red=False
        )
        send(instructions, printer_identifier=f"tcp://{printer_ip}:9100")
        logging.info("커스텀 이미지 인쇄 성공")
        print("[SUCCESS] 커스텀 이미지 인쇄 성공")
    except Exception as e:
        logging.error(f"커스텀 이미지 인쇄 실패: {e}")
        print(f"[ERROR] 커스텀 이미지 인쇄 실패: {e}")


if __name__ == "__main__":
    # 인터랙티브 메뉴를 통해 샘플 인쇄 선택

    samples = {
        "1": {
            "patient_name": "이영희",
            "med_name": "아모크라정375mg",
            "add_info": "고혈압",
            "frequency": 1,
            "dosage": 375.0,
            "duration": 30,
            "formulation_type": "정제",
            "main_ingredient_code": "AMO375",
            "dosage_form": "경구",
            "administration_route": "경구",
            "label_name": "고혈압용",
            "unit": None,
            "conversion_factor": 1.0,
            "storage_condition": "실온보관",
            "custom_dosage_instruction": ""
        },
        "2": {
            "patient_name": "박지성",
            "med_name": "삼남아세트아미노펜정500mg",
            "add_info": "통증 완화",
            "frequency": 2,
            "dosage": 500.0,
            "duration": 5,
            "formulation_type": "정제",
            "main_ingredient_code": "MED001",  # 예시용
            "dosage_form": "경구",
            "administration_route": "경구",
            "label_name": "통증용",
            "unit": None,
            "conversion_factor": 1.0,
            "storage_condition": "서늘한 곳에 보관",
            "custom_dosage_instruction": ""
        },
        "3": {
            "patient_name": "최민수",
            "med_name": "세레타이드125에보할러",
            "add_info": "알레르기 치료",
            "frequency": 3,
            "dosage": 125.0,
            "duration": 10,
            "formulation_type": "정제",
            "main_ingredient_code": "SER125",
            "dosage_form": "흡입",
            "administration_route": "흡입",
            "label_name": "알레르기용",
            "unit": None,
            "conversion_factor": 1.0,
            "storage_condition": "냉장보관",
            "custom_dosage_instruction": ""
        },
        "4": {
            "patient_name": "최민수",
            "med_name": "트윈스타정40/5mg",
            "add_info": "혈압 조절",
            "frequency": 2,
            "dosage": 40.0,
            "duration": 10,
            "formulation_type": "정제",
            "main_ingredient_code": "TW40",
            "dosage_form": "경구",
            "administration_route": "경구",
            "label_name": "고혈압용",
            "unit": None,
            "conversion_factor": 1.0,
            "storage_condition": "실온보관",
            "custom_dosage_instruction": ""
        },
        "5": {
            "patient_name": "최우주",
            "med_name": "오셀타원현탁용분말6mg/mL",
            "add_info": "오셀타미",
            "frequency": 2,
            "dosage": 4.0,
            "duration": 5,
            "formulation_type": "현탁용분말",
            "main_ingredient_code": "358907ASS",
            "dosage_form": "SS",
            "administration_route": "A",
            "label_name": "오셀타원현탁용분말6mg/mL",
            "unit": "ml",
            "conversion_factor": 0.126,
            "storage_condition": "실온및(냉장)",
            "custom_dosage_instruction": ""
        },
        "6": {
            "patient_name": "최우주",
            "med_name": "어린이타이레놀현탁액",
            "add_info": "해열,진통제",
            "frequency": 3,
            "dosage": 3.0,
            "duration": 3,
            "formulation_type": "현탁액",
            "main_ingredient_code": "101330ASS",
            "dosage_form": "SS",
            "administration_route": "A",
            "label_name": "어린이타이레놀현탁액",
            "unit": "ml",
            "conversion_factor": None,
            "storage_condition": "실온보관",
            "custom_dosage_instruction": ""
        }
    }

    print("=======================================")
    print("           라벨 인쇄 샘플 선택           ")
    print("=======================================")
    for key, sample in samples.items():
        print(f"{key}: {sample['patient_name']} / {sample['med_name']} / {sample['add_info']}")
    print("q: 종료")
    
    choice = input("인쇄할 샘플 번호를 선택하세요: ").strip()
    while choice.lower() != 'q':
        if choice in samples:
            sample = samples[choice]
            print(f"선택한 샘플: {sample['patient_name']} / {sample['med_name']}")
            print_label(
                patient_name=sample["patient_name"],
                med_name=sample["med_name"],
                add_info=sample["add_info"],
                frequency=sample["frequency"],
                dosage=sample["dosage"],
                duration=sample["duration"],
                formulation_type=sample["formulation_type"],
                main_ingredient_code=sample["main_ingredient_code"],
                dosage_form=sample["dosage_form"],
                administration_route=sample["administration_route"],
                label_name=sample["label_name"],
                unit=sample["unit"],
                conversion_factor=sample["conversion_factor"],
                storage_condition=sample["storage_condition"],
                custom_dosage_instruction=sample["custom_dosage_instruction"]
            )
        else:
            print("올바른 번호를 입력하세요.")
        choice = input("인쇄할 샴플 번호를 선택하세요 (종료하려면 q 입력): ").strip()