"""
전체 재질명 추출기
원본 설명에서 완전한 재질명을 추출하여 축약되지 않은 형태로 제공
"""

import logging
import re
from typing import Optional, Dict

logger = logging.getLogger(__name__)

def extract_full_material_grade(description: str) -> str:
    """
    원본 설명에서 전체 재질명 추출
    
    Args:
        description: 원본 자재 설명
    
    Returns:
        전체 재질명 (예: "ASTM A312 TP304", "ASTM A106 GR B")
    """
    if not description:
        return ""
    
    desc_upper = description.upper().strip()
    
    # 1. ASTM 규격 패턴들 (가장 구체적인 것부터)
    astm_patterns = [
        # A320 L7, A325, A490 등 단독 규격 (ASTM 없이)
        r'\bA320\s+L[0-9]+\b',  # A320 L7
        r'\bA325\b',            # A325
        r'\bA490\b',            # A490
        # ASTM A193/A194 GR B7/2H (볼트용 조합 패턴) - 최우선
        r'ASTM\s+A193/A194\s+GR\s+[A-Z0-9/]+',
        r'ASTM\s+A193/A194\s+[A-Z0-9/]+',
        # ASTM A320/A194M GR B8/8 (저온용 볼트 조합 패턴)
        r'ASTM\s+A320[M]?/A194[M]?\s+GR\s+[A-Z0-9/]+',
        r'ASTM\s+A320[M]?/A194[M]?\s+[A-Z0-9/]+',
        # 단독 A193/A194 패턴 (ASTM 없이)
        r'\bA193/A194\s+GR\s+[A-Z0-9/]+\b',
        r'\bA193/A194\s+[A-Z0-9/]+\b',
        # 단독 A320/A194M 패턴 (ASTM 없이)
        r'\bA320[M]?/A194[M]?\s+GR\s+[A-Z0-9/]+\b',
        r'\bA320[M]?/A194[M]?\s+[A-Z0-9/]+\b',
        # ASTM A312 TP304, ASTM A312 TP316L 등
        r'ASTM\s+A\d{3,4}[A-Z]*\s+TP\d+[A-Z]*',
        # ASTM A182 F304, ASTM A182 F316L 등
        r'ASTM\s+A\d{3,4}[A-Z]*\s+F\d+[A-Z]*',
        # ASTM A403 WP304, ASTM A234 WPB 등
        r'ASTM\s+A\d{3,4}[A-Z]*\s+WP[A-Z0-9]+',
        # ASTM A351 CF8M, ASTM A216 WCB 등
        r'ASTM\s+A\d{3,4}[A-Z]*\s+[A-Z]{2,4}[0-9]*[A-Z]*',
        # ASTM A106 GR B, ASTM A105 등 - GR 포함
        r'ASTM\s+A\d{3,4}[A-Z]*\s+GR\s+[A-Z0-9/]+',
        r'ASTM\s+A\d{3,4}[A-Z]*\s+GRADE\s+[A-Z0-9/]+',
        # ASTM A106 B (GR 없이 바로 등급) - 단일 문자 등급
        r'ASTM\s+A\d{3,4}[A-Z]*\s+[A-Z](?=\s|$)',
        # ASTM A105, ASTM A234 등 (등급 없는 경우)
        r'ASTM\s+A\d{3,4}[A-Z]*(?!\s+[A-Z0-9])',
        # 2자리 ASTM 규격도 지원 (A10, A36 등)
        r'ASTM\s+A\d{2}(?:\s+GR\s+[A-Z0-9/]+)?',
    ]
    
    for pattern in astm_patterns:
        match = re.search(pattern, desc_upper)
        if match:
            full_grade = match.group(0).strip()
            # 추가 정보가 있는지 확인 (PBE, BBE 등은 제외)
            end_pos = match.end()
            remaining = desc_upper[end_pos:].strip()
            
            # 끝단 가공 정보는 제외
            end_prep_codes = ['PBE', 'BBE', 'POE', 'BOE', 'TOE']
            for code in end_prep_codes:
                remaining = re.sub(rf'\b{code}\b', '', remaining).strip()
            
            # 남은 재질 관련 정보가 있으면 추가
            additional_info = []
            if remaining:
                # 일반적인 재질 추가 정보 패턴
                additional_patterns = [
                    r'\bH\b',  # H (고온용)
                    r'\bL\b',  # L (저탄소)
                    r'\bN\b',  # N (질소 첨가)
                    r'\bS\b',  # S (황 첨가)
                    r'\bMOD\b',  # MOD (개량형)
                ]
                
                for add_pattern in additional_patterns:
                    if re.search(add_pattern, remaining):
                        additional_info.append(re.search(add_pattern, remaining).group(0))
            
            if additional_info:
                full_grade += ' ' + ' '.join(additional_info)
            
            return full_grade
    
    # 2. ASME 규격 패턴들
    asme_patterns = [
        r'ASME\s+SA\d+[A-Z]*\s+TP\d+[A-Z]*(?:\s+[A-Z]+)*',
        r'ASME\s+SA\d+[A-Z]*\s+GR\s+[A-Z0-9]+(?:\s+[A-Z]+)*',
        r'ASME\s+SA\d+[A-Z]*\s+F\d+[A-Z]*(?:\s+[A-Z]+)*',
        r'ASME\s+SA\d+[A-Z]*(?!\s+[A-Z0-9])',
    ]
    
    for pattern in asme_patterns:
        match = re.search(pattern, desc_upper)
        if match:
            return match.group(0).strip()
    
    # 3. KS 규격 패턴들
    ks_patterns = [
        r'KS\s+D\d+[A-Z]*\s+[A-Z0-9]+(?:\s+[A-Z]+)*',
        r'KS\s+D\d+[A-Z]*(?!\s+[A-Z0-9])',
    ]
    
    for pattern in ks_patterns:
        match = re.search(pattern, desc_upper)
        if match:
            return match.group(0).strip()
    
    # 4. JIS 규격 패턴들
    jis_patterns = [
        r'JIS\s+[A-Z]\d+[A-Z]*\s+[A-Z0-9]+(?:\s+[A-Z]+)*',
        r'JIS\s+[A-Z]\d+[A-Z]*(?!\s+[A-Z0-9])',
    ]
    
    for pattern in jis_patterns:
        match = re.search(pattern, desc_upper)
        if match:
            return match.group(0).strip()
    
    # 5. 특수 재질 패턴들
    special_patterns = [
        # Inconel, Hastelloy 등
        r'INCONEL\s+\d+[A-Z]*',
        r'HASTELLOY\s+[A-Z]\d*[A-Z]*',
        r'MONEL\s+\d+[A-Z]*',
        # Titanium
        r'TITANIUM\s+GRADE\s+\d+[A-Z]*',
        r'TI\s+GR\s*\d+[A-Z]*',
        # 듀플렉스 스테인리스
        r'DUPLEX\s+\d+[A-Z]*',
        r'SUPER\s+DUPLEX\s+\d+[A-Z]*',
    ]
    
    for pattern in special_patterns:
        match = re.search(pattern, desc_upper)
        if match:
            return match.group(0).strip()
    
    # 6. 일반 스테인리스 패턴들 (숫자만)
    stainless_patterns = [
        r'\b(304L?|316L?|321|347|310|317L?|904L?|254SMO)\b',
        r'\bSS\s*(304L?|316L?|321|347|310|317L?|904L?|254SMO)\b',
        r'\bSUS\s*(304L?|316L?|321|347|310|317L?|904L?|254SMO)\b',
    ]
    
    for pattern in stainless_patterns:
        match = re.search(pattern, desc_upper)
        if match:
            grade = match.group(1) if match.groups() else match.group(0)
            if grade.startswith(('SS', 'SUS')):
                return grade
            else:
                return f"SS{grade}"
    
    # 7. 탄소강 패턴들
    carbon_patterns = [
        r'\bSM\d+[A-Z]*\b',  # SM400, SM490 등
        r'\bSS\d+[A-Z]*\b',  # SS400, SS490 등 (스테인리스와 구분)
        r'\bS\d+C\b',       # S45C, S50C 등
    ]
    
    for pattern in carbon_patterns:
        match = re.search(pattern, desc_upper)
        if match:
            return match.group(0).strip()
    
    # 8. 기존 material_grade가 있으면 그대로 반환
    # (분류기에서 이미 처리된 경우)
    return ""

