"""
바코드 스캔 시 간단한 라벨 자동 출력 모듈
Brother QL-810W 프린터용
"""

from PIL import Image, ImageDraw, ImageFont
from brother_ql.raster import BrotherQLRaster
from brother_ql.conversion import convert
from brother_ql.backends.helpers import send
import os
import logging
import re
from datetime import datetime
import glob

# 프린터 설정
PRINTER_IP = "192.168.0.168"
PRINTER_PORT = 9100
PRINTER_MODEL = "QL-810W"
LABEL_TYPE = "29"

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


def normalize_medication_name(med_name):
    """
    약품명 정제 (print_label.py의 함수 복사)
    - 괄호 제거
    - 밀리그램 → mg 변환
    - 대괄호 제거

    Args:
        med_name: 약품명

    Returns:
        str: 정제된 약품명
    """
    if not med_name:
        return med_name

    # 대괄호 및 내용 제거
    med_name = re.sub(r'\[.*?\]', '', med_name)
    med_name = re.sub(r'\[.*$', '', med_name)

    # 소괄호 및 내용 제거
    med_name = re.sub(r'\(.*?\)', '', med_name)
    med_name = re.sub(r'\(.*$', '', med_name)

    # 언더스코어 뒤 내용 제거
    med_name = re.sub(r'_.*$', '', med_name)

    # 밀리그램 변환
    med_name = re.sub(r'밀리그램|밀리그람|미리그램|미리그람', 'mg', med_name)

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

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

    # 공백 정리
    med_name = re.sub(r'\s+', ' ', med_name).strip()

    return med_name


