import sys
import os
import subprocess
import pandas as pd
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Border, Side, Alignment
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QPushButton, QLineEdit,
                            QHBoxLayout, QComboBox, QLabel, QTreeWidget, QTreeWidgetItem, QStyle,
                            QFrame, QSplitter, QStatusBar, QGraphicsDropShadowEffect, QHeaderView,
                            QStyleFactory, QSizePolicy, QSpacerItem, QScrollArea, QFileDialog)
from PyQt5.QtCore import Qt, QPropertyAnimation, QEasingCurve, QRect, QSize
from PyQt5.QtGui import QColor, QPalette, QFont, QIcon, QLinearGradient, QBrush, QPixmap, QKeySequence
from PyQt5.QtWidgets import QShortcut  # QShortcut 추가

# 스타일 관리 클래스
class StyleManager:
    DARK_THEME = {
        "BG": "#1E2526",
        "LIGHT_BG": "#2A3439",
        "ACCENT": "#D4AF37",
        "TEXT": "#E8ECEF",
        "MEDIUM_GRAY": "#A9B1B3",
        "DARK_ACCENT": "#3A4A50"
    }
    
    LIGHT_THEME = {
        "BG": "#F5F6F5",
        "LIGHT_BG": "#FFFFFF",
        "ACCENT": "#F4A261",
        "TEXT": "#2A3439",
        "MEDIUM_GRAY": "#6B7280",
        "DARK_ACCENT": "#E5E7EB"
    }
    
    @staticmethod
    def apply_style(app, theme="dark"):
        colors = StyleManager.DARK_THEME if theme == "dark" else StyleManager.LIGHT_THEME
        app.setStyle(QStyleFactory.create("Fusion"))
        font = QFont("Helvetica", 10)
        app.setFont(font)
        palette = QPalette()
        palette.setColor(QPalette.Window, QColor(colors["BG"]))
        palette.setColor(QPalette.WindowText, QColor(colors["TEXT"]))
        palette.setColor(QPalette.Base, QColor(colors["LIGHT_BG"]))
        palette.setColor(QPalette.AlternateBase, QColor(colors["DARK_ACCENT"]))
        palette.setColor(QPalette.ToolTipBase, QColor(colors["LIGHT_BG"]))
        palette.setColor(QPalette.ToolTipText, QColor(colors["TEXT"]))
        palette.setColor(QPalette.Text, QColor(colors["TEXT"]))
        palette.setColor(QPalette.Button, QColor(colors["DARK_ACCENT"]))
        palette.setColor(QPalette.ButtonText, QColor(colors["TEXT"]))
        palette.setColor(QPalette.BrightText, QColor(colors["ACCENT"]))
        palette.setColor(QPalette.Link, QColor(colors["ACCENT"]))
        palette.setColor(QPalette.Highlight, QColor(colors["ACCENT"]))
        palette.setColor(QPalette.HighlightedText, QColor(colors["BG"]))
        app.setPalette(palette)
        
        app.setStyleSheet(f"""
            QMainWindow {{ background: {colors["BG"]}; }}
            QWidget {{ background: {colors["BG"]}; }}
            QLabel {{ color: {colors["TEXT"]}; font-size: 12px; font-weight: 400; padding: 4px; }}
            QLabel#headerLabel {{ font-size: 14px; font-weight: bold; color: {colors["ACCENT"]}; padding: 8px; }}
            QComboBox {{ border: 1px solid {colors["DARK_ACCENT"]}; border-radius: 4px; padding: 4px 8px; font-size: 12px; background: {colors["LIGHT_BG"]}; color: {colors["TEXT"]}; selection-background-color: {colors["ACCENT"]}; selection-color: {colors["BG"]}; }}
            QComboBox::drop-down {{ subcontrol-origin: padding; subcontrol-position: right center; width: 20px; border-left: 1px solid {colors["DARK_ACCENT"]}; border-top-right-radius: 4px; border-bottom-right-radius: 4px; }}
            QComboBox QAbstractItemView {{ font-size: 12px; background: {colors["LIGHT_BG"]}; border: 1px solid {colors["DARK_ACCENT"]}; selection-background-color: {colors["ACCENT"]}; selection-color: {colors["BG"]}; padding: 4px; }}
            QLineEdit {{ border: 1px solid {colors["DARK_ACCENT"]}; border-radius: 4px; padding: 4px 8px; font-size: 12px; background: {colors["LIGHT_BG"]}; color: {colors["TEXT"]}; selection-background-color: {colors["ACCENT"]}; selection-color: {colors["BG"]}; }}
            QLineEdit:read-only {{ background: {colors["DARK_ACCENT"]}; border: 1px solid {colors["DARK_ACCENT"]}; }}
            QPushButton {{ background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 {colors["ACCENT"]}, stop:1 {StyleManager.adjust_brightness(colors["ACCENT"], -20)}); color: {colors["BG"]}; border: none; border-radius: 4px; padding: 6px 12px; font-size: 12px; font-weight: bold; }}
            QPushButton:hover {{ background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 {StyleManager.adjust_brightness(colors["ACCENT"], 20)}, stop:1 {colors["ACCENT"]}); }}
            QPushButton:pressed {{ background-color: {StyleManager.adjust_brightness(colors["ACCENT"], -40)}; }}
            QPushButton:disabled {{ background-color: {colors["MEDIUM_GRAY"]}; color: {colors["DARK_ACCENT"]}; }}
            QTreeWidget {{ border: 1px solid {colors["DARK_ACCENT"]}; border-radius: 4px; font-size: 12px; background: {colors["LIGHT_BG"]}; alternate-background-color: {colors["DARK_ACCENT"]}; selection-background-color: {colors["ACCENT"]}; selection-color: {colors["BG"]}; padding: 4px; }}
            QTreeWidget::item {{ padding: 4px; border-bottom: 1px solid {colors["DARK_ACCENT"]}; }}
            QTreeWidget::item:selected {{ background-color: {colors["ACCENT"]}; color: {colors["BG"]}; }}
            QHeaderView::section {{ background-color: {colors["DARK_ACCENT"]}; color: {colors["TEXT"]}; padding: 4px; border: none; border-right: 1px solid {colors["MEDIUM_GRAY"]}; border-bottom: 1px solid {colors["MEDIUM_GRAY"]}; font-size: 12px; font-weight: bold; }}
            QStatusBar {{ background-color: {colors["DARK_ACCENT"]}; color: {colors["TEXT"]}; font-size: 12px; padding: 4px 8px; }}
            QFrame#separator {{ background-color: {colors["MEDIUM_GRAY"]}; max-height: 1px; margin: 6px 0px; }}
            QToolTip {{ background-color: {colors["LIGHT_BG"]}; color: {colors["TEXT"]}; font-size: 12px; border: 1px solid {colors["DARK_ACCENT"]}; padding: 4px; }}
            CardWidget {{ background: {colors["LIGHT_BG"]}; border-radius: 4px; border: 1px solid {colors["DARK_ACCENT"]}; }}
        """)
    
    @staticmethod
    def adjust_brightness(color_hex, amount):
        color = QColor(color_hex)
        r, g, b = color.red(), color.green(), color.blue()
        r = max(0, min(255, r + amount))
        g = max(0, min(255, g + amount))
        b = max(0, min(255, b + amount))
        return f"#{r:02x}{g:02x}{b:02x}"

