"""
HTML 스타일 분석기 v3.0
HTML 요소를 분석하여 역할(Role)을 자동 분류

✅ v3.0 변경사항:
- 글벗 HTML 구조 완벽 지원 (.sheet, .body-content)
- 머리말/꼬리말/페이지번호 제거
- 강력한 중복 콘텐츠 필터링
- 제목 계층 구조 정확한 인식
"""

import re
from bs4 import BeautifulSoup, Tag, NavigableString
from dataclasses import dataclass
from typing import List, Dict, Optional, Tuple, Set
from enum import Enum


class DocumentSection(Enum):
    """문서 섹션 유형"""
    COVER = "cover"      # 표지
    TOC = "toc"          # 목차
    CONTENT = "content"  # 본문


@dataclass
class StyledElement:
    """스타일이 지정된 요소"""
    role: str           # 역할 (H1, BODY, TH 등)
    text: str           # 텍스트 내용
    tag: str            # 원본 HTML 태그
    html: str           # 원본 HTML
    section: str        # 섹션 (cover, toc, content)
    attributes: Dict    # 추가 속성 (이미지 src 등)
    
    def __repr__(self):
        preview = self.text[:30] + "..." if len(self.text) > 30 else self.text
        return f"<{self.role}> {preview}"


class StyleAnalyzer:
    """HTML 문서를 분석하여 역할 분류"""
    
    # 번호 패턴 정의
    PATTERNS = {
        # 장 번호: "제1장", "제2장"
        "chapter": re.compile(r'^제\s*\d+\s*장'),
        # 1단계 제목: "1 ", "2 " (숫자+공백, 점 없음)
        "h1_num": re.compile(r'^(\d+)\s+[가-힣]'),
        # 대항목: "1.", "2."
        "h2_num": re.compile(r'^(\d+)\.\s'),
        # 중항목: "1.1 ", "1.2 "
        "h3_num": re.compile(r'^(\d+)\.(\d+)\s'),
        # 소항목: "1.1.1"
        "h4_num": re.compile(r'^(\d+)\.(\d+)\.(\d+)'),
        # 세부: "1)", "2)"
        "h5_paren": re.compile(r'^(\d+)\)\s*'),
        # 세세부: "(1)", "(2)"
        "h6_paren": re.compile(r'^\((\d+)\)\s*'),
        # 가나다: "가.", "나."
        "h4_korean": re.compile(r'^[가-하]\.\s'),
        # 가나다 괄호: "가)", "나)"
        "h5_korean": re.compile(r'^[가-하]\)\s'),
        # 원문자: "①", "②"
        "h6_circle": re.compile(r'^[①②③④⑤⑥⑦⑧⑨⑩]'),
        # 목록: "•", "-", "○"
        "list_bullet": re.compile(r'^[•\-○]\s'),
        # 페이지 번호 패턴: "- 1 -", "- 12 -"
        "page_number": re.compile(r'^-\s*\d+\s*-$'),
        # 꼬리말 패턴: "문서제목- 1 -"
        "footer_pattern": re.compile(r'.+[-–]\s*\d+\s*[-–]$'),
    }
    
    # 제거할 텍스트 패턴들
    REMOVE_PATTERNS = [
        re.compile(r'^-\s*\d+\s*-$'),                    # "- 1 -"
        re.compile(r'[-–]\s*\d+\s*[-–]\s*$'),           # "문서제목- 1 -"
        re.compile(r'^\d+\s*×\s*\d+$'),                  # "643 × 236" (이미지 크기)
        re.compile(r'^\[이미지 없음:.*\]$'),              # "[이미지 없음: xxx]"
        re.compile(r'^\[그림\s*\d+-\d+\]$'),              # "[그림 1-1]"
    ]
    
    def __init__(self):
        self.elements: List[StyledElement] = []
        self.current_section = DocumentSection.CONTENT
        self.seen_texts: Set[str] = set()  # 중복 방지용
        self.document_title = ""  # 문서 제목 (꼬리말 제거용)
    
    def analyze(self, html: str) -> List[StyledElement]:
        """HTML 문서 분석하여 역할 분류된 요소 리스트 반환"""
        soup = BeautifulSoup(html, 'html.parser')
        self.elements = []
        self.seen_texts = set()
        
        # 1. 전처리: 불필요한 요소 제거
        self._preprocess(soup)
        
        # 2. 문서 제목 추출 (꼬리말 패턴 감지용)
        self._extract_document_title(soup)
        
        # 3. 섹션 감지 및 순회
        self._detect_and_process_sections(soup)
        
        # 4. 후처리: 중복 및 불필요 요소 제거
        self._postprocess()
        
        return self.elements
    
    def _preprocess(self, soup: BeautifulSoup):
        """HTML 전처리 - 불필요한 요소 제거"""
        print("  🔧 HTML 전처리 중...")
        
        # 1. 스크립트/스타일 태그 제거
        removed_count = 0
        for tag in soup(['script', 'style', 'noscript', 'meta', 'link', 'head']):
            tag.decompose()
            removed_count += 1
        
        if removed_count > 0:
            print(f"     - script/style 등 {removed_count}개 제거")
        
        # 2. 머리말/꼬리말 영역 제거 (글벗 HTML 구조)
        header_footer_count = 0
        for selector in ['.page-header', '.page-footer', '.header', '.footer', 
                        '[class*="header"]', '[class*="footer"]',
                        '.running-header', '.running-footer']:
            for elem in soup.select(selector):
                # 실제 콘텐츠 헤더가 아닌 페이지 헤더만 제거
                text = elem.get_text(strip=True)
                if self._is_header_footer_text(text):
                    elem.decompose()
                    header_footer_count += 1
        
        if header_footer_count > 0:
            print(f"     - 머리말/꼬리말 {header_footer_count}개 제거")
        
        # 3. 숨겨진 요소 제거
        hidden_count = 0
        for elem in soup.select('[style*="display:none"], [style*="display: none"]'):
            elem.decompose()
            hidden_count += 1
        for elem in soup.select('[style*="visibility:hidden"], [style*="visibility: hidden"]'):
            elem.decompose()
            hidden_count += 1
        
        # 4. #raw-container 외부의 .sheet 제거 (글벗 구조)
        raw_container = soup.find(id='raw-container')
        if raw_container:
            print("     - 글벗 구조 감지: #raw-container 우선 사용")
            # raw-container 외부의 모든 .sheet 제거
            for sheet in soup.select('.sheet'):
                if not self._is_descendant_of(sheet, raw_container):
                    sheet.decompose()
    
    def _extract_document_title(self, soup: BeautifulSoup):
        """문서 제목 추출 (꼬리말 패턴 감지용)"""
        # 표지에서 제목 찾기
        cover = soup.find(id='box-cover') or soup.find(class_='box-cover')
        if cover:
            h1 = cover.find('h1')
            if h1:
                self.document_title = h1.get_text(strip=True)
                print(f"     - 문서 제목 감지: {self.document_title[:30]}...")
    
    def _is_header_footer_text(self, text: str) -> bool:
        """머리말/꼬리말 텍스트인지 판단"""
        if not text:
            return False
        
        # 페이지 번호 패턴
        if self.PATTERNS['page_number'].match(text):
            return True
        
        # "문서제목- 1 -" 패턴
        if self.PATTERNS['footer_pattern'].match(text):
            return True
        
        # 문서 제목 + 페이지번호 조합
        if self.document_title and self.document_title in text:
            if re.search(r'[-–]\s*\d+\s*[-–]', text):
                return True
        
        return False
    
    def _should_skip_text(self, text: str) -> bool:
        """건너뛸 텍스트인지 판단"""
        if not text:
            return True
        
        # 제거 패턴 체크
        for pattern in self.REMOVE_PATTERNS:
            if pattern.match(text):
                return True
        
        # 머리말/꼬리말 체크
        if self._is_header_footer_text(text):
            return True
        
        # 문서 제목만 있는 줄 (꼬리말에서 온 것)
        if self.document_title and text.strip() == self.document_title:
            # 이미 표지에서 처리했으면 스킵
            if any(e.role == 'COVER_TITLE' and self.document_title in e.text 
                   for e in self.elements):
                return True
        
        return False
    
    def _is_descendant_of(self, element: Tag, ancestor: Tag) -> bool:
        """element가 ancestor의 자손인지 확인"""
        parent = element.parent
        while parent:
            if parent == ancestor:
                return True
            parent = parent.parent
        return False
    
    def _detect_and_process_sections(self, soup: BeautifulSoup):
        """섹션 감지 및 처리"""
        
        # 글벗 구조 (#raw-container) 우선 처리
        raw = soup.find(id='raw-container')
        if raw:
            self._process_geulbeot_structure(raw)
            return
        
        # .sheet 구조 처리 (렌더링된 페이지)
        sheets = soup.select('.sheet')
        if sheets:
            self._process_sheet_structure(sheets)
            return
        
        # 일반 HTML 구조 처리
        self._process_generic_html(soup)
    
    def _process_geulbeot_structure(self, raw: Tag):
        """글벗 HTML #raw-container 구조 처리"""
        print("  📄 글벗 #raw-container 구조 처리 중...")
        
        # 표지
        cover = raw.find(id='box-cover')
        if cover:
            print("     - 표지 섹션")
            self.current_section = DocumentSection.COVER
            self._process_cover(cover)
        
        # 목차
        toc = raw.find(id='box-toc')
        if toc:
            print("     - 목차 섹션")
            self.current_section = DocumentSection.TOC
            self._process_toc(toc)
        
        # 요약
        summary = raw.find(id='box-summary')
        if summary:
            print("     - 요약 섹션")
            self.current_section = DocumentSection.CONTENT
            self._process_content_element(summary)
        
        # 본문
        content = raw.find(id='box-content')
        if content:
            print("     - 본문 섹션")
            self.current_section = DocumentSection.CONTENT
            self._process_content_element(content)
    
    def _process_sheet_structure(self, sheets: List[Tag]):
        """글벗 .sheet 페이지 구조 처리"""
        print(f"  📄 .sheet 페이지 구조 처리 중... ({len(sheets)}페이지)")
        
        for i, sheet in enumerate(sheets):
            # 페이지 내 body-content만 추출
            body_content = sheet.select_one('.body-content')
            if body_content:
                self._process_content_element(body_content)
            else:
                # body-content가 없으면 머리말/꼬리말 제외하고 처리
                for child in sheet.children:
                    if isinstance(child, Tag):
                        classes = child.get('class', [])
                        class_str = ' '.join(classes) if classes else ''
                        
                        # 머리말/꼬리말 스킵
                        if any(x in class_str.lower() for x in ['header', 'footer']):
                            continue
                        
                        self._process_content_element(child)
    
    def _process_generic_html(self, soup: BeautifulSoup):
        """일반 HTML 구조 처리"""
        print("  📄 일반 HTML 구조 처리 중...")
        
        # 표지
        cover = soup.find(class_=re.compile(r'cover|title-page|box-cover'))
        if cover:
            self.current_section = DocumentSection.COVER
            self._process_cover(cover)
        
        # 목차
        toc = soup.find(class_=re.compile(r'toc|table-of-contents'))
        if toc:
            self.current_section = DocumentSection.TOC
            self._process_toc(toc)
        
        # 본문
        self.current_section = DocumentSection.CONTENT
        main_content = soup.find('main') or soup.find('article') or soup.find('body') or soup
        
        for child in main_content.children:
            if isinstance(child, Tag):
                self._process_content_element(child)
    
    def _process_cover(self, cover: Tag):
        """표지 처리"""
        # H1 = 제목
        h1 = cover.find('h1')
        if h1:
            text = h1.get_text(strip=True)
            if text and not self._is_duplicate(text):
                self.elements.append(StyledElement(
                    role="COVER_TITLE",
                    text=text,
                    tag="h1",
                    html=str(h1)[:200],
                    section="cover",
                    attributes={}
                ))
        
        # H2 = 부제목
        h2 = cover.find('h2')
        if h2:
            text = h2.get_text(strip=True)
            if text and not self._is_duplicate(text):
                self.elements.append(StyledElement(
                    role="COVER_SUBTITLE",
                    text=text,
                    tag="h2",
                    html=str(h2)[:200],
                    section="cover",
                    attributes={}
                ))
        
        # P = 정보
        for p in cover.find_all('p'):
            text = p.get_text(strip=True)
            if text and not self._is_duplicate(text):
                self.elements.append(StyledElement(
                    role="COVER_INFO",
                    text=text,
                    tag="p",
                    html=str(p)[:200],
                    section="cover",
                    attributes={}
                ))
    
    def _process_toc(self, toc: Tag):
        """목차 처리"""
        # UL/OL 기반 목차
        for li in toc.find_all('li'):
            text = li.get_text(strip=True)
            if not text or self._is_duplicate(text):
                continue
            
            classes = li.get('class', [])
            class_str = ' '.join(classes) if classes else ''
            
            # 레벨 판단 (구체적 → 일반 순서!)
            if 'lvl-1' in class_str or 'toc-lvl-1' in class_str:
                role = "TOC_H1"
            elif 'lvl-2' in class_str or 'toc-lvl-2' in class_str:
                role = "TOC_H2"
            elif 'lvl-3' in class_str or 'toc-lvl-3' in class_str:
                role = "TOC_H3"
            elif self.PATTERNS['h4_num'].match(text):   # 1.1.1 먼저!
                role = "TOC_H3"
            elif self.PATTERNS['h3_num'].match(text):   # 1.1 그다음
                role = "TOC_H2"
            elif self.PATTERNS['h2_num'].match(text):   # 1. 그다음
                role = "TOC_H1"
            else:
                role = "TOC_H1"
            
            self.elements.append(StyledElement(
                role=role,
                text=text,
                tag="li",
                html=str(li)[:200],
                section="toc",
                attributes={}
            ))
    
    def _process_content_element(self, element: Tag):
        """본문 요소 재귀 처리"""
        if not isinstance(element, Tag):
            return
        
        tag_name = element.name.lower() if element.name else ""
        classes = element.get('class', [])
        class_str = ' '.join(classes) if classes else ''
        
        # 머리말/꼬리말 클래스 스킵
        if any(x in class_str.lower() for x in ['header', 'footer', 'page-num']):
            return
        
        # 테이블 특수 처리
        if tag_name == 'table':
            self._process_table(element)
            return
        
        # 그림 특수 처리
        if tag_name in ['figure', 'img']:
            self._process_figure(element)
            return
        
        # 텍스트 추출
        text = self._get_direct_text(element)
        
        if text:
            # 건너뛸 텍스트 체크
            if self._should_skip_text(text):
                pass  # 자식은 계속 처리
            elif not self._is_duplicate(text):
                role = self._classify_role(element, tag_name, classes, text)
                if role:
                    self.elements.append(StyledElement(
                        role=role,
                        text=text,
                        tag=tag_name,
                        html=str(element)[:200],
                        section=self.current_section.value,
                        attributes=dict(element.attrs) if element.attrs else {}
                    ))
        
        # 자식 요소 재귀 처리 (컨테이너 태그)
        if tag_name in ['div', 'section', 'article', 'aside', 'main', 'body', 
                        'ul', 'ol', 'dl', 'blockquote']:
            for child in element.children:
                if isinstance(child, Tag):
                    self._process_content_element(child)
    
    def _get_direct_text(self, element: Tag) -> str:
        """요소의 직접 텍스트만 추출 (자식 컨테이너 제외)"""
        # 제목 태그는 전체 텍스트
        if element.name in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'li', 'td', 'th', 'caption']:
            return element.get_text(strip=True)
        
        # 컨테이너 태그는 직접 텍스트만
        texts = []
        for child in element.children:
            if isinstance(child, NavigableString):
                t = str(child).strip()
                if t:
                    texts.append(t)
        
        return ' '.join(texts)
    
    def _is_duplicate(self, text: str) -> bool:
        """중복 텍스트인지 확인"""
        if not text:
            return True
        
        # 정규화
        normalized = re.sub(r'\s+', ' ', text.strip())
        
        # 짧은 텍스트는 중복 허용 (번호 등)
        if len(normalized) < 10:
            return False
        
        # 첫 50자로 체크
        key = normalized[:50]
        
        if key in self.seen_texts:
            return True
        
        self.seen_texts.add(key)
        return False
    
    def _classify_role(self, element: Tag, tag: str, classes: List[str], text: str) -> Optional[str]:
        """요소의 역할 분류
        
        ⚠️ 중요: 패턴 매칭은 반드시 구체적인 것 → 일반적인 것 순서로!
           1.1.1 → 1.1 → 1. → 1
           (1) → 1)
           가) → 가.
        """
        
        class_str = ' '.join(classes) if classes else ''
        
        # ============ 제목 태그 (HTML 태그 우선) ============
        if tag == 'h1':
            return "H1"
        if tag == 'h2':
            return "H2"
        if tag == 'h3':
            return "H3"
        if tag == 'h4':
            return "H4"
        if tag == 'h5':
            return "H5"
        if tag == 'h6':
            return "H6"
        
        # ============ 본문 (p, div 등) - 번호 패턴으로 분류 ============
        if tag in ['p', 'div', 'span']:
            
            # ------ 숫자.숫자 패턴 (구체적 → 일반 순서!) ------
            
            # "1.1.1" 패턴 (가장 구체적 - 먼저 체크!)
            if self.PATTERNS['h4_num'].match(text):
                if len(text) < 100:
                    return "H3"
                return "BODY"
            
            # "1.1 " 패턴
            if self.PATTERNS['h3_num'].match(text):
                if len(text) < 100:
                    return "H2"
                return "BODY"
            
            # "1." 패턴
            if self.PATTERNS['h2_num'].match(text):
                if len(text) < 100:
                    return "H1"
                return "BODY"
            
            # "1 가나다..." 패턴 (숫자+공백+한글)
            if self.PATTERNS['h1_num'].match(text):
                return "H1"
            
            # ------ 괄호 패턴 (구체적 → 일반 순서!) ------
            
            # "(1)" 패턴 (괄호로 감싼 게 더 구체적 - 먼저 체크!)
            if self.PATTERNS['h6_paren'].match(text):
                if element.find('strong') or len(text) < 80:
                    return "H5"
                return "BODY"
            
            # "1)" 패턴
            if self.PATTERNS['h5_paren'].match(text):
                if element.find('strong') or len(text) < 80:
                    return "H4"
                return "BODY"
            
            # ------ 한글 패턴 (구체적 → 일반 순서!) ------
            
            # "가)" 패턴 (괄호가 더 구체적 - 먼저 체크!)
            if self.PATTERNS['h5_korean'].match(text):
                return "H5"
            
            # "가." 패턴
            if self.PATTERNS['h4_korean'].match(text):
                return "H4"
            
            # ------ 특수 기호 패턴 ------
            
            # "①②③" 패턴
            if self.PATTERNS['h6_circle'].match(text):
                return "H6"
            
            # ------ 기타 ------
            
            # 강조 박스
            if any(x in class_str for x in ['highlight', 'box', 'note', 'tip']):
                return "HIGHLIGHT_BOX"
            
            # 일반 본문
            return "BODY"
        
        # ============ 목록 ============
        if tag == 'li':
            return "LIST_ITEM"
        
        # ============ 정의 목록 ============
        if tag == 'dt':
            return "H5"
        if tag == 'dd':
            return "BODY"
        
        return "BODY"
    
    def _process_table(self, table: Tag):
        """테이블 처리 - 구조 데이터 포함"""
        
        # 캡션
        caption = table.find('caption')
        caption_text = ""
        if caption:
            caption_text = caption.get_text(strip=True)
            if caption_text and not self._is_duplicate(caption_text):
                self.elements.append(StyledElement(
                    role="TABLE_CAPTION",
                    text=caption_text,
                    tag="caption",
                    html=str(caption)[:100],
                    section=self.current_section.value,
                    attributes={}
                ))
        
        # 🆕 표 구조 데이터 수집
        table_data = {'rows': [], 'caption': caption_text}
        
        for tr in table.find_all('tr'):
            row = []
            for cell in tr.find_all(['th', 'td']):
                cell_info = {
                    'text': cell.get_text(strip=True),
                    'is_header': cell.name == 'th',
                    'colspan': int(cell.get('colspan', 1)),
                    'rowspan': int(cell.get('rowspan', 1)),
                    'bg_color': self._extract_bg_color(cell),
                }
                row.append(cell_info)
            if row:
                table_data['rows'].append(row)
        
        # 🆕 TABLE 요소로 추가 (개별 TH/TD 대신)
        if table_data['rows']:
            self.elements.append(StyledElement(
                role="TABLE",
                text=f"[표: {len(table_data['rows'])}행]",
                tag="table",
                html=str(table)[:200],
                section=self.current_section.value,
                attributes={'table_data': table_data}
            ))

    def _extract_bg_color(self, element: Tag) -> str:
        """요소에서 배경색 추출"""
        style = element.get('style', '')
        
        # background-color 추출
        match = re.search(r'background-color:\s*([^;]+)', style)
        if match:
            return self._normalize_color(match.group(1))
        
        # bgcolor 속성
        bgcolor = element.get('bgcolor', '')
        if bgcolor:
            return self._normalize_color(bgcolor)
        
        return ''
    
    def _process_figure(self, element: Tag):
        """그림 처리"""
        img = element.find('img') if element.name == 'figure' else element
        
        if img and img.name == 'img':
            src = img.get('src', '')
            alt = img.get('alt', '')
            
            if src:  # src가 있을 때만 추가
                self.elements.append(StyledElement(
                    role="FIGURE",
                    text=alt or "이미지",
                    tag="img",
                    html=str(img)[:100],
                    section=self.current_section.value,
                    attributes={"src": src, "alt": alt}
                ))
        
        # 캡션
        if element.name == 'figure':
            figcaption = element.find('figcaption')
            if figcaption:
                text = figcaption.get_text(strip=True)
                if text and not self._should_skip_text(text):
                    self.elements.append(StyledElement(
                        role="FIGURE_CAPTION",
                        text=text,
                        tag="figcaption",
                        html=str(figcaption)[:100],
                        section=self.current_section.value,
                        attributes={}
                    ))
    
    def _postprocess(self):
        """후처리: 불필요 요소 제거"""
        print(f"  🧹 후처리 중... (처리 전: {len(self.elements)}개)")
        
        filtered = []
        for elem in self.elements:
            # 빈 텍스트 제거
            if not elem.text or not elem.text.strip():
                continue
            
            # 머리말/꼬리말 텍스트 제거
            if self._is_header_footer_text(elem.text):
                continue
            
            # 제거 패턴 체크
            skip = False
            for pattern in self.REMOVE_PATTERNS:
                if pattern.match(elem.text.strip()):
                    skip = True
                    break
            
            if not skip:
                filtered.append(elem)
        
        self.elements = filtered
        print(f"     - 처리 후: {len(self.elements)}개")
    
    def get_role_summary(self) -> Dict[str, int]:
        """역할별 요소 수 요약"""
        summary = {}
        for elem in self.elements:
            summary[elem.role] = summary.get(elem.role, 0) + 1
        return dict(sorted(summary.items()))


    def extract_css_styles(self, html: str) -> Dict[str, Dict]:
        """
        HTML에서 역할별 CSS 스타일 추출
        Returns: {역할: {font_size, color, bold, ...}}
        """
        soup = BeautifulSoup(html, 'html.parser')
        role_styles = {}
        
        # <style> 태그에서 CSS 파싱
        style_tag = soup.find('style')
        if style_tag:
            css_text = style_tag.string or ''
            role_styles.update(self._parse_css_rules(css_text))
        
        # 인라인 스타일에서 추출 (요소별)
        for elem in self.elements:
            if elem.role not in role_styles:
                role_styles[elem.role] = self._extract_inline_style(elem.html)
        
        return role_styles

    def _parse_css_rules(self, css_text: str) -> Dict[str, Dict]:
        """CSS 텍스트에서 규칙 파싱"""
        import re
        rules = {}
        
        # h1, h2, .section-title 등의 패턴
        pattern = r'([^{]+)\{([^}]+)\}'
        for match in re.finditer(pattern, css_text):
            selector = match.group(1).strip()
            properties = match.group(2)
            
            style = {}
            for prop in properties.split(';'):
                if ':' in prop:
                    key, value = prop.split(':', 1)
                    key = key.strip().lower()
                    value = value.strip()
                    
                    if key == 'font-size':
                        style['font_size'] = self._parse_font_size(value)
                    elif key == 'color':
                        style['color'] = self._normalize_color(value)
                    elif key == 'font-weight':
                        style['bold'] = value in ['bold', '700', '800', '900']
                    elif key == 'text-align':
                        style['align'] = value
            
            # 셀렉터 → 역할 매핑
            role = self._selector_to_role(selector)
            if role:
                rules[role] = style
        
        return rules

    def _selector_to_role(self, selector: str) -> str:
        """CSS 셀렉터 → 역할 매핑"""
        selector = selector.lower().strip()
        mapping = {
            'h1': 'H1', 'h2': 'H2', 'h3': 'H3', 'h4': 'H4',
            '.cover-title': 'COVER_TITLE',
            '.section-title': 'H1',
            'th': 'TH', 'td': 'TD',
            'p': 'BODY',
        }
        for key, role in mapping.items():
            if key in selector:
                return role
        return None

    def _parse_font_size(self, value: str) -> float:
        """폰트 크기 파싱 (pt 단위로 변환)"""
        import re
        match = re.search(r'([\d.]+)(pt|px|em|rem)?', value)
        if match:
            size = float(match.group(1))
            unit = match.group(2) or 'pt'
            if unit == 'px':
                size = size * 0.75  # px → pt
            elif unit in ['em', 'rem']:
                size = size * 11  # 기본 11pt 기준
            return size
        return 11.0

    def _normalize_color(self, value: str) -> str:
        """색상값 정규화 (#RRGGBB)"""
        import re
        value = value.strip().lower()
        
        # 이미 #rrggbb 형식
        if re.match(r'^#[0-9a-f]{6}$', value):
            return value.upper()
        
        # #rgb → #rrggbb
        if re.match(r'^#[0-9a-f]{3}$', value):
            return f'#{value[1]*2}{value[2]*2}{value[3]*2}'.upper()
        
        # rgb(r, g, b)
        match = re.search(r'rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)', value)
        if match:
            r, g, b = int(match.group(1)), int(match.group(2)), int(match.group(3))
            return f'#{r:02X}{g:02X}{b:02X}'
        
        # 색상 이름
        color_names = {
            'black': '#000000', 'white': '#FFFFFF',
            'red': '#FF0000', 'green': '#008000', 'blue': '#0000FF',
            'navy': '#1A365D',
        }
        return color_names.get(value, '#000000')

    def _extract_inline_style(self, html: str) -> Dict:
        """HTML 요소에서 인라인 스타일 추출"""
        style = {}
        
        # style 속성 찾기
        match = re.search(r'style\s*=\s*["\']([^"\']+)["\']', html)
        if match:
            style_str = match.group(1)
            for prop in style_str.split(';'):
                if ':' in prop:
                    key, value = prop.split(':', 1)
                    key = key.strip().lower()
                    value = value.strip()
                    
                    if key == 'font-size':
                        style['font_size'] = self._parse_font_size(value)
                    elif key == 'color':
                        style['color'] = self._normalize_color(value)
                    elif key == 'font-weight':
                        style['bold'] = value in ['bold', '700', '800', '900']
                    elif key == 'text-align':
                        style['align'] = value
                    elif key == 'background-color':
                        style['bg_color'] = self._normalize_color(value)
        
        return style

    def _extract_bg_color(self, element) -> str:
        """요소에서 배경색 추출"""
        if not hasattr(element, 'get'):
            return ''
        
        style = element.get('style', '')
        
        # background-color 추출
        match = re.search(r'background-color:\s*([^;]+)', style)
        if match:
            return self._normalize_color(match.group(1))
        
        # bgcolor 속성
        bgcolor = element.get('bgcolor', '')
        if bgcolor:
            return self._normalize_color(bgcolor)
        
        return ''


    def export_for_hwp(self) -> List[Dict]:
            """HWP 변환용 데이터 내보내기"""
            return [
                {
                    "role": e.role,
                    "text": e.text,
                    "tag": e.tag,
                    "section": e.section,
                    "attributes": e.attributes
                }
                for e in self.elements
            ]


