"""
VOC 통계 분석 다이얼로그

사용자가 기간을 선택하고 통계 보고서를 생성할 수 있는 UI입니다.
기간 선택, 분석 옵션 설정, 미리보기, 보고서 생성 기능을 제공합니다.

주요 기능:
    - 기간 선택 (시작일, 종료일)
    - 분석 옵션 (부서별, 상태별, 키워드, 차트)
    - 출력 형식 선택 (Excel, PDF)
    - 통계 미리보기
    - 보고서 생성

작성자: KH.Choi
최종 수정: 2026-02-18
버전: 1.0
"""
import customtkinter as ctk
from tkinter import messagebox, filedialog
from datetime import datetime, timedelta
from pathlib import Path
import threading

from models.statistics import StatisticsOptions
from services.statistics_service import StatisticsService
from utils.logger import get_logger


class StatisticsDialog(ctk.CTkToplevel):
    """
    VOC 통계 분석 다이얼로그
    
    사용자가 기간과 옵션을 선택하여 통계 보고서를 생성할 수 있습니다.
    
    Attributes:
        controller: AppController 인스턴스
        logger: 로거 인스턴스
        service: StatisticsService 인스턴스
    
    사용 예시:
        >>> dialog = StatisticsDialog(controller)
    """
    
    def __init__(self, controller):
        """
        다이얼로그 초기화
        
        Args:
            controller: AppController 인스턴스
        """
        super().__init__()
        
        self.controller = controller
        self.logger = get_logger("StatisticsDialog")
        self.service = StatisticsService(controller.db)
        
        # 윈도우 설정
        self.title("VOC 통계 분석")
        self.geometry("600x700")
        self.resizable(False, False)
        
        # 모달 설정
        self.transient(controller.root)
        self.grab_set()
        
        # UI 구성
        self._create_widgets()
        
        # 기본값 설정 (최근 1개월)
        self._set_default_period()
    
    def _create_widgets(self):
        """UI 위젯 생성"""
        # 메인 프레임
        main_frame = ctk.CTkFrame(self)
        main_frame.pack(fill="both", expand=True, padx=20, pady=20)
        
        # 제목
        title_label = ctk.CTkLabel(
            main_frame,
            text="📊 VOC 통계 분석",
            font=("", 18, "bold")
        )
        title_label.pack(pady=(0, 20))
        
        # 1. 기간 선택 섹션
        self._create_period_section(main_frame)
        
        # 2. 분석 옵션 섹션
        self._create_options_section(main_frame)
        
        # 3. 출력 설정 섹션
        self._create_output_section(main_frame)
        
        # 4. 버튼 섹션
        self._create_button_section(main_frame)
    
    def _create_period_section(self, parent):
        """기간 선택 섹션 생성"""
        period_frame = ctk.CTkFrame(parent)
        period_frame.pack(fill="x", pady=(0, 15))
        
        # 섹션 제목
        ctk.CTkLabel(
            period_frame,
            text="📅 분석 기간",
            font=("", 14, "bold")
        ).pack(anchor="w", padx=10, pady=(10, 5))
        
        # 시작일
        start_frame = ctk.CTkFrame(period_frame)
        start_frame.pack(fill="x", padx=10, pady=5)
        
        ctk.CTkLabel(start_frame, text="시작일:", width=80).pack(side="left", padx=(0, 10))
        self.start_entry = ctk.CTkEntry(start_frame, placeholder_text="YYYY-MM-DD")
        self.start_entry.pack(side="left", fill="x", expand=True)
        
        # 종료일
        end_frame = ctk.CTkFrame(period_frame)
        end_frame.pack(fill="x", padx=10, pady=5)
        
        ctk.CTkLabel(end_frame, text="종료일:", width=80).pack(side="left", padx=(0, 10))
        self.end_entry = ctk.CTkEntry(end_frame, placeholder_text="YYYY-MM-DD")
        self.end_entry.pack(side="left", fill="x", expand=True)
        
        # 빠른 선택 버튼
        quick_frame = ctk.CTkFrame(period_frame)
        quick_frame.pack(fill="x", padx=10, pady=(10, 10))
        
        ctk.CTkLabel(quick_frame, text="빠른 선택:").pack(side="left", padx=(0, 10))
        
        ctk.CTkButton(
            quick_frame,
            text="최근 1주일",
            width=80,
            command=lambda: self._set_quick_period(7)
        ).pack(side="left", padx=2)
        
        ctk.CTkButton(
            quick_frame,
            text="최근 1개월",
            width=80,
            command=lambda: self._set_quick_period(30)
        ).pack(side="left", padx=2)
        
        ctk.CTkButton(
            quick_frame,
            text="최근 3개월",
            width=80,
            command=lambda: self._set_quick_period(90)
        ).pack(side="left", padx=2)
    
    def _create_options_section(self, parent):
        """분석 옵션 섹션 생성"""
        options_frame = ctk.CTkFrame(parent)
        options_frame.pack(fill="x", pady=(0, 15))
        
        # 섹션 제목
        ctk.CTkLabel(
            options_frame,
            text="⚙️ 분석 옵션",
            font=("", 14, "bold")
        ).pack(anchor="w", padx=10, pady=(10, 5))
        
        # 체크박스 프레임
        check_frame = ctk.CTkFrame(options_frame)
        check_frame.pack(fill="x", padx=10, pady=(5, 10))
        
        # 부서별 통계
        self.dept_var = ctk.BooleanVar(value=True)
        ctk.CTkCheckBox(
            check_frame,
            text="부서별 통계",
            variable=self.dept_var
        ).pack(anchor="w", pady=3)
        
        # 상태별 통계
        self.status_var = ctk.BooleanVar(value=True)
        ctk.CTkCheckBox(
            check_frame,
            text="상태별 통계",
            variable=self.status_var
        ).pack(anchor="w", pady=3)
        
        # 키워드 분석
        self.keyword_var = ctk.BooleanVar(value=True)
        ctk.CTkCheckBox(
            check_frame,
            text="키워드 분석",
            variable=self.keyword_var
        ).pack(anchor="w", pady=3)
        
        # 차트 생성
        self.chart_var = ctk.BooleanVar(value=True)
        ctk.CTkCheckBox(
            check_frame,
            text="차트 생성",
            variable=self.chart_var
        ).pack(anchor="w", pady=3)
    
    def _create_output_section(self, parent):
        """출력 설정 섹션 생성"""
        output_frame = ctk.CTkFrame(parent)
        output_frame.pack(fill="x", pady=(0, 15))
        
        # 섹션 제목
        ctk.CTkLabel(
            output_frame,
            text="💾 출력 설정",
            font=("", 14, "bold")
        ).pack(anchor="w", padx=10, pady=(10, 5))
        
        # 출력 형식
        format_frame = ctk.CTkFrame(output_frame)
        format_frame.pack(fill="x", padx=10, pady=5)
        
        ctk.CTkLabel(format_frame, text="출력 형식:", width=80).pack(side="left", padx=(0, 10))
        
        self.format_var = ctk.StringVar(value="excel")
        ctk.CTkRadioButton(
            format_frame,
            text="Excel",
            variable=self.format_var,
            value="excel"
        ).pack(side="left", padx=5)
        
        ctk.CTkRadioButton(
            format_frame,
            text="PDF",
            variable=self.format_var,
            value="pdf"
        ).pack(side="left", padx=5)
        
        ctk.CTkRadioButton(
            format_frame,
            text="둘 다",
            variable=self.format_var,
            value="both"
        ).pack(side="left", padx=5)
        
        # 저장 경로
        path_frame = ctk.CTkFrame(output_frame)
        path_frame.pack(fill="x", padx=10, pady=(5, 10))
        
        ctk.CTkLabel(path_frame, text="저장 경로:", width=80).pack(side="left", padx=(0, 10))
        
        self.path_entry = ctk.CTkEntry(path_frame, placeholder_text="기본 경로 사용")
        self.path_entry.pack(side="left", fill="x", expand=True, padx=(0, 5))
        
        ctk.CTkButton(
            path_frame,
            text="찾아보기",
            width=80,
            command=self._browse_output_path
        ).pack(side="left")
    
    def _create_button_section(self, parent):
        """버튼 섹션 생성"""
        button_frame = ctk.CTkFrame(parent)
        button_frame.pack(fill="x", pady=(10, 0))
        
        # 미리보기 버튼
        ctk.CTkButton(
            button_frame,
            text="📈 미리보기",
            width=140,
            height=40,
            command=self._show_preview
        ).pack(side="left", padx=(10, 5), pady=10)
        
        # 생성 버튼
        ctk.CTkButton(
            button_frame,
            text="✅ 보고서 생성",
            width=140,
            height=40,
            fg_color="green",
            hover_color="darkgreen",
            command=self._generate_report
        ).pack(side="left", padx=5, pady=10)
        
        # 취소 버튼
        ctk.CTkButton(
            button_frame,
            text="❌ 취소",
            width=140,
            height=40,
            fg_color="gray",
            hover_color="darkgray",
            command=self.destroy
        ).pack(side="left", padx=5, pady=10)
    
    def _set_default_period(self):
        """기본 기간 설정 (최근 1개월)"""
        end_date = datetime.now()
        start_date = end_date - timedelta(days=30)
        
        self.start_entry.delete(0, "end")
        self.start_entry.insert(0, start_date.strftime("%Y-%m-%d"))
        
        self.end_entry.delete(0, "end")
        self.end_entry.insert(0, end_date.strftime("%Y-%m-%d"))
    
    def _set_quick_period(self, days: int):
        """빠른 기간 선택"""
        end_date = datetime.now()
        start_date = end_date - timedelta(days=days)
        
        self.start_entry.delete(0, "end")
        self.start_entry.insert(0, start_date.strftime("%Y-%m-%d"))
        
        self.end_entry.delete(0, "end")
        self.end_entry.insert(0, end_date.strftime("%Y-%m-%d"))
    
    def _browse_output_path(self):
        """저장 경로 선택"""
        path = filedialog.askdirectory(title="저장 경로 선택")
        if path:
            self.path_entry.delete(0, "end")
            self.path_entry.insert(0, path)
    
    def _validate_inputs(self) -> tuple:
        """
        입력값 검증
        
        Returns:
            tuple: (성공 여부, 에러 메시지)
        """
        start = self.start_entry.get().strip()
        end = self.end_entry.get().strip()
        
        if not start or not end:
            return (False, "시작일과 종료일을 입력해주세요.")
        
        # 날짜 형식 검증
        try:
            start_date = datetime.strptime(start, "%Y-%m-%d")
            end_date = datetime.strptime(end, "%Y-%m-%d")
        except ValueError:
            return (False, "날짜 형식이 잘못되었습니다. (YYYY-MM-DD)")
        
        # 기간 검증
        if start_date > end_date:
            return (False, "시작일이 종료일보다 늦을 수 없습니다.")
        
        if (end_date - start_date).days > 365:
            return (False, "분석 기간은 최대 1년까지만 가능합니다.")
        
        return (True, "")
    
    def _show_preview(self):
        """통계 미리보기"""
        # 입력값 검증
        valid, error_msg = self._validate_inputs()
        if not valid:
            messagebox.showerror("입력 오류", error_msg)
            return
        
        try:
            # 통계 요약 조회
            start = self.start_entry.get().strip()
            end = self.end_entry.get().strip()
            
            report = self.service.get_stats_summary(start, end)
            
            if report.total_count == 0:
                messagebox.showinfo("알림", "선택한 기간에 VOC 데이터가 없습니다.")
                return
            
            # 미리보기 창 생성
            self._create_preview_window(report)
        
        except Exception as e:
            self.logger.error(f"미리보기 실패: {e}")
            messagebox.showerror("오류", f"미리보기 중 오류 발생:\n{e}")
    
    def _create_preview_window(self, report):
        """미리보기 창 생성"""
        preview = ctk.CTkToplevel(self)
        preview.title("통계 미리보기")
        preview.geometry("500x600")
        
        # parent 설정으로 Z-order 문제 해결
        preview.transient(self)
        preview.lift()
        preview.focus_force()
        
        # 스크롤 가능한 프레임
        scroll_frame = ctk.CTkScrollableFrame(preview)
        scroll_frame.pack(fill="both", expand=True, padx=20, pady=20)
        
        # 요약 정보
        ctk.CTkLabel(
            scroll_frame,
            text=f"📊 VOC 통계 요약",
            font=("", 16, "bold")
        ).pack(pady=(0, 10))
        
        ctk.CTkLabel(
            scroll_frame,
            text=f"기간: {report.period_start} ~ {report.period_end}",
            font=("", 12)
        ).pack(pady=2)
        
        ctk.CTkLabel(
            scroll_frame,
            text=f"총 VOC 건수: {report.total_count}건",
            font=("", 12, "bold")
        ).pack(pady=2)
        
        # 부서별 통계
        if report.by_department:
            ctk.CTkLabel(
                scroll_frame,
                text="\n부서별 통계",
                font=("", 14, "bold")
            ).pack(pady=(10, 5))
            
            for dept, count in sorted(report.by_department.items(), key=lambda x: x[1], reverse=True):
                percentage = round(count / report.total_count * 100, 1)
                ctk.CTkLabel(
                    scroll_frame,
                    text=f"  • {dept}: {count}건 ({percentage}%)"
                ).pack(anchor="w", padx=20)
        
        # 상태별 통계
        if report.by_status:
            ctk.CTkLabel(
                scroll_frame,
                text="\n상태별 통계",
                font=("", 14, "bold")
            ).pack(pady=(10, 5))
            
            for status, count in sorted(report.by_status.items(), key=lambda x: x[1], reverse=True):
                percentage = round(count / report.total_count * 100, 1)
                ctk.CTkLabel(
                    scroll_frame,
                    text=f"  • {status}: {count}건 ({percentage}%)"
                ).pack(anchor="w", padx=20)
        
        # 주요 키워드
        if report.top_keywords:
            ctk.CTkLabel(
                scroll_frame,
                text="\n주요 키워드 TOP 10",
                font=("", 14, "bold")
            ).pack(pady=(10, 5))
            
            for i, (keyword, freq) in enumerate(report.top_keywords[:10], 1):
                ctk.CTkLabel(
                    scroll_frame,
                    text=f"  {i}. {keyword}: {freq}회"
                ).pack(anchor="w", padx=20)
    
    def _generate_report(self):
        """보고서 생성"""
        # 입력값 검증
        valid, error_msg = self._validate_inputs()
        if not valid:
            messagebox.showerror("입력 오류", error_msg)
            return
        
        try:
            # StatisticsOptions 생성
            from models.statistics import StatisticsOptions
            
            options = StatisticsOptions(
                period_start=self.start_entry.get().strip(),
                period_end=self.end_entry.get().strip(),
                include_department=self.dept_var.get(),
                include_status=self.status_var.get(),
                include_keywords=self.keyword_var.get(),
                include_charts=self.chart_var.get(),
                output_format=self.format_var.get(),
                output_path=self.path_entry.get().strip() or None
            )
            
            # 백그라운드 스레드에서 생성
            self._generate_in_background(options)
        
        except Exception as e:
            self.logger.error(f"보고서 생성 실패: {e}")
            messagebox.showerror("오류", f"보고서 생성 중 오류 발생:\n{e}")
    
    def _generate_in_background(self, options):
        """백그라운드에서 보고서 생성"""
        # 프로그레스 다이얼로그 표시
        progress = ctk.CTkToplevel(self)
        progress.title("보고서 생성 중")
        progress.geometry("300x100")
        progress.transient(self)
        progress.lift()
        progress.focus_force()
        progress.grab_set()
        
        ctk.CTkLabel(
            progress,
            text="보고서를 생성하고 있습니다...\n잠시만 기다려주세요.",
            font=("", 12)
        ).pack(pady=20)
        
        def generate():
            try:
                success, message = self.service.generate_report(options)
                
                # UI 스레드에서 결과 표시
                self.after(0, lambda: self._show_result(success, message, progress))
            except Exception as e:
                self.after(0, lambda: self._show_result(False, str(e), progress))
        
        # 백그라운드 스레드 시작
        threading.Thread(target=generate, daemon=True).start()
    
    def _show_result(self, success: bool, message: str, progress_window):
        """결과 표시"""
        progress_window.destroy()
        
        if success:
            # 성공 메시지 및 파일 열기 옵션
            result = messagebox.askyesno(
                "보고서 생성 완료",
                f"{message}\n\n파일을 여는 폴더를 열까요?"
            )
            
            if result:
                # 파일 경로에서 디렉토리 추출
                import os
                if "\n" in message:
                    # 여러 파일인 경우 첫 번째 파일 경로 사용
                    first_path = message.split('\n')[1] if message.split('\n')[1] else message.split('\n')[0]
                else:
                    first_path = message
                
                dir_path = os.path.dirname(first_path)
                os.startfile(dir_path)
            
            self.destroy()
        else:
            messagebox.showerror("보고서 생성 실패", message)
