import os
import re
import winreg
from pathlib import Path
from datetime import datetime

# Windows API 모듈 안전하게 임포트 (ImportError 방지)
try:
    import win32print
    import win32ui
    import win32con
    import win32com.client
    HAS_WIN32 = True
except ImportError:
    HAS_WIN32 = False

# Pandas 안전 임포트
try:
    import pandas as pd
    HAS_PANDAS = True
except ImportError:
    HAS_PANDAS = False

# ReportLab (PDF) 안전 임포트
try:
    from reportlab.pdfgen import canvas
    from reportlab.lib.pagesizes import A4
    from reportlab.pdfbase import pdfmetrics
    from reportlab.pdfbase.ttfonts import TTFont
    HAS_REPORTLAB = True
except ImportError:
    HAS_REPORTLAB = False

# --- 상수: 부산 1호선 역사 데이터 ---
class ReportService:
    """보고서 생성 및 인쇄 관련 비즈니스 로직 담당"""
    
    def __init__(self):
        self.config = self._load_config()
        self.stations = self.config.get("stations_L1", [])
        
        # 기본 설정 (Config 없으면 fallback)
        self.default_settings = {
            "dept_office": "신평차량사업소",
            "dept_team": "검수1", 
            "reporter_name": "이대석",
            "reporter_pos": "팀장",
            "reporter_tel": "200-5144",
            "action_taken": "○ 해당 편성 점검 완료"
        }

    def _load_config(self):
        try:
            import json
            base_dir = Path(__file__).resolve().parent
            config_path = base_dir / "config.json"
            if config_path.exists():
                with open(config_path, 'r', encoding='utf-8') as f:
                    return json.load(f)
        except Exception: 
            pass
        return {}

    def _setup_hwp_security(self):
        """한글 자동화 보안 모듈 등록"""
        try:
            # 현재 파일(report_service.py) 기준 test 폴더 내 dll 찾기 시도
            # 또는 test_voc.py 로직처럼 특정 위치 가정
            # 여기서는 편의상 패스하거나 레지스트리 등록 시도
            # 실제 배포시에는 dll 경로가 중요함. 
            # test_voc.py의 로직을 그대로 가져오되 경로만 주의
            base_dir = Path(__file__).resolve().parent.parent / "test" # app/../test
            dll_path = base_dir / "FilePathCheck.dll"
            
            if not dll_path.exists():
                return False 

            reg_path = r"Software\HNC\HwpAutomation\Modules"
            with winreg.CreateKey(winreg.HKEY_CURRENT_USER, reg_path) as key:
                winreg.SetValueEx(key, "FilePathCheckDLL", 0, winreg.REG_SZ, str(dll_path))
            return True
        except Exception: return False

    def _get_clean_number(self, text):
        return re.sub(r'[^0-9]', '', text) if text else ""

    def _get_korean_weekday(self, date_str):
        try:
            # YYYY.MM.DD 또는 YYYY-MM-DD 처리
            clean_date = date_str.strip().replace('-', '.')
            clean_date = clean_date.rstrip('.') # 끝에 점 제거
            dt = datetime.strptime(clean_date, "%Y.%m.%d")
            return "월화수목금토일"[dt.weekday()]
        except Exception: return ""

    def _infer_info_from_context(self, full_text):
        """본문 문맥을 분석하여 '호선', '방향' 정보를 유추합니다."""
        inferred_line = ""
        inferred_dir = ""
        
        # 1. 역명 검색으로 호선 유추
        for station in self.stations:
            if station in full_text:
                inferred_line = "1호선"
                break

        # 2. 방향성 유추
        # Case A: "XX방향" 직접 언급
        direction_match = re.search(r"(\w+역?)\s*방향", full_text)
        if direction_match:
            dir_word = direction_match.group(1)
            if "노포" in dir_word: inferred_dir = "노포행(하선)"
            elif "다대포" in dir_word: inferred_dir = "다대포행(상선)"

        # Case B: "A역에서 타서 B역에서 내림" 이동 경로 분석
        ride_match = re.search(r"(\w+역?)\s*에서\s*타서\s*(\w+역?)\s*에서\s*내렸", full_text)
        if ride_match and not inferred_dir:
            start_st = ride_match.group(1).replace("역", "")
            end_st = ride_match.group(2).replace("역", "")
            
            if start_st in self.stations and end_st in self.stations:
                start_idx = self.stations.index(start_st)
                end_idx = self.stations.index(end_st)
                # 인덱스 증가 -> 노포행(하선), 감소 -> 다대포행(상선)
                inferred_dir = "노포행(하선)" if start_idx < end_idx else "다대포행(상선)"

        return inferred_line, inferred_dir

    def _classify_train_details(self, train_str, context_dir=""):
        """열차번호를 분석합니다. 열차번호가 없으면 문맥에서 유추된 방향(context_dir)을 사용합니다."""
        train_num = self._get_clean_number(train_str)
        
        # 열차번호가 없는 경우 -> 문맥 방향 사용
        if not train_num:
            return {"num": "", "type": "미확인", "direction": context_dir or "미확인"}

        val = int(train_num)
        last_digit = val % 10
        
        # 1. 방향 판별 (열차번호 규칙 우선)
        direction = "다대포행(상선)" if last_digit % 2 == 1 else "노포행(하선)"
        
        # 2. 열차 종별 판별
        type_map = {(1, 2): "영업", (3, 4): "회송", (5, 6): "시운전", (7, 8): "단기", (9,): "임시"}
        t_type = next((v for k, v in type_map.items() if last_digit in k), "미확인")
        
        # 예외: 임시열차(9)는 보통 상선으로 간주 (규칙에 따라 조정 가능)
        if last_digit == 9: direction = "다대포행(상선)"

        return {"num": str(val), "type": t_type, "direction": direction}

    def _parse_voc_info(self, title, content):
        """VOC 내용 전체를 분석하여 구조화된 정보를 반환합니다."""
        full_text = f"{title} {content}"
        
        # 1. 기본 정보 정규식 추출
        patterns = {
            "line": r"(\d+)\s*호선",
            "set": r"(\d+)\s*편성",
            "car": r"(\d+)\s*호차",
            "train": r"(?:제)?\s*(\d{4})\s*열차"
        }
        info = {k: (re.search(p, full_text).group(0) if re.search(p, full_text) else "") 
                for k, p in patterns.items()}

        # 2. 문맥 기반 정보 유추 (호선, 방향)
        inf_line, inf_dir = self._infer_info_from_context(full_text)
        
        # 명시된 호선이 없으면 유추된 호선 적용
        if not info["line"]: info["line"] = inf_line
        
        # 3. 열차 상세 정보 분석 (문맥 방향 정보 전달)
        train_details = self._classify_train_details(info["train"], inf_dir)

        # 4. 파일명용 제목 정제
        formatted_title = title
        for pat in [r'\d+\s*호선', r'\d+\s*편성', r'\d+\s*호차', r'제?\d{4}열차', r'[,]']:
            formatted_title = re.sub(pat, '', formatted_title)
        pure_title = re.sub(r'\s+', ' ', formatted_title).strip()

        return {
            "line_str": info["line"],
            "set_str": info["set"],
            "car_str": info["car"],
            "train_str": info["train"],
            "pure_title": pure_title,
            "train_details": train_details
        }

    def _get_schedule_remarks(self, train_num):
        """Parquet 시각표 데이터 조회"""
        if not HAS_PANDAS:
            return "정보 없음(Pandas 미설치)"
            
        # test 폴더 내의 parquet 파일 참조
        base_dir = Path(__file__).resolve().parent.parent / "test" # app/../test
        parquet_path = base_dir / "train_schedule.parquet"
        
        if not train_num or not parquet_path.exists():
            return "정보 없음"
        try:
            df = pd.read_parquet(parquet_path)
            target_num = int(train_num)
            matched = df[df['train_no'] == target_num]
            if matched.empty: return "정보 없음"
            row = matched.iloc[0]
            return f"{row['location']}\n{row['type']}\n입고 예정\n{row['date']}\n{row['time']}경"
        except Exception:
            return "조회 실패"

    def print_voc_detail(self, data):
        """윈도우 기본 프린터로 VOC 상세 내용 인쇄"""
        if not HAS_WIN32:
            return False, "윈도우 인쇄 모듈(pywin32)이 로드되지 않았습니다.\n(pip install pywin32)"

        try:
            hDC = win32ui.CreateDC()
            hDC.CreatePrinterDC(win32print.GetDefaultPrinter())
            hDC.StartDoc(f"VOC_{data['id']}")
            hDC.StartPage()
            
            # 인쇄 내용 구성
            y = 100
            line_height = 80
            
            # 제목
            hDC.TextOut(100, y, f"제목: {data['title']}")
            y += line_height * 2
            
            # 주요 정보
            info_txt = f"접수번호: {data['id']} | 부서: {data.get('department')} | 작성자: {data.get('writer')}"
            hDC.TextOut(100, y, info_txt)
            y += line_height * 2
            
            # 내용
            content = str(data.get('content') or '')
            hDC.TextOut(100, y, "<질의 내용>")
            y += line_height
            
            # 간단한 줄바꿈 처리
            max_chars = 60
            for i in range(0, len(content), max_chars):
                line = content[i:i+max_chars]
                hDC.TextOut(100, y, line)
                y += line_height
                if y > 4000: break # 페이지 넘김 생략

            y += line_height
            hDC.TextOut(100, y, "<답변 내용>")
            y += line_height
            answer = str(data.get('answer') or '')
            for i in range(0, len(answer), max_chars):
                line = answer[i:i+max_chars]
                hDC.TextOut(100, y, line)
                y += line_height
                if y > 6500: break

            hDC.EndPage()
            hDC.EndDoc()
            hDC.DeleteDC()
            return True, "인쇄가 완료되었습니다."
            
        except Exception as e:
            return False, f"인쇄 오류: {e}"

    def create_hwp_report(self, data):
        """한글(HWP) 보고서 생성 (템플릿 활용 및 자동 정보 추출)"""
        if not HAS_WIN32:
            return False, "윈도우 자동화 모듈(pywin32)이 로드되지 않았습니다."

        # 보안 모듈 설정 시도
        self._setup_hwp_security()

        try:
            # 1. 한글 오토메이션 실행
            try:
                hwp = win32com.client.gencache.EnsureDispatch("HWPFrame.HwpObject")
            except:
                hwp = win32com.client.Dispatch("HWPFrame.HwpObject")
            
            hwp.XHwpWindows.Item(0).Visible = True
        except Exception as e:
            return False, f"한글(Hwp) 실행 실패: {e}"

        try:
            # 2. 템플릿 파일 로드
            base_dir = Path(__file__).resolve().parent.parent / "test" # app/../test
            sample_file = base_dir / "VOC_Sample.hwp"
            
            if not sample_file.exists():
                return False, f"템플릿 파일을 찾을 수 없습니다.\n{sample_file}"

            hwp.Open(str(sample_file))

            # 3. 정보 분석
            raw_title = data.get('title', '')
            raw_content = data.get('content', '')
            # 날짜 형식 보정 (YYYY-MM-DD -> YYYY.MM.DD)
            doc_date = str(data.get('date', '')).replace('-', '.')
            
            parsed = self._parse_voc_info(raw_title, raw_content)
            remarks_text = self._get_schedule_remarks(parsed["train_details"]["num"])
            
            # 4. 필드 데이터 데이터 매핑 (data에서 넘어온 옵션값 우선 사용)
            dept_office = data.get('office', self.default_settings['dept_office'])
            dept_team = data.get('team', self.default_settings['dept_team'])
            reporter_name = data.get('reporter_name', self.default_settings['reporter_name'])
            reporter_pos = data.get('position', self.default_settings['reporter_pos'])
            reporter_tel = data.get('reporter_tel', self.default_settings['reporter_tel'])
            action_taken = data.get('action_taken', self.default_settings['action_taken'])
            
            field_data = {
                "doc_date": doc_date,
                "doc_day": self._get_korean_weekday(doc_date),
                "dept_office": dept_office,
                "dept_team": dept_team,
                "reporter_name": reporter_name,
                "reporter_pos": reporter_pos,
                "reporter_tel": reporter_tel,
                "voc_content": raw_content,
                "action_taken": action_taken,
                "remarks": remarks_text,
                
                "line_num": parsed['line_str'],
                "train_set": parsed['set_str'],
                "train_num": parsed['train_str'],
                "train_type": parsed['train_details']['type'],
                "train_dir": parsed['train_details']['direction']
            }

            # 5. 데이터 입력
            for field, value in field_data.items():
                hwp.PutFieldText(field, value)
            
            # 6. 저장
            date_clean = doc_date.replace(".", "")
            name_parts = [parsed['line_str'], parsed['set_str'], parsed['car_str'], f"{parsed['pure_title']} 관련"]
            base_name = " ".join(filter(None, name_parts)).strip()
            if not base_name: base_name = "VOC_보고서"
            final_filename = f"{base_name}({date_clean})(VOC).hwp"
            
            # 저장 경로는 프로젝트 루트의 test 폴더로 지정 (요청사항 반영)
            save_path = base_dir / re.sub(r'[\\/:*?"<>|]', "", final_filename)
            hwp.SaveAs(str(save_path))
            
            # 종료하지 않고 열어둠 (사용자 확인용)
            # hwp.Quit() 
            
            return True, f"HWP 보고서가 생성되었습니다.\n{save_path}"
            
        except Exception as e:
            return False, f"보고서 생성 중 오류가 발생했습니다.\n{e}"

    def create_pdf_report(self, data):
        """PDF 보고서 생성 (ReportLab 활용)"""
        if not HAS_REPORTLAB:
            return False, "PDF 생성 모듈(reportlab)이 설치되지 않았습니다.\n(pip install reportlab)"

        try:
            # 1. 파일명 생성
            date_str = str(data.get('date') or '').replace('-', '')[:8] 
            if not date_str: date_str = datetime.now().strftime("%Y%m%d")
            
            safe_title = "".join([c for c in data['title'] if c.isalnum() or c in (' ', '(', ')', '[', ']')]).strip()
            filename = f"{safe_title}({date_str})(VOC).pdf"
            
            save_dir = os.path.join(os.getcwd(), "test")
            if not os.path.exists(save_dir):
                os.makedirs(save_dir)
            
            full_path = os.path.join(save_dir, filename)

            # 2. 한글 폰트 등록 (윈도우 기본)
            font_path = "C:/Windows/Fonts/malgun.ttf"
            if not os.path.exists(font_path):
                return False, "한글 폰트(malgun.ttf)를 찾을 수 없습니다."
            
            pdfmetrics.registerFont(TTFont('Malgun', font_path))

            # 3. PDF 생성
            c = canvas.Canvas(full_path, pagesize=A4)
            width, height = A4
            
            # 좌표 설정
            y = height - 50
            margin = 50
            line_gap = 20
            
            # 제목
            c.setFont("Malgun", 16)
            c.drawString(margin, y, f"제목: {data['title']}")
            y -= line_gap * 2
            
            # 상세 정보
            c.setFont("Malgun", 10)
            c.drawString(margin, y, f"접수번호: {data['id']}  |  등록일자: {data.get('date')}  |  부서: {data.get('department')}")
            y -= line_gap
            c.drawString(margin, y, f"작성자: {data.get('writer')}  |  상태: {data['status']}")
            y -= line_gap * 2
            
            # 본문 내용 함수
            def draw_multiline(text, x, y, max_width):
                lines = []
                chars_per_line = 45 
                for i in range(0, len(text), chars_per_line):
                    lines.append(text[i:i+chars_per_line])
                
                for line in lines:
                    if y < 50:
                        c.showPage()
                        c.setFont("Malgun", 10)
                        y = height - 50
                    c.drawString(x, y, line)
                    y -= line_gap
                return y

            c.setFont("Malgun", 12)
            c.drawString(margin, y, "[질의 내용]")
            y -= line_gap
            
            c.setFont("Malgun", 10)
            content = str(data.get('content') or '')
            y = draw_multiline(content, margin, y, width - 2*margin)
            y -= line_gap

            c.setFont("Malgun", 12)
            c.drawString(margin, y, "[답변 내용]")
            y -= line_gap
            
            c.setFont("Malgun", 10)
            answer = str(data.get('answer') or '')
            y = draw_multiline(answer, margin, y, width - 2*margin)

            c.save()
            
            return True, f"PDF 보고서가 생성되었습니다.\n{full_path}"

        except Exception as e:
            return False, f"PDF 생성 오류: {e}"
