"""
POS 판매 내역 조회 GUI (PyQt5)
MSSQL SALE_MAIN 테이블에서 오늘 판매 내역을 조회하여 표시
"""

import sys
import os
import json
from datetime import datetime

# Qt 플랫폼 플러그인 경로 자동 설정 (PyQt5 import 전에 반드시 설정)
if not os.environ.get('QT_QPA_PLATFORM_PLUGIN_PATH'):
    import importlib.util
    _spec = importlib.util.find_spec('PyQt5')
    if _spec and _spec.origin:
        _pyqt5_plugins = os.path.join(
            os.path.dirname(_spec.origin), 'Qt5', 'plugins', 'platforms'
        )
        if os.path.isdir(_pyqt5_plugins):
            os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = _pyqt5_plugins

from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
    QPushButton, QLabel, QGroupBox, QTableWidget, QTableWidgetItem,
    QDialog, QMessageBox, QDateEdit, QCheckBox
)
from PyQt5.QtCore import QThread, pyqtSignal, Qt, QDate, QTimer
from PyQt5.QtGui import QFont, QColor

# 데이터베이스 연결 (backend/ 폴더를 Python 경로에 추가)
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from db.dbsetup import DatabaseManager

# QR 생성 모듈 import
from utils.qr_token_generator import generate_claim_token, save_token_to_db
from utils.qr_label_printer import print_qr_label


class SalesQueryThread(QThread):
    """
    판매 내역 조회 백그라운드 스레드
    GUI 블로킹을 방지하기 위해 DB 쿼리를 별도 스레드에서 실행
    """
    query_complete = pyqtSignal(list)  # 조회 완료 시그널
    query_error = pyqtSignal(str)      # 에러 발생 시그널

    def __init__(self, date_str):
        """
        Args:
            date_str: 조회할 날짜 (YYYYMMDD 형식)
        """
        super().__init__()
        self.date_str = date_str

    def run(self):
        """스레드 실행 (SALE_MAIN 조회 + SQLite 적립 사용자 조회)"""
        mssql_conn = None
        sqlite_conn = None
        try:
            db_manager = DatabaseManager()

            # MSSQL 연결
            mssql_conn = db_manager.get_engine('PM_PRES').raw_connection()
            mssql_cursor = mssql_conn.cursor()

            # SQLite 연결
            sqlite_conn = db_manager.get_sqlite_connection()
            sqlite_cursor = sqlite_conn.cursor()

            # 메인 쿼리: SALE_MAIN + CD_SUNAB(수납) 조인
            # CD_SUNAB.PRESERIAL = SALE_MAIN.SL_NO_order (주문번호 기준)
            query = """
            SELECT
                M.SL_NO_order,
                M.InsertTime,
                M.SL_MY_sale,
                ISNULL(M.SL_NM_custom, '[비고객]') AS customer_name,
                ISNULL(S.card_total, 0) AS card_total,
                ISNULL(S.cash_total, 0) AS cash_total,
                ISNULL(M.SL_MY_total, 0) AS total_amount,
                ISNULL(M.SL_MY_discount, 0) AS discount,
                S.cash_receipt_mode,
                S.cash_receipt_num
            FROM SALE_MAIN M
            OUTER APPLY (
                SELECT TOP 1
                    ISNULL(ETC_CARD, 0) + ISNULL(OTC_CARD, 0) AS card_total,
                    ISNULL(ETC_CASH, 0) + ISNULL(OTC_CASH, 0) AS cash_total,
                    nCASHINMODE AS cash_receipt_mode,
                    nAPPROVAL_NUM AS cash_receipt_num
                FROM CD_SUNAB
                WHERE PRESERIAL = M.SL_NO_order
            ) S
            WHERE M.SL_DT_appl = ?
            ORDER BY M.InsertTime DESC
            """

            mssql_cursor.execute(query, self.date_str)
            rows = mssql_cursor.fetchall()

            sales_list = []
            for row in rows:
                order_no, insert_time, sale_amount, customer, card_total, cash_total, total_amount, discount, cash_receipt_mode, cash_receipt_num = row

                # 품목 수 조회 (SALE_SUB)
                mssql_cursor.execute("""
                    SELECT COUNT(*) FROM SALE_SUB
                    WHERE SL_NO_order = ?
                """, order_no)
                item_count_row = mssql_cursor.fetchone()
                item_count = item_count_row[0] if item_count_row else 0

                # SQLite에서 QR 발행 여부 확인
                sqlite_cursor.execute("""
                    SELECT id FROM claim_tokens
                    WHERE transaction_id = ?
                """, (order_no,))
                qr_record = sqlite_cursor.fetchone()
                qr_issued = bool(qr_record)

                # SQLite에서 적립 사용자 조회
                sqlite_cursor.execute("""
                    SELECT u.nickname, u.phone, ct.claimable_points
                    FROM claim_tokens ct
                    LEFT JOIN users u ON ct.claimed_by_user_id = u.id
                    WHERE ct.transaction_id = ? AND ct.claimed_at IS NOT NULL
                """, (order_no,))
                claimed_user = sqlite_cursor.fetchone()

                # 적립 사용자 정보 분리
                if claimed_user and claimed_user['nickname'] and claimed_user['phone']:
                    claimed_name = claimed_user['nickname']
                    claimed_phone = claimed_user['phone']
                    claimed_points = claimed_user['claimable_points']
                else:
                    claimed_name = ""
                    claimed_phone = ""
                    claimed_points = 0

                # 결제수단 판별
                card_amt = float(card_total) if card_total else 0.0
                cash_amt = float(cash_total) if cash_total else 0.0
                # 현금영수증: nCASHINMODE='1' AND nAPPROVAL_NUM 존재 (mode=2는 카드거래 자동세팅)
                has_cash_receipt = (
                    str(cash_receipt_mode or '').strip() == '1'
                    and str(cash_receipt_num or '').strip() != ''
                )
                if card_amt > 0 and cash_amt > 0:
                    pay_method = '카드+현금'
                elif card_amt > 0:
                    pay_method = '카드'
                elif cash_amt > 0:
                    pay_method = '현영' if has_cash_receipt else '현금'
                else:
                    pay_method = ''
                paid = (card_amt + cash_amt) > 0

                disc_amt = float(discount) if discount else 0.0
                total_amt = float(total_amount) if total_amount else 0.0

                sales_list.append({
                    'order_no': order_no,
                    'time': insert_time.strftime('%H:%M') if insert_time else '--:--',
                    'amount': float(sale_amount) if sale_amount else 0.0,
                    'discount': disc_amt,
                    'total_before_dc': total_amt,
                    'customer': customer,
                    'pay_method': pay_method,
                    'paid': paid,
                    'item_count': item_count,
                    'claimed_name': claimed_name,
                    'claimed_phone': claimed_phone,
                    'claimed_points': claimed_points,
                    'qr_issued': qr_issued
                })

            self.query_complete.emit(sales_list)

        except Exception as e:
            self.query_error.emit(str(e))
        finally:
            if mssql_conn:
                mssql_conn.close()
            # sqlite_conn은 싱글톤이므로 닫지 않음 (닫으면 다른 곳에서 I/O 에러 발생)