class AnimationHelper:
    @staticmethod
    def fade_in(widget, duration=300):
        widget.show()
    
    @staticmethod
    def add_drop_shadow(widget, radius=10, x_offset=2, y_offset=2, color=QColor(0, 0, 0, 80)):
        shadow = QGraphicsDropShadowEffect()
        shadow.setBlurRadius(radius)
        shadow.setXOffset(x_offset)
        shadow.setYOffset(y_offset)
        shadow.setColor(color)
        widget.setGraphicsEffect(shadow)

class ChartTreeWidget(QTreeWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAlternatingRowColors(True)
        self.setAnimated(True)
        self.setHeaderLabels(['씬 이름', 'Frames', 'FEET'])
        self.setSelectionMode(QTreeWidget.ExtendedSelection)
        header = self.header()
        header.setSectionResizeMode(QHeaderView.Interactive)
        header.setSectionsMovable(False)
        header.setStretchLastSection(False)
        self.setColumnWidth(0, 250)
        self.setColumnWidth(1, 80)
        self.setColumnWidth(2, 80)
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        AnimationHelper.add_drop_shadow(self)

class CardWidget(QFrame):
    def __init__(self, title=None, parent=None):
        super().__init__(parent)
        self.setFrameShape(QFrame.StyledPanel)
        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(10, 10, 10, 10)
        self.layout.setSpacing(8)
        if title:
            self.title = QLabel(title)
            self.title.setObjectName("headerLabel")
            self.layout.addWidget(self.title)
            separator = QFrame()
            separator.setFrameShape(QFrame.HLine)
            separator.setFrameShadow(QFrame.Sunken)
            separator.setObjectName("separator")
            self.layout.addWidget(separator)
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        AnimationHelper.add_drop_shadow(self)

class ShowSelectionWidget(CardWidget):
    def __init__(self, parent=None):
        super().__init__("작품 선택", parent)
        self.show_layout = QHBoxLayout()
        self.show_layout.setSpacing(8)
        show_label = QLabel("작품:")
        show_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
        self.show_layout.addWidget(show_label)
        self.show_combo = QComboBox()
        self.show_combo.addItem("작품 선택")
        self.show_combo.addItems(["BB", "GN", "BM", "KOTH", "Test", "Yeson_DANG", "Yeson_Test", "Yeson_Test_4K"])
        self.show_combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        self.show_layout.addWidget(self.show_combo)
        self.show_layout.addStretch()
        self.layout.addLayout(self.show_layout)
        self.season_layout = QHBoxLayout()
        self.season_layout.setSpacing(8)
        season_label = QLabel("시즌:")
        season_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
        self.season_layout.addWidget(season_label)
        self.season_combo = QComboBox()
        self.season_combo.addItem("시즌 선택")
        self.season_combo.setEnabled(False)
        self.season_combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        self.season_layout.addWidget(self.season_combo)
        self.season_layout.addStretch()
        self.layout.addLayout(self.season_layout)
        self.episode_layout = QHBoxLayout()
        self.episode_layout.setSpacing(8)
        episode_label = QLabel("화수:")
        episode_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
        self.episode_layout.addWidget(episode_label)
        self.episode_combo = QComboBox()
        self.episode_combo.addItem("화수 선택")
        self.episode_combo.setEnabled(False)
        self.episode_combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        self.episode_layout.addWidget(self.episode_combo)
        self.episode_layout.addStretch()
        self.layout.addLayout(self.episode_layout)

class SummaryWidget(CardWidget):
    def __init__(self, parent=None):
        super().__init__("에피소드 정보", parent)
        summary_layout = QHBoxLayout()
        summary_layout.setSpacing(10)
        frame_layout = QVBoxLayout()
        frame_title = QLabel("총 프레임 수:")
        frame_title.setAlignment(Qt.AlignCenter)
        frame_layout.addWidget(frame_title)
        self.total_frames_display = QLineEdit()
        self.total_frames_display.setAlignment(Qt.AlignCenter)
        self.total_frames_display.setReadOnly(True)
        self.total_frames_display.setMinimumWidth(80)
        self.total_frames_display.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        frame_layout.addWidget(self.total_frames_display)
        summary_layout.addLayout(frame_layout)
        separator = QFrame()
        separator.setFrameShape(QFrame.VLine)
        separator.setFrameShadow(QFrame.Sunken)
        separator.setObjectName("separator")
        summary_layout.addWidget(separator)
        feet_layout = QVBoxLayout()
        feet_title = QLabel("총 FEET:")
        feet_title.setAlignment(Qt.AlignCenter)
        feet_layout.addWidget(feet_title)
        self.episode_feet_display = QLineEdit()
        self.episode_feet_display.setAlignment(Qt.AlignCenter)
        self.episode_feet_display.setReadOnly(True)
        self.episode_feet_display.setMinimumWidth(80)
        self.episode_feet_display.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        feet_layout.addWidget(self.episode_feet_display)
        summary_layout.addLayout(feet_layout)
        self.layout.addLayout(summary_layout)

class CalculationWidget(CardWidget):
    def __init__(self, parent=None):
        super().__init__("선택 씬 계산", parent)
        calc_layout = QHBoxLayout()
        calc_layout.setSpacing(8)
        self.calc_button = QPushButton("선택 씬 계산")
        self.calc_button.setCursor(Qt.PointingHandCursor)
        self.calc_button.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        calc_layout.addWidget(self.calc_button)
        calc_layout.addSpacing(10)
        feet_label = QLabel("선택된 씬 총 FEET:")
        feet_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
        calc_layout.addWidget(feet_label)
        self.total_feet_display = QLineEdit()
        self.total_feet_display.setAlignment(Qt.AlignCenter)
        self.total_feet_display.setReadOnly(True)
        self.total_feet_display.setMinimumWidth(100)
        self.total_feet_display.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        calc_layout.addWidget(self.total_feet_display)
        calc_layout.addStretch()
        self.layout.addLayout(calc_layout)

class SceneListWidget(CardWidget):
    def __init__(self, parent=None):
        super().__init__("씬 목록", parent)
        self.scene_tree = ChartTreeWidget()
        self.scene_tree.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.layout.addWidget(self.scene_tree)
        button_layout = QHBoxLayout()
        self.export_button = QPushButton("엑셀로 내보내기")
        self.export_button.setCursor(Qt.PointingHandCursor)
        self.export_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
        button_layout.addWidget(self.export_button)
        button_layout.addStretch()
        selection_label = QLabel("Tip: 여러 씬을 선택하려면 Ctrl 또는 Shift 키를 사용하세요.")
        selection_label.setAlignment(Qt.AlignRight)
        selection_label.setStyleSheet("font-style: italic;")
        button_layout.addWidget(selection_label)
        self.layout.addLayout(button_layout)

class ValidationApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("씬 관리 시스템")
        self.setGeometry(100, 100, 900, 700)
        self.setMinimumSize(600, 400)
        
        self.current_theme = "dark"  # 기본 테마 설정
        app = QApplication.instance()
        StyleManager.apply_style(app, self.current_theme)
        
        self.path_manager = PathManager()
        self.calculator = FrameCalculator()
        self.db_manager = SceneDbManager(self.path_manager)
        
        self.setup_ui()
        
        self.connect_signals()
        
        self.statusBar = QStatusBar()
        self.setStatusBar(self.statusBar)
        self.statusBar.showMessage("애플리케이션이 준비되었습니다")
        
        # '1' 키 단축키 설정
        self.theme_shortcut = QShortcut(QKeySequence("1"), self)
        self.theme_shortcut.activated.connect(self.toggle_theme)

    def setup_ui(self):
        self.main_widget = QWidget()
        self.scroll_area = QScrollArea()
        self.scroll_area.setWidget(self.main_widget)
        self.scroll_area.setWidgetResizable(True)
        self.setCentralWidget(self.scroll_area)
        
        self.main_layout = QVBoxLayout()
        self.main_layout.setSpacing(10)
        self.main_layout.setContentsMargins(15, 15, 15, 15)
        
        header_layout = QHBoxLayout()
        self.app_title = QLabel("씬 피트 계산")
        self.app_title.setStyleSheet("font-size: 16px; font-weight: bold; padding: 8px;")
        header_layout.addWidget(self.app_title)
        header_layout.addStretch()
        
        # 테마 전환 버튼 추가 (단축키는 별도로 설정)
        self.theme_button = QPushButton("밝은 테마로 전환" if self.current_theme == "dark" else "다크 테마로 전환")
        self.theme_button.setCursor(Qt.PointingHandCursor)
        self.theme_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
        self.theme_button.clicked.connect(self.toggle_theme)
        header_layout.addWidget(self.theme_button)
        
        self.main_layout.addLayout(header_layout)
        
        self.show_selection = ShowSelectionWidget()
        info_layout = QHBoxLayout()
        info_layout.setSpacing(10)
        self.summary = SummaryWidget()
        self.calculation = CalculationWidget()
        info_layout.addWidget(self.summary, 1)
        info_layout.addWidget(self.calculation, 1)
        self.scene_list_widget = SceneListWidget()
        
        self.main_layout.addWidget(self.show_selection)
        self.main_layout.addLayout(info_layout)
        self.main_layout.addWidget(self.scene_list_widget, 1)
        
        self.main_widget.setLayout(self.main_layout)
    
    def toggle_theme(self):
        """테마를 전환하는 메서드"""
        # 현재 테마를 반대로 전환
        self.current_theme = "light" if self.current_theme == "dark" else "dark"
        self.theme_button.setText("밝은 테마로 전환" if self.current_theme == "dark" else "다크 테마로 전환")
        StyleManager.apply_style(QApplication.instance(), self.current_theme)
        self.statusBar.showMessage(f"{self.current_theme.capitalize()} 테마로 전환되었습니다.")
    
    def connect_signals(self):
        self.show_selection.show_combo.currentTextChanged.connect(self.on_show_changed)
        self.show_selection.season_combo.currentTextChanged.connect(self.on_season_changed)
        self.show_selection.episode_combo.currentTextChanged.connect(self.on_episode_changed)
        self.calculation.calc_button.clicked.connect(self.calculate_selected_scenes)
        self.scene_list_widget.export_button.clicked.connect(self.export_to_excel)
    
    def on_show_changed(self, show):
        self.scene_list_widget.scene_tree.clear()
        self.show_selection.season_combo.clear()
        self.show_selection.episode_combo.clear()
        self.show_selection.season_combo.addItem("시즌 선택")
        self.show_selection.episode_combo.addItem("화수 선택")
        if show == "작품 선택":
            self.show_selection.season_combo.setEnabled(False)
            self.show_selection.episode_combo.setEnabled(False)
            self.statusBar.showMessage("작품을 선택해주세요")
            return
        project_path = self.path_manager.get_show_path(show)
        if not project_path:
            self.statusBar.showMessage(f"{show} 작품의 경로를 찾을 수 없습니다")
            return
        if show == "Yeson_DANG":
            self.show_selection.season_combo.setEnabled(False)
            self.show_selection.episode_combo.setEnabled(False)
            self.load_scene_folders(project_path)
            self.statusBar.showMessage(f"{show} 작품이 선택되었습니다")
            return
        self.show_selection.season_combo.setEnabled(True)
        if show in ["BB", "GN", "BM", "KOTH", "Test"]:
            seasons = self.path_manager.show_paths[show]["seasons"]
        elif show in ["Yeson_Test", "Yeson_Test_4K"]:
            try:
                seasons = [item for item in os.listdir(project_path) 
                          if os.path.isdir(os.path.join(project_path, item)) 
                          and not item.startswith('.') and item != "DANG"]
                seasons.sort()
            except Exception as e:
                print(f"시즌 폴더 검색 중 오류: {e}")
                seasons = []
        self.show_selection.season_combo.addItems(seasons)
        self.show_selection.episode_combo.setEnabled(show not in ["Test", "Yeson_DANG", "Yeson_Test", "Yeson_Test_4K"])
        self.statusBar.showMessage(f"{show} 작품이 선택되었습니다. 시즌을 선택해주세요.")
    
    def on_season_changed(self, season):
        self.scene_list_widget.scene_tree.clear()
        if season == "시즌 선택":
            self.show_selection.episode_combo.clear()
            self.show_selection.episode_combo.addItem("화수 선택")
            self.show_selection.episode_combo.setEnabled(False)
            return
        show = self.show_selection.show_combo.currentText()
        project_path = self.path_manager.get_show_path(show)
        if not project_path:
            return
        if show in ["Test", "Yeson_Test", "Yeson_Test_4K"]:
            season_path = os.path.join(project_path, season)
            self.load_scene_folders(season_path)
            self.show_selection.episode_combo.setEnabled(False)
            self.statusBar.showMessage(f"{show} - {season} 시즌이 선택되었습니다.")
            return
        self.show_selection.episode_combo.clear()
        self.show_selection.episode_combo.addItem("화수 선택")
        self.show_selection.episode_combo.setEnabled(True)
        self.update_episode_list(show, season, project_path)
        self.statusBar.showMessage(f"{show} - {season} 시즌이 선택되었습니다. 화수를 선택해주세요.")
    
    def on_episode_changed(self, episode):
        self.scene_list_widget.scene_tree.clear()
        if episode == "화수 선택":
            return
        show = self.show_selection.show_combo.currentText()
        season = self.show_selection.season_combo.currentText()
        project_path = self.path_manager.get_show_path(show)
        if not project_path:
            return
        episode_path = os.path.join(project_path, season, episode)
        self.load_scene_folders(episode_path)
        self.update_scene_list()
        self.show_selection.show_combo.repaint()
        self.show_selection.season_combo.repaint()
        self.show_selection.episode_combo.repaint()
        self.statusBar.showMessage(f"{show} - {season} - {episode} 화수가 로드되었습니다.")
    
    def load_scene_folders(self, path):
        if not os.path.exists(path):
            self.statusBar.showMessage(f"경로를 찾을 수 없습니다: {path}")
            return
        scene_folders = []
        for item in os.listdir(path):
            if os.path.isdir(os.path.join(path, item)) and item.startswith('scene-'):
                scene_folders.append(item)
        for scene in sorted(scene_folders):
            item = QTreeWidgetItem([scene])
            self.scene_list_widget.scene_tree.addTopLevelItem(item)
        if scene_folders:
            self.statusBar.showMessage(f"{len(scene_folders)}개의 씬 폴더를 찾았습니다.")
        else:
            self.statusBar.showMessage("씬 폴더를 찾을 수 없습니다.")
    
    def update_episode_list(self, show, season, project_path):
        season_path = os.path.join(project_path, season)
        if not os.path.exists(season_path):
            self.statusBar.showMessage(f"시즌 경로를 찾을 수 없습니다: {season_path}")
            return
        episodes = []
        if show == "BB":
            for item in os.listdir(season_path):
                if "Season13" in season and "DASA" in item:
                    episodes.append(item)
                elif "Season14" in season and "EASA" in item:
                    episodes.append(item)
        elif show == "GN":
            if "GN_Season" in season:
                season_num = season.replace("GN_Season", "")
                for item in os.listdir(season_path):
                    if item.startswith(f"{season_num}LBW"):
                        episodes.append(item)
        elif show == "BM":
            for item in os.listdir(season_path):
                if item.startswith("BM_8"):
                    episodes.append(item)
        elif show == "KOTH":
            for item in os.listdir(season_path):
                if "Season14" in season and item.startswith("EABE"):
                    episodes.append(item)
                elif "Season15" in season and item.startswith("15"):
                    episodes.append(item)
        self.show_selection.episode_combo.addItems(sorted(episodes))
        if episodes:
            self.statusBar.showMessage(f"{len(episodes)}개의 에피소드를 찾았습니다.")
        else:
            self.statusBar.showMessage("에피소드를 찾을 수 없습니다.")
    
    def update_scene_list(self):
        show = self.show_selection.show_combo.currentText()
        episode = self.show_selection.episode_combo.currentText()
        season = self.show_selection.season_combo.currentText()
        if show in ["Test", "Yeson_DANG", "Yeson_Test", "Yeson_Test_4K"]:
            identifier = season if show in ["Test", "Yeson_Test", "Yeson_Test_4K"] else show
            frames_info = self.db_manager.get_frames_info(identifier)
        else:
            frames_info = self.db_manager.get_frames_info(episode)
        if not frames_info:
            self.statusBar.showMessage("프레임 정보를 가져올 수 없습니다.")
            return
        latest_scenes = self.db_manager.filter_scene_folders(frames_info)
        total_frames = sum(int(frames_info[scene]) for scene in latest_scenes.values())
        total_feet = self.calculator.calculate_sheet_length(total_frames)
        if show in ["Test", "Yeson_DANG", "Yeson_Test", "Yeson_Test_4K"]:
            self.update_simple_scene_list(latest_scenes, frames_info)
        else:
            self.update_sequence_scene_list(latest_scenes, frames_info, show)
        self.summary.total_frames_display.setText(str(total_frames))
        self.summary.episode_feet_display.setText(self.calculator.format_feet_display(total_feet))
        self.statusBar.showMessage(f"씬 목록이 업데이트되었습니다. 총 프레임: {total_frames}, 총 FEET: {self.calculator.format_feet_display(total_feet)}")
    
    def update_simple_scene_list(self, latest_scenes, frames_info):
        items = []
        for scene_name in sorted(latest_scenes.values()):
            frames = frames_info[scene_name]
            sheet_length = self.calculator.calculate_sheet_length(frames)
            feet_display = self.calculator.format_feet_display(sheet_length)
            item = QTreeWidgetItem([scene_name, str(frames), feet_display])
            items.append(item)
        self.scene_list_widget.scene_tree.clear()
        self.scene_list_widget.scene_tree.addTopLevelItems(items)
    
    def update_sequence_scene_list(self, latest_scenes, frames_info, show):
        items = []
        for scene_name in sorted(latest_scenes.values()):
            if show == "BM":
                scene_num = scene_name.replace('scene-', '')
                seq = scene_num[0]
            elif show == "KOTH":
                scene_num = scene_name.replace('scene-', '')
                try:
                    scene_number = int(scene_num[:3])
                    if scene_number < 300:
                        seq = "Act1"
                    elif scene_number < 600:
                        seq = "Act2"
                    else:
                        seq = "Act3"
                except ValueError:
                    seq = "-"
            else:
                seq = scene_name.split('_')[0]
            frames = frames_info[scene_name]
            sheet_length = self.calculator.calculate_sheet_length(frames)
            feet_display = self.calculator.format_feet_display(sheet_length)
            item = QTreeWidgetItem([scene_name, str(frames), feet_display])
            items.append(item)
        self.scene_list_widget.scene_tree.clear()
        self.scene_list_widget.scene_tree.addTopLevelItems(items)
    
    def calculate_selected_scenes(self):
        selected_items = self.scene_list_widget.scene_tree.selectedItems()
        if not selected_items:
            self.calculation.total_feet_display.setText("0F 00f")
            self.statusBar.showMessage("선택된 씬이 없습니다.")
            return
        total_frames = sum(int(item.text(1)) for item in selected_items)
        total_feet = self.calculator.calculate_sheet_length(total_frames)
        feet_display = self.calculator.format_feet_display(total_feet)
        self.calculation.total_feet_display.setText(feet_display)
        self.statusBar.showMessage(f"{len(selected_items)}개 씬이 선택되었습니다. 총 프레임: {total_frames}, 총 FEET: {feet_display}")
    
    def export_to_excel(self):
        tree = self.scene_list_widget.scene_tree
        if tree.topLevelItemCount() == 0:
            self.statusBar.showMessage("내보낼 씬 목록이 없습니다.")
            return
        
        show = self.show_selection.show_combo.currentText()
        season = self.show_selection.season_combo.currentText()
        episode = self.show_selection.episode_combo.currentText()
        total_frames = self.summary.total_frames_display.text()
        total_feet = self.summary.episode_feet_display.text()
        
        scene_data = []
        for index in range(tree.topLevelItemCount()):
            item = tree.topLevelItem(index)
            scene_data.append([item.text(0), item.text(1), item.text(2)])
        
        default_filename = f"{show}_{season}_{episode}_scenes.xlsx" if show != "작품 선택" else "scenes.xlsx"
        file_path, _ = QFileDialog.getSaveFileName(self, "엑셀 파일로 저장", default_filename, "Excel Files (*.xlsx)")
        if not file_path:
            self.statusBar.showMessage("파일 저장이 취소되었습니다.")
            return
        
        try:
            wb = Workbook()
            ws = wb.active
            ws.title = "SceneList"
            
            # 현재 테마에 맞는 색상 가져오기
            colors = StyleManager.DARK_THEME if self.current_theme == "dark" else StyleManager.LIGHT_THEME
            
            # 스타일 정의
            header_font = Font(name="Helvetica", size=14, bold=True, color=colors["ACCENT"][1:])
            subheader_font = Font(name="Helvetica", size=12, bold=True, color=colors["TEXT"][1:])
            text_font = Font(name="Helvetica", size=12, color=colors["TEXT"][1:])
            border = Border(left=Side(style="thin", color=colors["DARK_ACCENT"][1:]),
                            right=Side(style="thin", color=colors["DARK_ACCENT"][1:]),
                            top=Side(style="thin", color=colors["DARK_ACCENT"][1:]),
                            bottom=Side(style="thin", color=colors["DARK_ACCENT"][1:]))
            fill_bg = PatternFill(start_color=colors["BG"][1:], end_color=colors["BG"][1:], fill_type="solid")
            fill_light_bg = PatternFill(start_color=colors["LIGHT_BG"][1:], end_color=colors["LIGHT_BG"][1:], fill_type="solid")
            fill_header = PatternFill(start_color=colors["DARK_ACCENT"][1:], end_color=colors["DARK_ACCENT"][1:], fill_type="solid")
            fill_accent = PatternFill(start_color=colors["ACCENT"][1:], end_color=colors["ACCENT"][1:], fill_type="solid")
            center_align = Alignment(horizontal="center", vertical="center")
            
            # 전체 배경 초기화
            for row in ws.iter_rows(min_row=1, max_row=100, min_col=1, max_col=10):
                for cell in row:
                    cell.fill = fill_bg
            
            # 1. 작품 선택 섹션 (ShowSelectionWidget)
            ws["A1"] = "작품 선택"
            ws["A1"].font = header_font
            ws["A1"].fill = fill_light_bg
            ws["A1"].border = border
            ws["A1"].alignment = center_align
            ws.merge_cells("A1:C1")
            
            ws["A2"] = "작품"
            ws["B2"] = show
            ws["A3"] = "시즌"
            ws["B3"] = season
            ws["A4"] = "화수"
            ws["B4"] = episode
            for row in ws["A2:C4"]:
                for cell in row:
                    cell.font = text_font
                    cell.fill = fill_light_bg
                    cell.border = border
                    cell.alignment = center_align
            ws["C2"] = "선택 정보"  # 추가적인 UI 느낌
            ws["C2"].fill = fill_header
            
            # 2. 에피소드 정보 섹션 (SummaryWidget)
            ws["A6"] = "에피소드 정보"
            ws["A6"].font = header_font
            ws["A6"].fill = fill_light_bg
            ws["A6"].border = border
            ws["A6"].alignment = center_align
            ws.merge_cells("A6:C6")
            
            ws["A7"] = "총 프레임 수"
            ws["B7"] = total_frames
            ws["A8"] = "총 FEET"
            ws["B8"] = total_feet
            for row in ws["A7:B8"]:
                for cell in row:
                    cell.font = text_font
                    cell.fill = fill_light_bg
                    cell.border = border
                    cell.alignment = center_align
            ws["C7"] = "요약"  # UI와 유사한 느낌 추가
            ws["C7"].fill = fill_header
            ws["C8"] = ""
            ws["C8"].fill = fill_header
            
            # 3. 씬 목록 섹션 (SceneListWidget)
            ws["A10"] = "씬 목록"
            ws["A10"].font = header_font
            ws["A10"].fill = fill_light_bg
            ws["A10"].border = border
            ws["A10"].alignment = center_align
            ws.merge_cells("A10:C10")
            
            headers = ["씬 이름", "Frames", "FEET"]
            for col, header in enumerate(headers, start=1):
                cell = ws.cell(row=11, column=col, value=header)
                cell.font = subheader_font
                cell.fill = fill_header
                cell.border = border
                cell.alignment = center_align
            
            for row_idx, scene in enumerate(scene_data, start=12):
                for col_idx, value in enumerate(scene, start=1):
                    cell = ws.cell(row=row_idx, column=col_idx, value=value)
                    cell.font = text_font
                    cell.fill = fill_light_bg
                    cell.border = border
                    cell.alignment = center_align
            
            # 열 너비 조정 (UI와 비슷한 비율)
            ws.column_dimensions["A"].width = 40  # 씬 이름 열을 넓게
            ws.column_dimensions["B"].width = 15
            ws.column_dimensions["C"].width = 15
            
            # 추가적인 UI 스타일링: 섹션 간 구분선 느낌
            ws["A5"].fill = fill_accent
            ws["B5"].fill = fill_accent
            ws["C5"].fill = fill_accent
            ws["A9"].fill = fill_accent
            ws["B9"].fill = fill_accent
            ws["C9"].fill = fill_accent
            
            # 파일 저장
            wb.save(file_path)
            self.statusBar.showMessage(f"씬 목록이 {file_path}에 저장되었습니다.")
        except Exception as e:
            self.statusBar.showMessage(f"엑셀 파일 저장 중 오류 발생: {e}")

class PathManager:
    def __init__(self):
        self.show_paths = {
            "BB": {"base": "Bento_Project", "seasons": ["BB_Season13", "BB_Season14"]},
            "GN": {"base": "Bento_Project2/Great_North", "seasons": ["GN_Season4", "GN_Season5"]},
            "BM": {"base": "Titmouse/Big_Mouth", "seasons": ["BM_Season8"]},
            "KOTH": {"base": "Disney/KOTH", "seasons": ["KOTH_Season14", "KOTH_Season15"]},
            "Test": {"base": "Test", "seasons": ["TEST_SYSTEM"]},
            "Yeson_DANG": {"base": "Yeson_Test/DANG", "seasons": []},
            "Yeson_Test": {"base": "Yeson_Test", "seasons": []},
            "Yeson_Test_4K": {"base": "Yeson_Test_4K", "seasons": []}
        }
        self.possible_paths = [
            "/usadata2", "/usadata3", "/System/Volumes/Data/mnt/usadata2", 
            "/System/Volumes/Data/mnt/usadata3", "/System/Volumes/data/mnt/usadata2", 
            "/System/Volumes/data/mnt/usadata3", "/System/Volumes/Data/System/Volumes/Data/mnt/usadata2", 
            "/System/Volumes/Data/System/Volumes/Data/mnt/usadata3", 
            "/System/Volumes/data/System/Volumes/data/mnt/usadata2", 
            "/System/Volumes/data/System/Volumes/data/mnt/usadata3"
        ]
        self.db_paths = [
            "/USA_DB/db_jobs", "/System/Volumes/Data/mnt/USA_DB/db_jobs", 
            "/System/Volumes/data/mnt/USA_DB/db_jobs", 
            "/System/Volumes/Data/System/Volumes/Data/mnt/USA_DB/db_jobs", 
            "/System/Volumes/data/System/Volumes/data/mnt/USA_DB/db_jobs"
        ]
    
    def get_show_path(self, show):
        if show == "작품 선택":
            return None
        for base_path in self.possible_paths:
            if not os.path.exists(base_path):
                continue
            if show == "BB":
                project_path = os.path.join(base_path, "Bento_Project")
            elif show == "GN":
                project_path = os.path.join(base_path, "Bento_Project2", "Great_North")
            elif show == "BM":
                project_path = os.path.join(base_path, "Titmouse", "Big_Mouth")
            elif show == "KOTH":
                project_path = os.path.join(base_path, "Disney", "KOTH")
            elif show == "Test":
                project_path = os.path.join(base_path, "Test")
            elif show == "Yeson_DANG":
                if "usadata3" not in base_path:
                    continue
                project_path = os.path.join(base_path, "Yeson_Test", "DANG")
            elif show in ["Yeson_Test", "Yeson_Test_4K"]:
                if "usadata3" not in base_path:
                    continue
                project_path = os.path.join(base_path, show)
            if os.path.exists(project_path):
                return project_path
        return None
    
    def get_possible_db_paths(self, episode):
        return [os.path.join(base_path, episode, "scene.db") for base_path in self.db_paths]

class FrameCalculator:
    @staticmethod
    def calculate_sheet_length(frames):
        try:
            frames = int(frames)
            integer_part = frames // 16
            decimal_part = frames % 16
            return f"{integer_part}.{decimal_part:02d}"
        except:
            return "0.00"
    
    @staticmethod
    def format_feet_display(sheet_length):
        try:
            feet, frames = str(sheet_length).split('.')
            return f"{feet}F {frames}f"
        except:
            return "0F 00f"

class SceneDbManager:
    def __init__(self, path_manager):
        self.path_manager = path_manager
        self.cache = {}
    
    def get_frames_info(self, episode):
        if episode in self.cache:
            return self.cache[episode]
        frames_info = {}
        if not episode or episode in ["작품 선택", "시즌 선택", "화수 선택"]:
            return frames_info
        is_valid_episode = (
            episode.startswith(('DASA', 'EASA')) or
            episode.startswith(('4LBW', '5LBW')) or
            episode.startswith('BM_8') or
            episode.startswith(('EABE', '15')) or
            episode in ["Test", "Yeson_DANG", "Yeson_Test", "Yeson_Test_4K"]
        )
        if not is_valid_episode:
            return frames_info
        possible_db_paths = self.path_manager.get_possible_db_paths(episode)
        for db_path in possible_db_paths:
            if not os.path.exists(db_path):
                continue
            try:
                dbu_command = [
                    "/Applications/Toon Boom Harmony 21.1 Premium/Harmony 21.1 Premium.app/Contents/tba/macosx/bin/dbu",
                    "-l", "-r", db_path
                ]
                result = subprocess.run(dbu_command, capture_output=True, text=True)
                if result.returncode != 0:
                    continue
                current_scene = None
                for line in result.stdout.splitlines():
                    if 'Path:' in line:
                        path = line.split('Path:')[1].strip()
                        current_scene = os.path.basename(path)
                    elif 'Frames:' in line and current_scene:
                        frames = line.split('Frames:')[1].strip()
                        frames_info[current_scene] = frames
                if frames_info:
                    self.cache[episode] = frames_info
                    break
            except Exception as e:
                print(f"프레임 정보 추출 중 오류 발생: {e}")
                continue
        return frames_info
    
    @staticmethod
    def filter_scene_folders(frames_info):
        excluded_terms = ['CHARACTERS', 'PROPS', 'Sub_Model', 'STOCK', 'Crowd', '_old', '_batch', 'batch']
        return {
            scene_name: scene_name for scene_name in frames_info.keys()
            if not any(term in scene_name for term in excluded_terms)
        }

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = ValidationApp()
    window.show()
    sys.exit(app.exec_())