def create_wide_label(goods_name, sale_price):
    """
    가로형 와이드 라벨 이미지 생성 (product_label.py 기반)

    - 크기: 800 x 306px (Brother QL 29mm 가로형)
    - 하드코딩: 효능 "치통/진통제", 용법, 사용팁
    - 동적: 약품명만 스캔된 값 사용

    Args:
        goods_name: 약품명 (스캔된 실제 약품명)
        sale_price: 판매가 (사용하지 않음, 호환성 유지)

    Returns:
        PIL.Image: 생성된 가로형 라벨 이미지 (800x306px, mode='1')
    """
    try:
        # 1. 캔버스 생성 (가로로 긴 형태)
        width = 800
        height = 306  # Brother QL 29mm 용지 폭

        img = Image.new('1', (width, height), 1)  # 흰색 배경
        draw = ImageDraw.Draw(img)

        # 2. 폰트 로드
        font_path = os.path.join(os.path.dirname(__file__), "fonts", "malgunbd.ttf")
        try:
            font_effect = ImageFont.truetype(font_path, 72)      # 효능 (매우 크게!)
            font_drugname = ImageFont.truetype(font_path, 36)    # 약품명 (중간)
            font_dosage = ImageFont.truetype(font_path, 50)      # 용법 (크게, 사용팁 없으므로)
            font_pharmacy = ImageFont.truetype(font_path, 32)    # 약국명 (크게)
            font_small = ImageFont.truetype(font_path, 26)       # 사용팁
        except IOError:
            logging.warning("폰트 로드 실패. 기본 폰트 사용.")
            font_effect = ImageFont.load_default()
            font_drugname = ImageFont.load_default()
            font_dosage = ImageFont.load_default()
            font_pharmacy = ImageFont.load_default()
            font_small = ImageFont.load_default()

        # 3. 하드코딩 데이터
        effect = "치통/진통제"
        dosage_instruction = "1캡슐 또는 2캡슐 복용, 1일 최대 5캡슐 [다른 NSAID와 복용시 약사와 상담]"
        usage_tip = "식후 복용 권장"

        # 4. 약품명 정제
        goods_name = normalize_medication_name(goods_name)

        # 5. 레이아웃 시작
        x_margin = 25

        # 효능 - 중앙 상단에 크게 (매우 강조!)
        effect_bbox = draw.textbbox((0, 0), effect, font=font_effect)
        effect_width = effect_bbox[2] - effect_bbox[0]
        effect_x = (width - effect_width) // 2

        # 굵게 표시 (offset)
        for offset in [(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1)]:
            draw.text((effect_x + offset[0], 20 + offset[1]), effect, font=font_effect, fill=0)

        # 약품명 - "치통/진통제 (약품명)" 형식으로 오른쪽에 표시
        # 괄호 형식으로 표시
        drugname_text = f"({goods_name})"

        # 효능 텍스트 끝 위치 계산
        effect_end_x = effect_x + effect_width + 30  # 효능 끝에서 30px 여백

        # 동적 폰트 크기 조정 (박스 안에 들어오도록)
        max_drugname_width = width - effect_end_x - 50  # 오른쪽 여백 50px
        drugname_font_size = 48  # 초기 폰트 크기 (크게 시작)

        while drugname_font_size > 20:  # 최소 20pt까지 축소
            font_drugname_dynamic = ImageFont.truetype(font_path, drugname_font_size)
            drugname_bbox = draw.textbbox((0, 0), drugname_text, font=font_drugname_dynamic)
            drugname_width = drugname_bbox[2] - drugname_bbox[0]

            if drugname_width <= max_drugname_width:
                break
            drugname_font_size -= 2  # 2pt씩 축소

        # 효능과 같은 Y 위치 (중앙 정렬)
        drugname_height = drugname_bbox[3] - drugname_bbox[1]
        drugname_y = 20 + (72 - drugname_height) // 2
        draw.text((effect_end_x, drugname_y), drugname_text, font=font_drugname_dynamic, fill=0)

        # 용법 - 왼쪽 하단에 크게 표시 (동적 폰트 크기 조정)
        y = 120  # 효능 아래부터 시작

        if dosage_instruction:
            # 대괄호로 묶인 부분을 별도 줄로 분리
            dosage_text = re.sub(r'\s*(\[.*?\])\s*', r'\n\1\n', dosage_instruction)

            # 여러 줄 처리
            dosage_lines = []

            # 줄바꿈으로 먼저 분리
            text_parts = dosage_text.split('\n')

            for part in text_parts:
                part = part.strip()
                if not part:
                    continue

                # 대괄호로 묶인 부분은 그대로 한 줄로
                if part.startswith('[') and part.endswith(']'):
                    dosage_lines.append(part)
                # 일반 텍스트는 그대로 추가 (폰트 크기로 조정)
                else:
                    dosage_lines.append(part)

            # 동적 폰트 크기 조정 (박스 안에 들어오도록)
            max_dosage_width = width - x_margin - 50  # 좌우 여백
            dosage_font_size = 50  # 초기 폰트 크기

            # 가장 긴 줄을 기준으로 폰트 크기 조정
            longest_line = max(dosage_lines, key=len) if dosage_lines else ""
            test_line = f"□ {longest_line}"

            while dosage_font_size > 30:  # 최소 30pt까지 축소
                font_dosage_dynamic = ImageFont.truetype(font_path, dosage_font_size)
                test_bbox = draw.textbbox((0, 0), test_line, font=font_dosage_dynamic)
                test_width = test_bbox[2] - test_bbox[0]

                if test_width <= max_dosage_width:
                    break
                dosage_font_size -= 2  # 2pt씩 축소

            # 첫 줄에 체크박스 추가
            if dosage_lines:
                first_line = f"□ {dosage_lines[0]}"
                draw.text((x_margin, y), first_line, font=font_dosage_dynamic, fill=0)

                # 줄 간격 조정 (폰트 크기에 비례)
                line_spacing = int(dosage_font_size * 1.2)
                y += line_spacing

                # 나머지 줄
                for line in dosage_lines[1:]:
                    # 대괄호로 묶인 줄은 들여쓰기 없이
                    indent = 0 if (line.startswith('[') and line.endswith(']')) else 30
                    draw.text((x_margin + indent, y), line, font=font_dosage_dynamic, fill=0)
                    y += line_spacing + 2

        # 사용팁 (체크박스 + 텍스트)
        if usage_tip and y < height - 60:
            tip_text = f"□ {usage_tip}"
            # 길면 축약
            if len(tip_text) > 55:
                tip_text = tip_text[:52] + "..."
            draw.text((x_margin, y), tip_text, font=font_small, fill=0)

        # 약국명 - 오른쪽 하단에 크게 (테두리 박스)
        sign_text = "청춘약국"
        sign_bbox = draw.textbbox((0, 0), sign_text, font=font_pharmacy)
        sign_width = sign_bbox[2] - sign_bbox[0]
        sign_height = sign_bbox[3] - sign_bbox[1]

        # 패딩 설정
        sign_padding_lr = 10  # 좌우 패딩
        sign_padding_top = 5   # 상단 패딩
        sign_padding_bottom = 10  # 하단 패딩

        sign_x = width - sign_width - x_margin - 10 - sign_padding_lr
        sign_y = height - 55  # 위치 고정

        # 테두리 박스 그리기
        box_x1 = sign_x - sign_padding_lr
        box_y1 = sign_y - sign_padding_top
        box_x2 = sign_x + sign_width + sign_padding_lr
        box_y2 = sign_y + sign_height + sign_padding_bottom
        draw.rectangle([box_x1, box_y1, box_x2, box_y2], outline=0, width=2)

        # 약국명 텍스트 (굵게)
        for offset in [(0, 0), (1, 0), (0, 1), (1, 1)]:
            draw.text((sign_x + offset[0], sign_y + offset[1]), sign_text, font=font_pharmacy, fill=0)

        # 테두리 (가위선 스타일)
        for i in range(3):
            draw.rectangle([5 + i, 5 + i, width - 5 - i, height - 5 - i], outline=0)

        logging.info(f"가로형 와이드 라벨 이미지 생성 성공: {goods_name}")
        return img

    except Exception as e:
        logging.error(f"가로형 와이드 라벨 이미지 생성 실패: {e}")
        raise