class QRGeneratorThread(QThread):
    """
    QR 토큰 생성 및 라벨 출력 백그라운드 스레드
    GUI 블로킹 방지
    """
    qr_complete = pyqtSignal(bool, str, str)  # 성공 여부, 메시지, 이미지 경로

    def __init__(self, transaction_id, total_amount, transaction_time, preview_mode=False,
                 printer_mode='zebra', pos_config=None):
        """
        Args:
            transaction_id (str): POS 거래 ID
            total_amount (float): 판매 금액
            transaction_time (datetime): 거래 시간
            preview_mode (bool): 미리보기 모드
            printer_mode (str): 프린터 모드 ('zebra' or 'pos')
            pos_config (dict): POS 프린터 설정 {'ip': ..., 'port': ...}
        """
        super().__init__()
        self.transaction_id = transaction_id
        self.total_amount = total_amount
        self.transaction_time = transaction_time
        self.preview_mode = preview_mode
        self.printer_mode = printer_mode
        self.pos_config = pos_config

    def run(self):
        """스레드 실행"""
        try:
            # 1. Claim Token 생성
            token_info = generate_claim_token(
                self.transaction_id,
                self.total_amount
            )

            # 2. DB 저장
            success, error = save_token_to_db(
                self.transaction_id,
                token_info['token_hash'],
                self.total_amount,
                token_info['claimable_points'],
                token_info['expires_at'],
                token_info['pharmacy_id']
            )

            if not success:
                self.qr_complete.emit(False, error, "")
                return

            # 3. 프린터 모드별 인쇄
            if self.printer_mode == 'zebra':
                # Zebra 라벨 프린터
                if self.preview_mode:
                    # 미리보기
                    success, image_path = print_qr_label(
                        token_info['qr_url'],
                        self.transaction_id,
                        self.total_amount,
                        token_info['claimable_points'],
                        self.transaction_time,
                        preview_mode=True
                    )

                    if success:
                        self.qr_complete.emit(
                            True,
                            f"QR 생성 완료 ({token_info['claimable_points']}P)",
                            image_path
                        )
                    else:
                        self.qr_complete.emit(False, "이미지 생성 실패", "")
                else:
                    # 실제 인쇄
                    success = print_qr_label(
                        token_info['qr_url'],
                        self.transaction_id,
                        self.total_amount,
                        token_info['claimable_points'],
                        self.transaction_time,
                        preview_mode=False
                    )

                    if success:
                        self.qr_complete.emit(
                            True,
                            f"Zebra 라벨 출력 완료 ({token_info['claimable_points']}P)",
                            ""
                        )
                    else:
                        self.qr_complete.emit(False, "Zebra 프린터 전송 실패", "")

            elif self.printer_mode == 'pos':
                # POS 영수증 프린터
                from utils.pos_qr_printer import print_qr_receipt_escpos

                if not self.pos_config or not self.pos_config.get('ip'):
                    self.qr_complete.emit(False, 'POS 프린터 설정이 필요합니다', '')
                    return

                success = print_qr_receipt_escpos(
                    token_info['qr_url'],
                    self.transaction_id,
                    self.total_amount,
                    token_info['claimable_points'],
                    self.transaction_time,
                    self.pos_config['ip'],
                    self.pos_config.get('port', 9100)
                )

                if success:
                    self.qr_complete.emit(
                        True,
                        f"POS 영수증 출력 완료 ({token_info['claimable_points']}P)",
                        ''
                    )
                else:
                    self.qr_complete.emit(False, 'POS 프린터 인쇄 실패', '')

        except Exception as e:
            self.qr_complete.emit(False, f"오류: {str(e)}", "")


