import json
import time
import threading
import schedule
import customtkinter as ctk
from tkinter import messagebox

from app.scraper_service import VOCScraper
from app.database import VOCDatabase
from app.view import SettingsView, SystemTray, VOCListView, VOCDetailView
from app.report_service import ReportService

SETTINGS_FILE = 'settings.json'

class AppController:
    def __init__(self, test_mode=False):
        self.test_mode = test_mode
        self.load_settings()
        self.db = VOCDatabase()
        if not self.test_mode:
            self.model = VOCScraper()
        else:
            self.model = None # 테스트 모드에서는 스크래퍼 초기화 안 함
            
        self.report_service = ReportService() # 서비스 레이어 추가
        self.tray = SystemTray(self)
        
        self.view = None # 나중에 초기화
        self.running = False
        self.list_window = None
        self.settings_window = None # 설정 창 초기화 추가

        # [중요] 모든 GUI의 부모가 될 hidden root 생성
        self.root = ctk.CTk()
        self.root.withdraw() # 메인 창은 숨김

    def request_print_voc(self, data):
        """View로부터 인쇄 요청을 받아 서비스 레이어 호출"""
        success, msg = self.report_service.print_voc_detail(data)
        if success:
            messagebox.showinfo("인쇄 성공", msg)
        else:
            messagebox.showerror("인쇄 실패", msg)

    def request_create_report(self, data):
        """View로부터 보고서 생성 요청을 받아 옵션 먼저 선택하도록 다이얼로그 띄움"""
        from app.view.dialogs.report_option_dialog import ReportOptionDialog
        
        # 콜백 함수: 다이얼로그에서 '생성'을 누르면 실행될 실제 작업
        def actual_generate(data, options):
             # options: { 'office': '...', 'team': '...', ... }
             # report_service에 전달할 때 이 옵션들을 병합해서 전달
             data.update(options) # 원본 데이터를 건드리는 건 좋지 않지만, 여기선 편의상 병합
             success, msg = self.report_service.create_hwp_report(data)
             if success:
                 messagebox.showinfo("보고서 생성 성공", msg)
             else:
                 messagebox.showerror("보고서 생성 실패", msg)

        ReportOptionDialog(self.root, self, data, callback=actual_generate)

    def save_report_options(self, options):
        """보고서 옵션(작성자, 부서 등)을 설정에 저장"""
        self.settings['report_options'] = options
        self.save_settings()

    def request_create_pdf(self, data):
        """View로부터 PDF 생성 요청을 받아 서비스 레이어 호출"""
        success, msg = self.report_service.create_pdf_report(data)
        if success:
            messagebox.showinfo("PDF 생성 성공", msg)
        else:
            messagebox.showerror("PDF 생성 실패", msg)

    def load_settings(self):
        """설정 파일 로드 (없으면 기본값 생성)"""
        try:
            with open(SETTINGS_FILE, 'r', encoding='utf-8') as f:
                self.settings = json.load(f)
        except:
            # 기본 설정 생성
            self.settings = {
                "login": {"id": "", "pw": ""},
                "crawling": {
                    "interval_minutes": 10,
                    "max_pages": 2,
                    "target_depts": ["승무", "차량", "운영"],
                    "keywords": ["안전", "장애"],
                    "recheck_hours": 3
                }
            }
            self.save_settings()

    def save_settings(self):
        """현재 설정을 파일로 저장"""
        with open(SETTINGS_FILE, 'w', encoding='utf-8') as f:
            json.dump(self.settings, f, indent=4, ensure_ascii=False)

    def is_related_post(self, title, dept):
        """설정에 따른 관심글 여부 판단 (키워드 또는 부서 매칭)"""
        # 1. 키워드 체크
        for kw in self.settings['crawling']['keywords']:
            if kw in title: return 1
        # 2. 부서 체크
        for target in self.settings['crawling']['target_depts']:
            if target in dept: return 1
        return 0

    def run_cycle(self):
        """크롤링 전체 사이클 실행 및 알림 발송"""
        if self.test_mode:
            print(f"[{time.strftime('%H:%M')}] [테스트모드] 웹 수집을 건너뛰고 DB 데이터를 사용합니다.")
            return

        print(f"[{time.strftime('%H:%M')}] 크롤링 사이클 시작...")
        
        # 1. 로그인 상태 확인 및 재로그인
        if not self.model.is_logged_in():
            print("로그인 세션 만료. 재로그인 시도...")
            login_success = self.model.login(
                self.settings['login']['id'], 
                self.settings['login']['pw']
            )
            if not login_success:
                print("재로그인 실패. 다음 주기에 재시도합니다.")
                return

        # 2. 목록 수집 (Metadata)
        max_pg = self.settings['crawling'].get('max_pages', 2)
        keywords = self.settings['crawling']['keywords']
        target_depts = self.settings['crawling']['target_depts']
        
        list_result = self.model.fetch_list_pages(max_pg, keywords, target_depts)
        
        if list_result.get('status') == 'session_expired':
            # 목록 수집 중 튕기면 바로 재로그인 후 재시도
            self.model.login(self.settings['login']['id'], self.settings['login']['pw'])
            list_result = self.model.fetch_list_pages(max_pg, keywords, target_depts)

        if list_result.get('status') == 'success':
            for post in list_result['data']:
                # 관심글 여부 마킹
                post['is_related'] = self.is_related_post(post['title'], post['department'])
                
                # DB 저장 (Upsert)
                is_new, is_updated = self.db.upsert_post(post)
                
                if is_new:
                    # 신규 글 알림
                    if post['is_related']:
                        self.tray.show_notification("신규 관심 VOC", f"[{post['department']}] {post['title']}")
                
                if is_updated:
                    # 상태 변경 알림
                    if post['is_related']:
                        self.tray.show_notification("VOC 상태 업데이트", f"상태변경: {post['title']}")

        # 3. 상세 내용 수집 (조건부)
        # 내용이 없거나 최근 글인 경우 상세 파싱 시도
        targets = self.db.get_posts_needing_detail(self.settings['crawling']['recheck_hours'])
        
        for tgt in targets:
            voc_id, title, is_related = tgt
            
            # [수정] 비공개 글 체크: 상세 수집 전 다시 확인
            post_check = self.db.get_post_by_id(voc_id)
            if post_check and post_check['is_public'] == 0:
                # 비공개 글은 상세 수집 패스
                # (단, 나중에 처리완료 여부 확인 등을 위해 메타데이터만 업데이트할 수도 있지만,
                #  현재 사용자 요구는 "열람하지마" 이므로 접속 자제)
                continue

            detail_data = self.model.fetch_detail_content(voc_id)
            
            if detail_data:
                # DB 업데이트
                self.db.update_detail(voc_id, detail_data)
                
                # 내용이 신규 수집되었고 관심글이라면 알림 (필요 시)
                if is_related:
                    print(f"상세 정보 업데이트 완료: {title}")
            
            time.sleep(1.2) # 서버 부하 방지용 딜레이

    def start_background(self):
        """백그라운드 스레드 시작 및 트레이 아이콘 가동"""
        self.running = True
        
        # 초기 스케줄 설정
        self.update_config_runtime()

        # 1. 스케줄러 루프 실행
        threading.Thread(target=self._scheduler_loop, daemon=True).start()
        
        # 2. 첫 실행은 즉시 시작
        threading.Thread(target=self.run_cycle, daemon=True).start()

        # 3. 트레이 아이콘 실행
        threading.Thread(target=self.tray.run, args=(
            self.run_cycle,           # on_check
            self.open_list_view,      # on_list
            self.open_settings,       # on_settings
            self.quit_app             # on_quit
        ), daemon=True).start()

        # 4. GUI 메인 루프 (Main Thread 점유)
        self.root.mainloop()

    def _scheduler_loop(self):
        """스케줄 등록된 작업을 주기적으로 실행"""
        while self.running:
            schedule.run_pending()
            time.sleep(1)

    def open_list_view(self, focus_id=None):
        """수집 목록 창 열기 (이미 열려 있으면 포커스 및 데이터 갱신)"""
        self.root.after(0, lambda: self._actual_open_list(focus_id))

    def _actual_open_list(self, focus_id):
        """실제 창 생성 및 데이터 바인딩 (메인 스레드에서 실행)"""
        rows = self.db.get_all_posts()
        # View에 전달할 데이터 포맷팅 (채널, 작성자, 공개여부 추가)
        formatted = []
        for r in rows:
            formatted.append({
                'id': str(r[0]), 
                'title': r[1], 
                'writer': r[2],
                'department': r[4], 
                'is_public': r[5],
                'status': r[6],
                'channel': r[10], # channel (접수채널) 인덱스 주의
                'date': r[3]
            })

        if self.list_window is None or not self.list_window.winfo_exists():
            self.list_window = VOCListView(self, formatted)
        else:
            self.list_window.update_data(formatted)
            self.list_window.deiconify()
            self.list_window.focus_force()

        # 특정 게시글 바로 조회 요청이 있는 경우 (예: 검색 시)
        if focus_id and isinstance(focus_id, str):
            self.request_detail_popup(focus_id)

    def request_detail_popup(self, voc_id):
        """특정 VOC의 상세 정보를 별도의 창으로 엽니다."""
        post = self.db.get_post_by_id(voc_id)
        if post:
            # post는 sqlite3.Row 객체이므로 딕셔너리로 변환 (안전한 방법 사용)
            data = {k: post[k] for k in post.keys()}
            self.root.after(0, lambda: VOCDetailView(self, data))

    def handle_view_request_detail(self, voc_id):
        """목록 창 내의 상세 텍스트 박스 업데이트 (추가 호환성용)"""
        post = self.db.get_post_by_id(voc_id)
        if post and self.list_window:
            title = post['title']
            content = post['content'] if post['content'] else "상세 내용이 아직 수집되지 않았습니다."
            self.list_window.display_detail(title, content)

    def open_settings(self, icon=None, item=None):
        """설정 창 열기"""
        self.root.after(0, self._actual_open_settings)

    def _actual_open_settings(self):
        """실제 설정 창 생성 (메인 스레드에서 실행)"""
        if self.settings_window is None or not self.settings_window.winfo_exists():
            self.settings_window = SettingsView(self)
        else:
            self.settings_window.deiconify()
            self.settings_window.focus_force()

    def update_config_runtime(self):
        """설정 변경 시 스케줄러 재설정"""
        schedule.clear()
        interval = self.settings['crawling']['interval_minutes']
        schedule.every(interval).minutes.do(self.run_cycle)
        print(f"스케줄 업데이트: {interval}분 간격으로 수집합니다.")

    def quit_app(self, icon=None, item=None):
        """애플리케이션 종료"""
        print("프로그램을 종료합니다...")
        self.running = False
        if self.model:
            self.model.close()
        self.tray.stop()
        self.root.quit()
        self.root.destroy()