def cleanup_old_preview_files(max_files=10):
    """
    임시 미리보기 파일 정리 (최대 개수 초과 시 오래된 파일 삭제)

    Args:
        max_files: 유지할 최대 파일 개수
    """
    try:
        temp_dir = os.path.join(os.path.dirname(__file__), "temp")
        if not os.path.exists(temp_dir):
            return

        # label_preview_*.png 파일 목록 가져오기
        preview_files = glob.glob(os.path.join(temp_dir, "label_preview_*.png"))

        # 파일 개수가 max_files를 초과하면 오래된 파일 삭제
        if len(preview_files) > max_files:
            # 생성 시간 기준으로 정렬 (오래된 순)
            preview_files.sort(key=os.path.getmtime)

            # 초과된 파일 삭제
            files_to_delete = preview_files[:len(preview_files) - max_files]
            for file_path in files_to_delete:
                try:
                    os.remove(file_path)
                    logging.info(f"오래된 미리보기 파일 삭제: {file_path}")
                except Exception as e:
                    logging.warning(f"파일 삭제 실패: {file_path} - {e}")

    except Exception as e:
        logging.warning(f"미리보기 파일 정리 실패: {e}")


def print_barcode_label(goods_name, sale_price, preview_mode=False):
    """
    바코드 스캔 시 가로형 와이드 라벨 출력 또는 미리보기

    Args:
        goods_name: 약품명
        sale_price: 판매가 (호환성 유지용, 내부 미사용)
        preview_mode: True = 이미지 파일 경로 반환, False = 프린터 전송

    Returns:
        preview_mode=True: (성공 여부, 이미지 파일 경로)
        preview_mode=False: 성공 여부 (bool)
    """
    try:
        logging.info(f"가로형 와이드 라벨 {'미리보기' if preview_mode else '출력'} 시작: {goods_name}")

        # 1. 가로형 라벨 이미지 생성
        label_image = create_wide_label(goods_name, sale_price)

        # 2. 미리보기 모드: PNG 파일로 저장
        if preview_mode:
            # temp 디렉터리 생성
            temp_dir = os.path.join(os.path.dirname(__file__), "temp")
            os.makedirs(temp_dir, exist_ok=True)

            # 파일명 생성 (타임스탬프 포함)
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
            filename = f"label_preview_{timestamp}.png"
            file_path = os.path.join(temp_dir, filename)

            # PNG로 저장 (회전하지 않은 가로형 이미지)
            label_image.save(file_path, "PNG")

            logging.info(f"미리보기 이미지 저장 완료: {file_path}")

            # 오래된 파일 정리
            cleanup_old_preview_files(max_files=10)

            return True, file_path

        # 3. 실제 인쇄 모드: 프린터로 전송
        else:
            # 이미지 90도 회전 (시계 반대방향)
            # Brother QL은 세로 방향 기준이므로 가로형 이미지를 회전
            label_image_rotated = label_image.rotate(90, expand=True)

            logging.info(f"이미지 회전 완료: {label_image_rotated.size}")

            # Brother QL Raster 객체 생성
            qlr = BrotherQLRaster(PRINTER_MODEL)

            # PIL 이미지를 Brother QL 형식으로 변환
            instructions = convert(
                qlr=qlr,
                images=[label_image_rotated],
                label=LABEL_TYPE,
                rotate="0",          # 이미 회전했으므로 0
                threshold=70.0,      # 흑백 변환 임계값
                dither=False,        # 디더링 비활성화 (선명한 텍스트)
                compress=False,      # 압축 비활성화
                red=False,           # 흑백 전용
                dpi_600=False,
                hq=True,             # 고품질 모드
                cut=True             # 자동 절단
            )

            # 프린터로 전송
            printer_identifier = f"tcp://{PRINTER_IP}:{PRINTER_PORT}"
            send(instructions, printer_identifier=printer_identifier)

            logging.info(f"가로형 와이드 라벨 출력 성공: {goods_name}")
            return True

    except Exception as e:
        logging.error(f"가로형 와이드 라벨 {'미리보기' if preview_mode else '출력'} 실패: {e}")
        if preview_mode:
            return False, None
        else:
            return False


if __name__ == "__main__":
    # 테스트 코드
    test_result = print_barcode_label("타이레놀정500mg", 3000.0)
    print(f"테스트 결과: {'성공' if test_result else '실패'}")