class QRLabelPreviewDialog(QDialog):
    """
    QR 라벨 미리보기 팝업 (barcode_reader_gui.py의 LabelPreviewDialog 참고)
    """

    def __init__(self, image_path, transaction_id, parent=None):
        """
        Args:
            image_path (str): 미리보기 이미지 파일 경로
            transaction_id (str): 거래 번호
            parent: 부모 위젯
        """
        super().__init__(parent)
        self.image_path = image_path
        self.transaction_id = transaction_id
        self.init_ui()

    def init_ui(self):
        """UI 초기화"""
        self.setWindowTitle(f'QR 라벨 미리보기 - {self.transaction_id}')
        self.setModal(False)  # 모달 아님 (계속 작업 가능)

        layout = QVBoxLayout()

        # 안내 라벨
        info_label = QLabel('[미리보기] 실제 인쇄하려면 "미리보기 모드" 체크를 해제하세요.')
        info_label.setStyleSheet('color: #2196F3; font-size: 12px; padding: 10px;')
        layout.addWidget(info_label)

        # 이미지 표시
        from PyQt5.QtGui import QPixmap
        pixmap = QPixmap(self.image_path)

        # 스케일링 (최대 1000px 폭)
        if pixmap.width() > 1000:
            pixmap = pixmap.scaledToWidth(1000, Qt.SmoothTransformation)

        image_label = QLabel()
        image_label.setPixmap(pixmap)
        image_label.setAlignment(Qt.AlignCenter)
        layout.addWidget(image_label)

        # 닫기 버튼
        close_btn = QPushButton('닫기')
        close_btn.setStyleSheet('background-color: #2196F3; color: white; font-weight: bold; padding: 8px 20px;')
        close_btn.clicked.connect(self.close)
        layout.addWidget(close_btn)

        self.setLayout(layout)
        self.adjustSize()


class SaleDetailDialog(QDialog):
    """
    판매 상세 조회 팝업
    더블클릭한 판매 건의 SALE_SUB 품목 상세 표시
    """

    def __init__(self, order_no, parent=None):
        """
        Args:
            order_no: 판매 주문번호 (SL_NO_order)
            parent: 부모 위젯
        """
        super().__init__(parent)
        self.order_no = order_no
        self.setWindowTitle(f'판매 상세: {order_no}')
        self.setModal(True)
        self.resize(700, 400)
        self.init_ui()
        self.load_details()

    def init_ui(self):
        """UI 초기화"""
        layout = QVBoxLayout()

        # 제목
        title_label = QLabel(f'주문번호: {self.order_no}')
        title_label.setStyleSheet('font-size: 14px; font-weight: bold; padding: 10px;')
        layout.addWidget(title_label)

        # 상세 테이블
        self.detail_table = QTableWidget()
        self.detail_table.setColumnCount(4)
        self.detail_table.setHorizontalHeaderLabels([
            '약품코드', '약품명', '수량', '금액'
        ])
        self.detail_table.setColumnWidth(0, 100)
        self.detail_table.setColumnWidth(1, 300)
        self.detail_table.setColumnWidth(2, 80)
        self.detail_table.setColumnWidth(3, 100)
        layout.addWidget(self.detail_table)

        # 닫기 버튼
        close_btn = QPushButton('닫기')
        close_btn.setStyleSheet('background-color: #2196F3; color: white; padding: 8px; font-weight: bold;')
        close_btn.clicked.connect(self.close)
        layout.addWidget(close_btn)

        self.setLayout(layout)

    def load_details(self):
        """SALE_SUB + CD_GOODS 조인 조회"""
        conn = None
        try:
            db_manager = DatabaseManager()
            conn = db_manager.get_engine('PM_PRES').raw_connection()
            cursor = conn.cursor()

            query = """
            SELECT
                S.DrugCode,
                ISNULL(G.GoodsName, '(약품명 없음)') AS goods_name,
                S.SL_NM_item AS quantity,
                S.SL_TOTAL_PRICE
            FROM SALE_SUB S
            LEFT JOIN PM_DRUG.dbo.CD_GOODS G ON S.DrugCode = G.DrugCode
            WHERE S.SL_NO_order = ?
            ORDER BY S.DrugCode
            """

            cursor.execute(query, self.order_no)
            rows = cursor.fetchall()

            # 테이블에 데이터 채우기
            self.detail_table.setRowCount(len(rows))
            for row_idx, row in enumerate(rows):
                drug_code, goods_name, quantity, price = row

                # 약품코드
                self.detail_table.setItem(row_idx, 0, QTableWidgetItem(str(drug_code)))

                # 약품명
                self.detail_table.setItem(row_idx, 1, QTableWidgetItem(goods_name))

                # 수량 (중앙 정렬)
                qty_item = QTableWidgetItem(str(quantity))
                qty_item.setTextAlignment(Qt.AlignCenter)
                self.detail_table.setItem(row_idx, 2, qty_item)

                # 금액 (우측 정렬, 천단위 콤마)
                price_value = float(price) if price else 0.0
                price_item = QTableWidgetItem(f'{price_value:,.0f}원')
                price_item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                self.detail_table.setItem(row_idx, 3, price_item)

        except Exception as e:
            QMessageBox.critical(self, '오류', f'상세 조회 실패:\n{str(e)}')
        finally:
            if conn:
                conn.close()