if __name__ == "__main__":
    # 테스트
    test_html = """
    <html>
    <head>
        <script>var x = 1;</script>
        <style>.test { color: red; }</style>
    </head>
    <body>
        <div class="sheet">
            <div class="page-header">건설·토목 측량 DX 실무지침</div>
            <div class="body-content">
                <h1>1 DX 개요와 기본 개념</h1>
                <h2>1.1 측량 DX 프레임</h2>
                <h3>1.1.1 측량 DX 발전 단계</h3>
                <p>1) <strong>Digitization 정의</strong></p>
                <p>본문 내용입니다. 이것은 충분히 긴 텍스트로 본문으로 인식되어야 합니다.</p>
                <p>(1) 단계별 정의 및 진화</p>
                <p>측량 기술의 발전은 장비의 변화와 성과물의 차원에 따라 구분된다.</p>
            </div>
            <div class="page-footer">건설·토목 측량 DX 실무지침- 1 -</div>
        </div>
        
        <div class="sheet">
            <div class="page-header">건설·토목 측량 DX 실무지침</div>
            <div class="body-content">
                <p>① 첫 번째 항목</p>
                <table>
                    <caption>표 1. 데이터 비교</caption>
                    <tr><th>구분</th><th>내용</th></tr>
                    <tr><td>항목1</td><td>설명1</td></tr>
                </table>
            </div>
            <div class="page-footer">건설·토목 측량 DX 실무지침- 2 -</div>
        </div>
    </body>
    </html>
    """
    
    analyzer = StyleAnalyzer()
    elements = analyzer.analyze(test_html)
    
    print("\n" + "="*60)
    print("분석 결과")
    print("="*60)
    for elem in elements:
        print(f"  {elem.role:18} | {elem.section:7} | {elem.text[:50]}")
    
    print("\n" + "="*60)
    print("역할 요약")
    print("="*60)
    for role, count in analyzer.get_role_summary().items():
        print(f"  {role}: {count}")