def update_full_material_grades(db, batch_size: int = 1000) -> Dict:
    """
    기존 자재들의 full_material_grade 업데이트
    
    Args:
        db: 데이터베이스 세션
        batch_size: 배치 처리 크기
    
    Returns:
        업데이트 결과 통계
    """
    from sqlalchemy import text
    
    try:
        # 전체 자재 수 조회
        count_query = text("SELECT COUNT(*) FROM materials WHERE full_material_grade IS NULL OR full_material_grade = ''")
        total_count = db.execute(count_query).scalar()
        
        logger.info(f"업데이트 대상 자재: {total_count}개")
        
        updated_count = 0
        processed_count = 0
        
        # 배치 단위로 처리
        offset = 0
        while offset < total_count:
            # 배치 조회
            select_query = text("""
                SELECT id, original_description, material_grade 
                FROM materials 
                WHERE full_material_grade IS NULL OR full_material_grade = ''
                ORDER BY id
                LIMIT :limit OFFSET :offset
            """)
            
            results = db.execute(select_query, {"limit": batch_size, "offset": offset}).fetchall()
            
            if not results:
                break
            
            # 배치 업데이트
            for material_id, original_description, current_grade in results:
                full_grade = extract_full_material_grade(original_description)
                
                # 전체 재질명이 추출되지 않으면 기존 grade 사용
                if not full_grade and current_grade:
                    full_grade = current_grade
                
                if full_grade:
                    update_query = text("""
                        UPDATE materials 
                        SET full_material_grade = :full_grade 
                        WHERE id = :material_id
                    """)
                    db.execute(update_query, {
                        "full_grade": full_grade,
                        "material_id": material_id
                    })
                    updated_count += 1
                
                processed_count += 1
            
            # 배치 커밋
            db.commit()
            offset += batch_size
            
            logger.info(f"진행률: {processed_count}/{total_count} ({processed_count/total_count*100:.1f}%)")
        
        return {
            "total_processed": processed_count,
            "updated_count": updated_count,
            "success": True
        }
        
    except Exception as e:
        db.rollback()
        logger.error(f"업데이트 실패: {str(e)}")
        return {
            "total_processed": 0,
            "updated_count": 0,
            "success": False,
            "error": str(e)
        }