class UserMileageDialog(QDialog):
    """
    회원 마일리지 내역 조회 팝업
    적립 사용자 클릭 시 해당 회원의 적립 내역 표시
    """

    def __init__(self, phone, parent=None):
        """
        Args:
            phone: 전화번호
            parent: 부모 위젯
        """
        super().__init__(parent)
        self.phone = phone
        self.setWindowTitle(f'회원 마일리지 내역')
        self.setModal(True)
        self.resize(800, 500)
        self.init_ui()
        self.load_user_info()

    def init_ui(self):
        """UI 초기화"""
        layout = QVBoxLayout()

        # 회원 정보 그룹박스
        info_group = QGroupBox('회원 정보')
        info_layout = QVBoxLayout()
        info_group.setLayout(info_layout)

        self.info_label = QLabel('조회 중...')
        self.info_label.setStyleSheet('font-size: 13px; padding: 10px; background: #f5f7fa; border-radius: 8px;')
        info_layout.addWidget(self.info_label)

        layout.addWidget(info_group)

        # 적립 내역 테이블
        history_group = QGroupBox('적립 내역')
        history_layout = QVBoxLayout()
        history_group.setLayout(history_layout)

        self.history_table = QTableWidget()
        self.history_table.setColumnCount(5)
        self.history_table.setHorizontalHeaderLabels([
            '날짜', '구분', '포인트', '잔액', '설명'
        ])
        self.history_table.setColumnWidth(0, 150)
        self.history_table.setColumnWidth(1, 80)
        self.history_table.setColumnWidth(2, 100)
        self.history_table.setColumnWidth(3, 100)
        self.history_table.setColumnWidth(4, 300)
        history_layout.addWidget(self.history_table)

        layout.addWidget(history_group)

        # 닫기 버튼
        close_btn = QPushButton('닫기')
        close_btn.setStyleSheet('background-color: #2196F3; color: white; padding: 8px; font-weight: bold;')
        close_btn.clicked.connect(self.close)
        layout.addWidget(close_btn)

        self.setLayout(layout)

    def load_user_info(self):
        """SQLite에서 회원 정보 및 적립 내역 조회"""
        conn = None
        try:
            db_manager = DatabaseManager()
            conn = db_manager.get_sqlite_connection()
            cursor = conn.cursor()

            # 전화번호로 사용자 조회
            cursor.execute("""
                SELECT id, nickname, phone, mileage_balance, created_at
                FROM users WHERE phone = ?
            """, (self.phone,))

            user = cursor.fetchone()

            if not user:
                self.info_label.setText('등록되지 않은 회원입니다.')
                return

            # 회원 정보 표시
            info_text = f"""
            <b>이름:</b> {user['nickname']}<br>
            <b>전화번호:</b> {user['phone']}<br>
            <b>포인트 잔액:</b> <span style="color: #4CAF50; font-weight: bold;">{user['mileage_balance']:,}P</span><br>
            <b>가입일:</b> {user['created_at']}
            """
            self.info_label.setText(info_text)

            # 적립 내역 조회
            cursor.execute("""
                SELECT points, balance_after, reason, description, created_at
                FROM mileage_ledger
                WHERE user_id = ?
                ORDER BY created_at DESC
                LIMIT 50
            """, (user['id'],))

            transactions = cursor.fetchall()

            # 테이블에 데이터 채우기
            self.history_table.setRowCount(len(transactions))
            for row_idx, tx in enumerate(transactions):
                from PyQt5.QtGui import QColor

                # 날짜
                date_item = QTableWidgetItem(tx['created_at'])
                self.history_table.setItem(row_idx, 0, date_item)

                # 구분 (CLAIM, USE 등)
                reason_text = '적립' if tx['reason'] == 'CLAIM' else '사용'
                reason_item = QTableWidgetItem(reason_text)
                reason_item.setTextAlignment(Qt.AlignCenter)
                if tx['reason'] == 'CLAIM':
                    reason_item.setForeground(QColor('#4CAF50'))
                else:
                    reason_item.setForeground(QColor('#f03e3e'))
                self.history_table.setItem(row_idx, 1, reason_item)

                # 포인트 (우측 정렬)
                points_item = QTableWidgetItem(f"{tx['points']:+,}P")
                points_item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                if tx['points'] > 0:
                    points_item.setForeground(QColor('#4CAF50'))
                else:
                    points_item.setForeground(QColor('#f03e3e'))
                self.history_table.setItem(row_idx, 2, points_item)

                # 잔액 (우측 정렬)
                balance_item = QTableWidgetItem(f"{tx['balance_after']:,}P")
                balance_item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                self.history_table.setItem(row_idx, 3, balance_item)

                # 설명
                self.history_table.setItem(row_idx, 4, QTableWidgetItem(tx['description']))

        except Exception as e:
            QMessageBox.critical(self, '오류', f'회원 정보 조회 실패:\n{str(e)}')
        # conn은 싱글톤이므로 닫지 않음


class POSSalesGUI(QMainWindow):
    """
    POS 판매 내역 조회 메인 GUI
    """
    CONFIG_FILE = os.path.join(os.path.dirname(__file__), 'gui_settings.json')

    # 판매 테이블 컬럼 정의: (헤더명, 기본폭, 데이터키)
    SALES_COLUMNS = [
        ('주문번호',    150, 'order_no'),
        ('시간',        70, 'time'),
        ('금액',       100, 'amount'),
        ('결제',        80, 'pay_method'),
        ('수납',        50, 'paid'),
        ('고객명',      80, 'customer'),
        ('품목수',      55, 'item_count'),
        ('적립자',      90, 'claimed_name'),
        ('전화번호',   120, 'claimed_phone'),
        ('적립포인트',  90, 'claimed_points'),
        ('QR',          50, 'qr_issued'),
    ]

    def __init__(self):
        super().__init__()
        self.db_manager = DatabaseManager()
        self.sales_thread = None
        self.qr_thread = None  # QR 생성 스레드 추가
        self.sales_data = []
        self._gui_settings = self._load_settings()
        self.init_ui()

    def init_ui(self):
        """UI 초기화"""
        self.setWindowTitle('POS 판매 조회')
        saved_geo = self._gui_settings.get('window_geometry')
        if saved_geo and len(saved_geo) == 4:
            self.setGeometry(*saved_geo)
        else:
            self.setGeometry(100, 100, 1300, 600)

        # 중앙 위젯
        central_widget = QWidget()
        self.setCentralWidget(central_widget)

        main_layout = QVBoxLayout()
        central_widget.setLayout(main_layout)

        # === 1. 조회 설정 그룹 ===
        settings_group = QGroupBox('조회 설정')
        settings_layout = QHBoxLayout()
        settings_group.setLayout(settings_layout)

        # 날짜 선택기
        settings_layout.addWidget(QLabel('날짜:'))
        self.date_edit = QDateEdit()
        self.date_edit.setCalendarPopup(True)
        self.date_edit.setDate(QDate.currentDate())
        self.date_edit.setDisplayFormat('yyyy-MM-dd')
        self.date_edit.dateChanged.connect(self.on_date_changed)
        settings_layout.addWidget(self.date_edit)

        # 새로고침 버튼
        self.refresh_btn = QPushButton('새로고침')
        self.refresh_btn.setStyleSheet('background-color: #4CAF50; color: white; padding: 8px; font-weight: bold;')
        self.refresh_btn.clicked.connect(self.refresh_sales)
        settings_layout.addWidget(self.refresh_btn)

        # QR 생성 버튼 (활성화)
        self.qr_btn = QPushButton('QR 생성')
        self.qr_btn.setEnabled(True)  # 활성화!
        self.qr_btn.setStyleSheet('background-color: #FF9800; color: white; padding: 8px; font-weight: bold;')
        self.qr_btn.setToolTip('선택된 거래의 QR 적립 라벨 생성')
        self.qr_btn.clicked.connect(self.generate_qr_label)  # 이벤트 연결
        settings_layout.addWidget(self.qr_btn)

        # 키오스크 적립 버튼
        self.kiosk_btn = QPushButton('키오스크 적립')
        self.kiosk_btn.setStyleSheet(
            'background-color: #6366f1; color: white; padding: 8px; font-weight: bold;')
        self.kiosk_btn.setToolTip('선택된 거래를 키오스크 화면에 표시')
        self.kiosk_btn.clicked.connect(self.trigger_kiosk_claim)
        settings_layout.addWidget(self.kiosk_btn)

        # 미리보기 모드 체크박스 추가
        self.preview_checkbox = QCheckBox('미리보기 모드')
        self.preview_checkbox.setChecked(True)  # 기본값: 미리보기
        self.preview_checkbox.setStyleSheet('font-size: 12px; color: #4CAF50;')
        self.preview_checkbox.setToolTip('체크: PNG 미리보기, 해제: 프린터 직접 출력')
        settings_layout.addWidget(self.preview_checkbox)

        # === 프린터 선택 토글 버튼 추가 ===
        self.printer_toggle = QPushButton('🖨️ Zebra 라벨')
        self.printer_toggle.setCheckable(True)
        self.printer_toggle.setChecked(True)  # 기본값: Zebra
        self.printer_toggle.setToolTip('클릭하여 프린터 전환\n✓ Zebra QL-810W 라벨\n✗ POS 영수증 프린터')
        self.printer_toggle.clicked.connect(self.toggle_printer_mode)
        self.printer_toggle.setStyleSheet("""
            QPushButton {
                padding: 8px 16px;
                font-weight: bold;
                border: 2px solid #2196F3;
                border-radius: 4px;
                background-color: #E3F2FD;
                color: #1976D2;
            }
            QPushButton:checked {
                background-color: #FFF3E0;
                border-color: #FF9800;
                color: #E65100;
            }
            QPushButton:hover {
                background-color: #BBDEFB;
            }
            QPushButton:checked:hover {
                background-color: #FFE0B2;
            }
        """)
        settings_layout.addWidget(self.printer_toggle)

        # POS 프린터 설정 버튼 (토글이 POS일 때만 표시)
        self.pos_settings_btn = QPushButton('⚙️ POS 설정')
        self.pos_settings_btn.setVisible(False)
        self.pos_settings_btn.clicked.connect(self.open_pos_settings)
        self.pos_settings_btn.setToolTip('POS 프린터 IP/포트 설정')
        self.pos_settings_btn.setStyleSheet('background-color: #9E9E9E; color: white; padding: 8px; font-weight: bold;')
        settings_layout.addWidget(self.pos_settings_btn)

        settings_layout.addStretch()

        main_layout.addWidget(settings_group)

        # === 2. 판매 내역 테이블 ===
        sales_group = QGroupBox('판매 내역')
        sales_layout = QVBoxLayout()
        sales_group.setLayout(sales_layout)

        self.sales_table = QTableWidget()
        col_count = len(self.SALES_COLUMNS)
        self.sales_table.setColumnCount(col_count)
        self.sales_table.setHorizontalHeaderLabels([c[0] for c in self.SALES_COLUMNS])

        # 컬럼 폭: 저장된 값 우선, 없으면 SALES_COLUMNS 기본값
        saved_widths = self._gui_settings.get('sales_column_widths')
        for i, (_, default_w, _) in enumerate(self.SALES_COLUMNS):
            w = saved_widths[i] if saved_widths and len(saved_widths) == col_count else default_w
            self.sales_table.setColumnWidth(i, w)

        self.sales_table.horizontalHeader().setStretchLastSection(False)
        self.sales_table.horizontalHeader().sectionResized.connect(self._on_column_resized)
        self.sales_table.setSelectionBehavior(QTableWidget.SelectRows)
        self.sales_table.doubleClicked.connect(self.show_sale_detail)
        self.sales_table.cellClicked.connect(self.on_cell_clicked)

        sales_layout.addWidget(self.sales_table)

        main_layout.addWidget(sales_group)

        # === 3. 상태바 ===
        status_layout = QHBoxLayout()

        self.status_label = QLabel('대기 중...')
        self.status_label.setStyleSheet('color: gray; font-size: 12px; padding: 5px;')
        status_layout.addWidget(self.status_label)

        status_layout.addStretch()

        self.total_label = QLabel('총 매출: 0원')
        self.total_label.setStyleSheet('color: blue; font-size: 12px; font-weight: bold; padding: 5px;')
        status_layout.addWidget(self.total_label)

        main_layout.addLayout(status_layout)

        # 초기 조회 실행
        self.refresh_sales()

        # 자동 새로고침 타이머 (5초마다)
        self.refresh_timer = QTimer()
        self.refresh_timer.timeout.connect(self.refresh_sales)
        self.refresh_timer.start(5000)  # 5초 = 5000ms

    def on_date_changed(self, date):
        """날짜 변경 시 유효성 검사"""
        today = QDate.currentDate()
        if date > today:
            QMessageBox.warning(self, '경고', '미래 날짜는 선택할 수 없습니다.')
            self.date_edit.setDate(today)

    def refresh_sales(self):
        """판매 내역 조회 시작"""
        # 중복 방지
        if self.sales_thread and self.sales_thread.isRunning():
            return

        date_str = self.date_edit.date().toString('yyyyMMdd')

        # 스레드 시작
        self.sales_thread = SalesQueryThread(date_str)
        self.sales_thread.query_complete.connect(self.on_sales_loaded)
        self.sales_thread.query_error.connect(self.on_query_error)
        self.sales_thread.start()

        # UI 업데이트
        self.status_label.setText('조회 중...')
        self.status_label.setStyleSheet('color: orange; font-size: 12px; padding: 5px;')
        self.refresh_btn.setEnabled(False)

    def on_sales_loaded(self, sales_list):
        """조회 완료 시 테이블 갱신"""
        self.sales_data = sales_list
        self.populate_table(sales_list)

        # 총 매출 계산
        total_amount = sum(sale['amount'] for sale in sales_list)

        # 상태 업데이트
        self.status_label.setText(f'{len(sales_list)}건 조회 완료')
        self.status_label.setStyleSheet('color: green; font-size: 12px; padding: 5px;')
        self.total_label.setText(f'총 매출: {total_amount:,.0f}원')
        self.refresh_btn.setEnabled(True)

    def populate_table(self, sales_list):
        """QTableWidget에 데이터 채우기"""
        # 컬럼 인덱스 맵 (SALES_COLUMNS 순서 기반)
        COL = {key: i for i, (_, _, key) in enumerate(self.SALES_COLUMNS)}

        self.sales_table.setRowCount(len(sales_list))

        # 적립 완료 셀 스타일
        CLAIMED_COLOR = QColor('#4CAF50')
        def make_claimed_font(underline=True):
            f = QFont()
            f.setBold(True)
            if underline:
                f.setUnderline(True)
            return f

        for row, sale in enumerate(sales_list):
            # 주문번호
            self.sales_table.setItem(row, COL['order_no'],
                QTableWidgetItem(sale['order_no']))

            # 시간
            self.sales_table.setItem(row, COL['time'],
                QTableWidgetItem(sale['time']))

            # 금액 (우측 정렬, 천단위 콤마, 할인 표시)
            if sale['discount'] > 0:
                amount_item = QTableWidgetItem(f"{sale['amount']:,.0f}원 (-{sale['discount']:,.0f})")
                amount_item.setForeground(QColor('#E65100'))
                f = QFont()
                f.setBold(True)
                amount_item.setFont(f)
                amount_item.setToolTip(
                    f"원가: {sale['total_before_dc']:,.0f}원\n"
                    f"할인: -{sale['discount']:,.0f}원\n"
                    f"결제: {sale['amount']:,.0f}원"
                )
            else:
                amount_item = QTableWidgetItem(f"{sale['amount']:,.0f}원")
            amount_item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
            self.sales_table.setItem(row, COL['amount'], amount_item)

            # 결제수단
            pay_item = QTableWidgetItem(sale['pay_method'])
            pay_item.setTextAlignment(Qt.AlignCenter)
            if sale['pay_method'] == '카드':
                pay_item.setForeground(QColor('#1976D2'))
            elif sale['pay_method'] == '현영':
                pay_item.setForeground(QColor('#00897B'))  # 청록 (현금영수증)
                f = QFont()
                f.setBold(True)
                pay_item.setFont(f)
            elif sale['pay_method'] == '현금':
                pay_item.setForeground(QColor('#E65100'))
            elif sale['pay_method']:
                pay_item.setForeground(QColor('#7B1FA2'))
            else:
                pay_item.setText('-')
                pay_item.setForeground(QColor('#BDBDBD'))
            self.sales_table.setItem(row, COL['pay_method'], pay_item)

            # 수납 여부
            paid_item = QTableWidgetItem()
            paid_item.setTextAlignment(Qt.AlignCenter)
            if sale['paid']:
                paid_item.setText('✓')
                paid_item.setForeground(QColor('#4CAF50'))
                f = QFont()
                f.setBold(True)
                paid_item.setFont(f)
            else:
                paid_item.setText('-')
                paid_item.setForeground(QColor('#BDBDBD'))
            self.sales_table.setItem(row, COL['paid'], paid_item)

            # 고객명 (MSSQL POS)
            self.sales_table.setItem(row, COL['customer'],
                QTableWidgetItem(sale['customer']))

            # 품목수 (중앙 정렬)
            count_item = QTableWidgetItem(str(sale['item_count']))
            count_item.setTextAlignment(Qt.AlignCenter)
            self.sales_table.setItem(row, COL['item_count'], count_item)

            # 적립자 (SQLite 마일리지)
            claimed_name_item = QTableWidgetItem(sale['claimed_name'])
            if sale['claimed_name']:
                claimed_name_item.setForeground(CLAIMED_COLOR)
                claimed_name_item.setFont(make_claimed_font())
                claimed_name_item.setToolTip('클릭하여 회원 마일리지 내역 보기')
            self.sales_table.setItem(row, COL['claimed_name'], claimed_name_item)

            # 전화번호 (SQLite 마일리지)
            claimed_phone_item = QTableWidgetItem(sale['claimed_phone'])
            if sale['claimed_phone']:
                claimed_phone_item.setForeground(CLAIMED_COLOR)
                claimed_phone_item.setFont(make_claimed_font())
                claimed_phone_item.setToolTip('클릭하여 회원 마일리지 내역 보기')
            self.sales_table.setItem(row, COL['claimed_phone'], claimed_phone_item)

            # 적립포인트 (SQLite 마일리지)
            points_text = f"{sale['claimed_points']:,}P" if sale['claimed_points'] > 0 else ""
            claimed_points_item = QTableWidgetItem(points_text)
            if sale['claimed_points'] > 0:
                claimed_points_item.setForeground(CLAIMED_COLOR)
                claimed_points_item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                claimed_points_item.setFont(make_claimed_font())
                claimed_points_item.setToolTip('클릭하여 회원 마일리지 내역 보기')
            self.sales_table.setItem(row, COL['claimed_points'], claimed_points_item)

            # QR 발행 여부
            qr_status_item = QTableWidgetItem()
            qr_status_item.setTextAlignment(Qt.AlignCenter)
            if sale['qr_issued']:
                qr_status_item.setText('✓')
                qr_status_item.setForeground(CLAIMED_COLOR)
                f = QFont()
                f.setBold(True)
                f.setPointSize(14)
                qr_status_item.setFont(f)
                qr_status_item.setToolTip('QR 발행 완료')
            else:
                qr_status_item.setText('-')
                qr_status_item.setForeground(QColor('#BDBDBD'))
                qr_status_item.setToolTip('QR 미발행')
            self.sales_table.setItem(row, COL['qr_issued'], qr_status_item)

    def on_query_error(self, error_msg):
        """DB 조회 에러 처리"""
        QMessageBox.critical(self, '오류', f'조회 실패:\n{error_msg}')
        self.status_label.setText('오류 발생')
        self.status_label.setStyleSheet('color: red; font-size: 12px; padding: 5px;')
        self.refresh_btn.setEnabled(True)

    def show_sale_detail(self):
        """선택된 판매 건의 상세 조회"""
        current_row = self.sales_table.currentRow()
        if current_row < 0:
            return

        order_no = self.sales_table.item(current_row, 0).text()
        detail_dialog = SaleDetailDialog(order_no, self)
        detail_dialog.exec_()

    def on_cell_clicked(self, row, column):
        """테이블 셀 클릭 이벤트 - 적립 사용자 클릭 시 마일리지 내역 표시"""
        # SALES_COLUMNS 기반 인덱스 사용
        COL = {key: i for i, (_, _, key) in enumerate(self.SALES_COLUMNS)}
        mileage_cols = [COL['claimed_name'], COL['claimed_phone'], COL['claimed_points']]
        if column not in mileage_cols:
            return

        # 전화번호 가져오기
        phone_item = self.sales_table.item(row, COL['claimed_phone'])
        if not phone_item or not phone_item.text():
            # 적립 사용자가 없는 경우
            return

        phone = phone_item.text()

        # 회원 마일리지 내역 Dialog 표시
        mileage_dialog = UserMileageDialog(phone, self)
        mileage_dialog.exec_()

    def toggle_printer_mode(self):
        """프린터 모드 토글"""
        is_zebra = self.printer_toggle.isChecked()

        if is_zebra:
            # Zebra 모드
            self.printer_toggle.setText('🖨️ Zebra 라벨')
            self.pos_settings_btn.setVisible(False)
            self.preview_checkbox.setEnabled(True)  # 미리보기 활성화
        else:
            # POS 모드
            self.printer_toggle.setText('🖨️ POS 영수증')
            self.pos_settings_btn.setVisible(True)
            self.preview_checkbox.setChecked(False)  # 미리보기 비활성화
            self.preview_checkbox.setEnabled(False)  # ESC/POS는 미리보기 불가

            # POS 설정 확인
            if not self.check_pos_printer_config():
                QMessageBox.warning(
                    self, '설정 필요',
                    'POS 프린터 설정이 필요합니다.\n'
                    '[POS 설정] 버튼을 클릭하여 IP/포트를 입력하세요.'
                )

    def open_pos_settings(self):
        """POS 프린터 설정 다이얼로그 열기"""
        from pos_thermal import POSSettingsDialog

        dialog = POSSettingsDialog(self)
        if dialog.exec_() == QDialog.Accepted:
            self.status_label.setText('POS 프린터 설정이 저장되었습니다.')

    def check_pos_printer_config(self):
        """POS 프린터 설정 확인"""
        config_path = os.path.join(os.path.dirname(__file__), '..', 'config.json')

        if not os.path.exists(config_path):
            return False

        try:
            with open(config_path, 'r', encoding='utf-8') as f:
                config = json.load(f)

            pos_config = config.get('pos_printer', {})
            ip = pos_config.get('ip')

            return bool(ip)
        except:
            return False

    def trigger_kiosk_claim(self):
        """선택된 판매 건을 키오스크에 표시"""
        current_row = self.sales_table.currentRow()
        if current_row < 0:
            QMessageBox.warning(self, '경고', '거래를 선택해주세요.')
            return

        order_no = self.sales_table.item(current_row, 0).text()
        amount_text = self.sales_table.item(current_row, 2).text()
        amount = float(amount_text.replace(',', '').replace('원', ''))

        try:
            import requests as req
            resp = req.post(
                'http://localhost:7001/api/kiosk/trigger',
                json={'transaction_id': order_no, 'amount': amount},
                timeout=5
            )
            result = resp.json()

            if result.get('success'):
                self.status_label.setText(f'키오스크 적립 대기 중 ({result.get("points", 0)}P)')
                self.status_label.setStyleSheet(
                    'color: #6366f1; font-size: 12px; padding: 5px; font-weight: bold;')
            else:
                QMessageBox.warning(self, '키오스크', result.get('message', '전송 실패'))
        except Exception as e:
            QMessageBox.critical(self, '오류', f'Flask 서버 연결 실패:\n{str(e)}')

    def generate_qr_label(self):
        """선택된 판매 건에 대해 QR 라벨 생성"""
        # 선택된 행 확인
        current_row = self.sales_table.currentRow()
        if current_row < 0:
            QMessageBox.warning(self, '경고', '거래를 선택해주세요.')
            return

        # 거래 정보 가져오기
        order_no = self.sales_table.item(current_row, 0).text()
        amount_text = self.sales_table.item(current_row, 2).text()

        # 금액 파싱 (예: "50,000원" → 50000.0)
        amount = float(amount_text.replace(',', '').replace('원', ''))

        # sales_data에서 거래 시간 찾기
        sale = next((s for s in self.sales_data if s['order_no'] == order_no), None)
        if not sale:
            QMessageBox.warning(self, '오류', '거래 정보를 찾을 수 없습니다.')
            return

        # 거래 시간 파싱
        date_str = self.date_edit.date().toString('yyyy-MM-dd')
        time_str = sale['time']
        transaction_time = datetime.strptime(f"{date_str} {time_str}", '%Y-%m-%d %H:%M')

        # 중복 방지 확인 (이미 QR 생성된 거래인지)
        if self.qr_thread and self.qr_thread.isRunning():
            QMessageBox.warning(self, '경고', 'QR 생성 중입니다. 잠시만 기다려주세요.')
            return

        # 미리보기 모드 확인
        preview_mode = self.preview_checkbox.isChecked()

        # 프린터 모드 확인
        is_zebra = self.printer_toggle.isChecked()
        printer_mode = 'zebra' if is_zebra else 'pos'

        # POS 모드일 때 설정 로드
        pos_config = None
        if printer_mode == 'pos':
            config_path = os.path.join(os.path.dirname(__file__), '..', 'config.json')
            try:
                with open(config_path, 'r', encoding='utf-8') as f:
                    config = json.load(f)
                pos_config = config.get('pos_printer', {})

                if not pos_config.get('ip'):
                    QMessageBox.warning(self, '설정 오류', 'POS 프린터 IP가 설정되지 않았습니다.\n[POS 설정] 버튼을 클릭하세요.')
                    return
            except Exception as e:
                QMessageBox.warning(self, '설정 오류', f'POS 프린터 설정을 불러올 수 없습니다.\n{str(e)}')
                return

        # QR 생성 스레드 시작
        self.qr_thread = QRGeneratorThread(
            order_no,
            amount,
            transaction_time,
            preview_mode,
            printer_mode,
            pos_config
        )
        self.qr_thread.qr_complete.connect(self.on_qr_generated)
        self.qr_thread.start()

        # 상태 표시
        printer_name = 'Zebra 라벨' if printer_mode == 'zebra' else 'POS 영수증'
        self.status_label.setText(f'QR 생성 중... ({order_no}) - {printer_name}')
        self.status_label.setStyleSheet('color: orange; font-size: 12px; padding: 5px;')
        self.qr_btn.setEnabled(False)

    def on_qr_generated(self, success, message, image_path):
        """QR 생성 완료 시그널 핸들러"""
        # 버튼 재활성화
        self.qr_btn.setEnabled(True)

        if success:
            # 성공
            self.status_label.setText(f'✓ {message}')
            self.status_label.setStyleSheet('color: green; font-size: 12px; padding: 5px;')

            # QR 생성 성공 시 즉시 테이블 새로고침 (QR 발행 상태 실시간 반영)
            self.refresh_sales()

            # 미리보기 모드면 Dialog 표시
            if image_path:
                order_no = self.sales_table.item(self.sales_table.currentRow(), 0).text()
                preview_dialog = QRLabelPreviewDialog(image_path, order_no, self)
                preview_dialog.show()
            else:
                # 실제 인쇄 완료
                QMessageBox.information(self, '완료', f'{message}\n프린터: 192.168.0.168')
        else:
            # 실패
            self.status_label.setText('QR 생성 실패')
            self.status_label.setStyleSheet('color: red; font-size: 12px; padding: 5px;')
            QMessageBox.critical(self, '오류', f'QR 생성 실패:\n{message}')

    # --- 설정 저장/로드 ---
    def _load_settings(self):
        try:
            with open(self.CONFIG_FILE, 'r', encoding='utf-8') as f:
                return json.load(f)
        except (FileNotFoundError, json.JSONDecodeError):
            return {}

    def _save_settings(self):
        try:
            with open(self.CONFIG_FILE, 'w', encoding='utf-8') as f:
                json.dump(self._gui_settings, f, ensure_ascii=False, indent=2)
        except Exception:
            pass

    def _on_column_resized(self, index, old_size, new_size):
        widths = [self.sales_table.columnWidth(i) for i in range(self.sales_table.columnCount())]
        self._gui_settings['sales_column_widths'] = widths

    def closeEvent(self, event):
        """종료 시 정리 + 설정 저장"""
        # 컬럼 폭 최종 저장
        if hasattr(self, 'sales_table'):
            widths = [self.sales_table.columnWidth(i) for i in range(self.sales_table.columnCount())]
            self._gui_settings['sales_column_widths'] = widths
        # 윈도우 위치/크기 저장
        geo = self.geometry()
        self._gui_settings['window_geometry'] = [geo.x(), geo.y(), geo.width(), geo.height()]
        self._save_settings()

        # 자동 새로고침 타이머 중지
        if hasattr(self, 'refresh_timer'):
            self.refresh_timer.stop()

        if self.sales_thread and self.sales_thread.isRunning():
            self.sales_thread.wait()
        if self.qr_thread and self.qr_thread.isRunning():  # QR 스레드 추가
            self.qr_thread.wait()
        self.db_manager.close_all()
        event.accept()


if __name__ == '__main__':
    app = QApplication(sys.argv)

    # 한글 폰트 설정
    font = QFont('맑은 고딕', 10)
    app.setFont(font)

    window = POSSalesGUI()
    window.show()

    sys.exit(app.exec_())
