"""
구매 수량 계산 서비스
- 자재별 여유율 적용
- PIPE: 절단 손실 + 6M 단위 계산
- 기타: 최소 주문 수량 적용
"""

import math
from typing import Dict, List, Tuple
from sqlalchemy.orm import Session
from sqlalchemy import text

# 자재별 기본 여유율 (올바른 규칙으로 수정)
SAFETY_FACTORS = {
    'PIPE': 1.00,        # 0% 추가 (절단 손실은 별도 계산)
    'FITTING': 1.00,     # 0% 추가 (BOM 수량 그대로)
    'VALVE': 1.00,       # 0% 추가 (BOM 수량 그대로)
    'FLANGE': 1.00,      # 0% 추가 (BOM 수량 그대로)
    'BOLT': 1.05,        # 5% 추가 (분실율)
    'GASKET': 1.00,      # 0% 추가 (5의 배수 올림으로 처리)
    'INSTRUMENT': 1.00,  # 0% 추가 (BOM 수량 그대로)
    'DEFAULT': 1.00      # 기본 0% 추가
}

# 최소 주문 수량 (자재별) - 올바른 규칙으로 수정
MINIMUM_ORDER_QTY = {
    'PIPE': 6000,        # 6M 단위
    'FITTING': 1,        # 개별 주문 가능
    'VALVE': 1,          # 개별 주문 가능
    'FLANGE': 1,         # 개별 주문 가능
    'BOLT': 4,           # 4의 배수 단위
    'GASKET': 5,         # 5의 배수 단위
    'INSTRUMENT': 1,     # 개별 주문 가능
    'DEFAULT': 1
}

def calculate_pipe_purchase_quantity(materials: List[Dict]) -> Dict:
    """
    PIPE 구매 수량 계산
    - 각 절단마다 2mm 손실 (올바른 규칙)
    - 6,000mm (6M) 단위로 올림
    """
    total_bom_length = 0
    cutting_count = 0
    pipe_details = []
    
    for material in materials:
        # 길이 정보 추출 (Decimal 타입 처리)
        length_mm = float(material.get('length_mm', 0) or 0)
        quantity = float(material.get('quantity', 1) or 1)
        
        if length_mm > 0:
            total_length = length_mm * quantity  # 총 길이 = 단위길이 × 수량
            total_bom_length += total_length
            cutting_count += quantity  # 절단 횟수 = 수량
            pipe_details.append({
                'description': material.get('original_description', ''),
                'length_mm': length_mm,
                'quantity': quantity,
                'total_length': total_length
            })
    
    # 절단 손실 계산 (각 절단마다 2mm - 올바른 규칙)
    cutting_loss = cutting_count * 2
    
    # 총 필요 길이 = BOM 길이 + 절단 손실
    required_length = total_bom_length + cutting_loss
    
    # 6M 단위로 올림 계산
    standard_length = 6000  # 6M = 6,000mm
    pipes_needed = math.ceil(required_length / standard_length) if required_length > 0 else 0
    total_purchase_length = pipes_needed * standard_length
    waste_length = total_purchase_length - required_length if pipes_needed > 0 else 0
    
    return {
        'bom_quantity': total_bom_length,
        'cutting_count': cutting_count,
        'cutting_loss': cutting_loss,
        'required_length': required_length,
        'pipes_count': pipes_needed,
        'calculated_qty': total_purchase_length,
        'waste_length': waste_length,
        'utilization_rate': (required_length / total_purchase_length * 100) if total_purchase_length > 0 else 0,
        'unit': 'mm',
        'pipe_details': pipe_details
    }

def calculate_standard_purchase_quantity(category: str, bom_quantity: float, 
                                       safety_factor: float = None) -> Dict:
    """
    일반 자재 구매 수량 계산
    - 여유율 적용
    - 최소 주문 수량 적용
    """
    # 여유율 결정
    if safety_factor is None:
        safety_factor = SAFETY_FACTORS.get(category, SAFETY_FACTORS['DEFAULT'])
    
    # 1단계: 여유율 적용 (Decimal 타입 처리)
    bom_quantity = float(bom_quantity) if bom_quantity else 0.0
    safety_qty = bom_quantity * safety_factor
    
    # 2단계: 최소 주문 수량 확인
    min_order_qty = MINIMUM_ORDER_QTY.get(category, MINIMUM_ORDER_QTY['DEFAULT'])
    
    # 3단계: 최소 주문 수량과 비교하여 큰 값 선택
    calculated_qty = max(safety_qty, min_order_qty)
    
    # 4단계: 특별 처리 (올바른 규칙 적용)
    if category == 'BOLT':
        # BOLT: 5% 여유율 후 4의 배수로 올림
        calculated_qty = math.ceil(safety_qty / min_order_qty) * min_order_qty
    elif category == 'GASKET':
        # GASKET: 5의 배수로 올림 (여유율 없음)
        calculated_qty = math.ceil(bom_quantity / min_order_qty) * min_order_qty
    
    return {
        'bom_quantity': bom_quantity,
        'safety_factor': safety_factor,
        'safety_qty': safety_qty,
        'min_order_qty': min_order_qty,
        'calculated_qty': calculated_qty,
        'waste_quantity': calculated_qty - bom_quantity,
        'utilization_rate': (bom_quantity / calculated_qty * 100) if calculated_qty > 0 else 0
    }

def generate_purchase_items_from_materials(db: Session, file_id: int, 
                                         job_no: str, revision: str) -> List[Dict]:
    """
    자재 데이터로부터 구매 품목 생성
    """
    # 1. 파일의 모든 자재 조회 (상세 테이블 정보 포함)
    materials_query = text("""
        SELECT m.*, 
               pd.length_mm, pd.outer_diameter, pd.schedule, pd.material_spec as pipe_material_spec,
               fd.fitting_type, fd.connection_method as fitting_connection, fd.main_size as fitting_main_size, 
               fd.reduced_size as fitting_reduced_size, fd.material_grade as fitting_material_grade,
               vd.valve_type, vd.connection_method as valve_connection, vd.pressure_rating as valve_pressure,
               vd.size_inches as valve_size,
               fl.flange_type, fl.pressure_rating as flange_pressure, fl.size_inches as flange_size,
               gd.gasket_type, gd.gasket_subtype, gd.material_type as gasket_material, gd.filler_material, 
               gd.size_inches as gasket_size, gd.pressure_rating as gasket_pressure, gd.thickness as gasket_thickness,
               bd.bolt_type, bd.material_standard, bd.diameter as bolt_diameter, bd.length as bolt_length,
               id.instrument_type, id.connection_size as instrument_size
        FROM materials m
        LEFT JOIN pipe_details pd ON m.id = pd.material_id
        LEFT JOIN fitting_details fd ON m.id = fd.material_id  
        LEFT JOIN valve_details vd ON m.id = vd.material_id
        LEFT JOIN flange_details fl ON m.id = fl.material_id
        LEFT JOIN gasket_details gd ON m.id = gd.material_id
        LEFT JOIN bolt_details bd ON m.id = bd.material_id
        LEFT JOIN instrument_details id ON m.id = id.material_id
        WHERE m.file_id = :file_id
        ORDER BY m.classified_category, m.original_description
    """)
    
    materials = db.execute(materials_query, {"file_id": file_id}).fetchall()
    

    
    # 2. 카테고리별로 그룹핑
    grouped_materials = {}
    for material in materials:
        category = material.classified_category or 'OTHER'
        if category not in grouped_materials:
            grouped_materials[category] = []
        
        # Row 객체를 딕셔너리로 안전하게 변환
        material_dict = {
            'id': material.id,
            'file_id': material.file_id,
            'original_description': material.original_description,
            'quantity': material.quantity,
            'unit': material.unit,
            'size_spec': material.size_spec,
            'material_grade': material.material_grade,
            'classified_category': material.classified_category,
            'line_number': material.line_number,
            # PIPE 상세 정보
            'length_mm': getattr(material, 'length_mm', None),
            'outer_diameter': getattr(material, 'outer_diameter', None),
            'schedule': getattr(material, 'schedule', None),
            'pipe_material_spec': getattr(material, 'pipe_material_spec', None),
            # FITTING 상세 정보
            'fitting_type': getattr(material, 'fitting_type', None),
            'fitting_connection': getattr(material, 'fitting_connection', None),
            'fitting_main_size': getattr(material, 'fitting_main_size', None),
            'fitting_reduced_size': getattr(material, 'fitting_reduced_size', None),
            'fitting_material_grade': getattr(material, 'fitting_material_grade', None),
            # VALVE 상세 정보
            'valve_type': getattr(material, 'valve_type', None),
            'valve_connection': getattr(material, 'valve_connection', None),
            'valve_pressure': getattr(material, 'valve_pressure', None),
            'valve_size': getattr(material, 'valve_size', None),
            # FLANGE 상세 정보
            'flange_type': getattr(material, 'flange_type', None),
            'flange_pressure': getattr(material, 'flange_pressure', None),
            'flange_size': getattr(material, 'flange_size', None),
            # GASKET 상세 정보
            'gasket_type': getattr(material, 'gasket_type', None),
            'gasket_subtype': getattr(material, 'gasket_subtype', None),
            'gasket_material': getattr(material, 'gasket_material', None),
            'filler_material': getattr(material, 'filler_material', None),
            'gasket_size': getattr(material, 'gasket_size', None),
            'gasket_pressure': getattr(material, 'gasket_pressure', None),
            'gasket_thickness': getattr(material, 'gasket_thickness', None),
            # BOLT 상세 정보
            'bolt_type': getattr(material, 'bolt_type', None),
            'material_standard': getattr(material, 'material_standard', None),
            'bolt_diameter': getattr(material, 'bolt_diameter', None),
            'bolt_length': getattr(material, 'bolt_length', None),
            # INSTRUMENT 상세 정보
            'instrument_type': getattr(material, 'instrument_type', None),
            'instrument_size': getattr(material, 'instrument_size', None)
        }
        
        grouped_materials[category].append(material_dict)
    
    # 3. 각 카테고리별로 구매 품목 생성
    purchase_items = []
    
    for category, category_materials in grouped_materials.items():
        if category == 'PIPE':
            # PIPE는 재질+사이즈+스케줄별로 그룹핑
            pipe_groups = {}
            for material in category_materials:
                # 그룹핑 키 생성
                material_spec = material.get('pipe_material_spec') or material.get('material_grade', '')
                outer_diameter = material.get('outer_diameter') or material.get('main_nom', '')
                schedule = material.get('schedule', '')
                
                group_key = f"{material_spec}|{outer_diameter}|{schedule}"
                
                if group_key not in pipe_groups:
                    pipe_groups[group_key] = []
                pipe_groups[group_key].append(material)
            
            # 각 PIPE 그룹별로 구매 수량 계산
            for group_key, group_materials in pipe_groups.items():
                pipe_calc = calculate_pipe_purchase_quantity(group_materials)
                
                if pipe_calc['calculated_qty'] > 0:
                    material_spec, outer_diameter, schedule = group_key.split('|')
                    
                    # 품목 코드 생성
                    item_code = generate_item_code('PIPE', material_spec, outer_diameter, schedule)
                    
                    # 사양 생성
                    spec_parts = [f"PIPE {outer_diameter}"]
                    if schedule: spec_parts.append(schedule)
                    if material_spec: spec_parts.append(material_spec)
                    specification = ', '.join(spec_parts)
                    
                    purchase_item = {
                        'item_code': item_code,
                        'category': 'PIPE',
                        'specification': specification,
                        'material_spec': material_spec,
                        'size_spec': outer_diameter,
                        'unit': 'mm',
                        **pipe_calc,
                        'job_no': job_no,
                        'revision': revision,
                        'file_id': file_id,
                        'materials': group_materials
                    }
                    purchase_items.append(purchase_item)
        
        else:
            # 기타 자재들은 사양별로 그룹핑
            spec_groups = generate_material_specs_for_category(category_materials, category)
            
            for spec_key, spec_data in spec_groups.items():
                if spec_data['totalQuantity'] > 0:
                    # 구매 수량 계산
                    calc_result = calculate_standard_purchase_quantity(
                        category, 
                        spec_data['totalQuantity']
                    )
                    
                    # 품목 코드 생성  
                    item_code = generate_item_code(category, spec_data.get('material_spec', ''), 
                                                 spec_data.get('size_display', ''))
                    
                    purchase_item = {
                        'item_code': item_code,
                        'category': category,
                        'specification': spec_data.get('full_spec', spec_key),
                        'material_spec': spec_data.get('material_spec', ''),
                        'size_spec': spec_data.get('size_display', ''),
                        'size_fraction': spec_data.get('size_fraction', ''),
                        'surface_treatment': spec_data.get('surface_treatment', ''),
                        'special_applications': spec_data.get('special_applications', {}),
                        'unit': spec_data.get('unit', 'EA'),
                        **calc_result,
                        'job_no': job_no,
                        'revision': revision,
                        'file_id': file_id,
                        'materials': spec_data['items']
                    }
                    purchase_items.append(purchase_item)
    
    return purchase_items

def generate_material_specs_for_category(materials: List[Dict], category: str) -> Dict:
    """카테고리별 자재 사양 그룹핑 (MaterialsPage.jsx 로직과 동일)"""
    specs = {}
    
    for material in materials:
        spec_key = ''
        spec_data = {}
        
        if category == 'FITTING':
            fitting_type = material.get('fitting_type', 'FITTING')
            connection_method = material.get('fitting_connection', '')
            # 상세 테이블의 재질 정보 우선 사용
            material_spec = material.get('fitting_material_grade') or material.get('material_grade', '')
            # 상세 테이블의 사이즈 정보 사용
            main_size = material.get('fitting_main_size', '')
            reduced_size = material.get('fitting_reduced_size', '')
            
            # 사이즈 표시 생성 (축소형인 경우 main x reduced 형태)
            if main_size and reduced_size and main_size != reduced_size:
                size_display = f"{main_size} x {reduced_size}"
            else:
                size_display = main_size or material.get('size_spec', '')
            
            # 기존 분류기 방식: 피팅 타입 + 연결방식 + 압력등급
            # 예: "ELBOW, SOCKET WELD, 3000LB"
            fitting_display = fitting_type.replace('_', ' ') if fitting_type else 'FITTING'
            
            spec_parts = [fitting_display]
            
            # 연결방식 추가
            if connection_method and connection_method != 'UNKNOWN':
                connection_display = connection_method.replace('_', ' ')
                spec_parts.append(connection_display)
            
            # 압력등급 추출 (description에서)
            description = material.get('original_description', '').upper()
            import re
            pressure_match = re.search(r'(\d+)LB', description)
            if pressure_match:
                spec_parts.append(f"{pressure_match.group(1)}LB")
            
            # 스케줄 정보 추출 (니플 등에 중요)
            schedule_match = re.search(r'SCH\s*(\d+)', description)
            if schedule_match:
                spec_parts.append(f"SCH {schedule_match.group(1)}")
            
            full_spec = ', '.join(spec_parts)
            
            spec_key = f"FITTING|{full_spec}|{material_spec}|{size_display}"
            spec_data = {
                'category': 'FITTING',
                'full_spec': full_spec,
                'material_spec': material_spec,
                'size_display': size_display,
                'unit': 'EA'
            }
            
        elif category == 'VALVE':
            valve_type = material.get('valve_type', 'VALVE')
            connection_method = material.get('valve_connection', '')
            pressure_rating = material.get('valve_pressure', '')
            material_spec = material.get('material_grade', '')
            # 상세 테이블의 사이즈 정보 우선 사용
            size_display = material.get('valve_size') or material.get('size_spec', '')
            
            spec_parts = [valve_type.replace('_', ' ')]
            if connection_method: spec_parts.append(connection_method.replace('_', ' '))
            if pressure_rating: spec_parts.append(pressure_rating)
            full_spec = ', '.join(spec_parts)
            
            spec_key = f"VALVE|{full_spec}|{material_spec}|{size_display}"
            spec_data = {
                'category': 'VALVE',
                'full_spec': full_spec,
                'material_spec': material_spec,
                'size_display': size_display,
                'unit': 'EA'
            }
            
        elif category == 'FLANGE':
            flange_type = material.get('flange_type', 'FLANGE')
            pressure_rating = material.get('flange_pressure', '')
            material_spec = material.get('material_grade', '')
            # 상세 테이블의 사이즈 정보 우선 사용
            size_display = material.get('flange_size') or material.get('size_spec', '')
            
            spec_parts = [flange_type.replace('_', ' ')]
            if pressure_rating: spec_parts.append(pressure_rating)
            full_spec = ', '.join(spec_parts)
            
            spec_key = f"FLANGE|{full_spec}|{material_spec}|{size_display}"
            spec_data = {
                'category': 'FLANGE',
                'full_spec': full_spec, 
                'material_spec': material_spec,
                'size_display': size_display,
                'unit': 'EA'
            }
            
        elif category == 'BOLT':
            bolt_type = material.get('bolt_type', 'BOLT')
            material_standard = material.get('material_standard', '')
            # 상세 테이블의 사이즈 정보 우선 사용
            diameter = material.get('bolt_diameter') or material.get('size_spec', '')
            length = material.get('bolt_length', '')
            material_spec = material_standard or material.get('material_grade', '')
            
            # 기존 분류기 방식에 따른 사이즈 표시 (분수 형태)
            # 소수점을 분수로 변환 (예: 0.625 -> 5/8)
            size_display = diameter
            if diameter and '.' in diameter:
                try:
                    decimal_val = float(diameter)
                    # 일반적인 볼트 사이즈 분수 변환
                    fraction_map = {
                        0.25: "1/4\"", 0.3125: "5/16\"", 0.375: "3/8\"", 
                        0.4375: "7/16\"", 0.5: "1/2\"", 0.5625: "9/16\"",
                        0.625: "5/8\"", 0.6875: "11/16\"", 0.75: "3/4\"",
                        0.8125: "13/16\"", 0.875: "7/8\"", 1.0: "1\""
                    }
                    if decimal_val in fraction_map:
                        size_display = fraction_map[decimal_val]
                except:
                    pass
            
            # 길이 정보 포함한 사이즈 표시 (예: 5/8" x 165L)
            if length:
                # 길이에서 숫자만 추출
                import re
                length_match = re.search(r'(\d+(?:\.\d+)?)', str(length))
                if length_match:
                    length_num = length_match.group(1)
                    size_display_with_length = f"{size_display} x {length_num}L"
                else:
                    size_display_with_length = f"{size_display} x {length}"
            else:
                size_display_with_length = size_display
            
            spec_parts = [bolt_type.replace('_', ' ')]
            if material_standard: spec_parts.append(material_standard)
            full_spec = ', '.join(spec_parts)
            
            # 사이즈+길이로 그룹핑
            spec_key = f"BOLT|{full_spec}|{material_spec}|{size_display_with_length}"
            spec_data = {
                'category': 'BOLT',
                'full_spec': full_spec,
                'material_spec': material_spec,
                'size_display': size_display_with_length,
                'unit': 'EA'
            }
            
        elif category == 'GASKET':
            # 상세 테이블 정보 우선 사용
            gasket_type = material.get('gasket_type', 'GASKET')
            gasket_subtype = material.get('gasket_subtype', '')
            gasket_material = material.get('gasket_material', '')
            filler_material = material.get('filler_material', '')
            gasket_pressure = material.get('gasket_pressure', '')
            gasket_thickness = material.get('gasket_thickness', '')
            
            # 상세 테이블의 사이즈 정보 우선 사용
            size_display = material.get('gasket_size') or material.get('size_spec', '')
            
            # 기존 분류기 방식: 가스켓 타입 + 압력등급 + 재질
            # 예: "SPIRAL WOUND, 150LB, 304SS + GRAPHITE"
            spec_parts = [gasket_type.replace('_', ' ')]
            
            # 서브타입 추가 (있는 경우)
            if gasket_subtype and gasket_subtype != gasket_type:
                spec_parts.append(gasket_subtype.replace('_', ' '))
            
            # 상세 테이블의 압력등급 우선 사용, 없으면 description에서 추출
            if gasket_pressure:
                spec_parts.append(gasket_pressure)
            else:
                description = material.get('original_description', '').upper()
                import re
                pressure_match = re.search(r'(\d+)LB', description)
                if pressure_match:
                    spec_parts.append(f"{pressure_match.group(1)}LB")
            
            # 재질 정보 구성 (상세 테이블 정보 활용)
            material_spec_parts = []
            
            # SWG의 경우 메탈 + 필러 형태로 구성
            if gasket_type == 'SPIRAL_WOUND':
                # 기존 저장된 데이터가 부정확한 경우, 원본 description에서 직접 파싱
                description = material.get('original_description', '').upper()
                
                # SS304/GRAPHITE/CS/CS 패턴 파싱 (H/F/I/O 다음에 오는 재질 정보)
                import re
                material_spec = None
                
                # H/F/I/O SS304/GRAPHITE/CS/CS 패턴 (전체 구성 표시)
                hfio_material_match = re.search(r'H/F/I/O\s+([A-Z0-9]+)/([A-Z]+)/([A-Z0-9]+)/([A-Z0-9]+)', description)
                if hfio_material_match:
                    part1 = hfio_material_match.group(1)  # SS304
                    part2 = hfio_material_match.group(2)  # GRAPHITE
                    part3 = hfio_material_match.group(3)  # CS
                    part4 = hfio_material_match.group(4)  # CS
                    material_spec = f"{part1}/{part2}/{part3}/{part4}"
                else:
                    # 단순 SS304/GRAPHITE/CS/CS 패턴 (전체 구성 표시)
                    simple_material_match = re.search(r'([A-Z0-9]+)/([A-Z]+)/([A-Z0-9]+)/([A-Z0-9]+)', description)
                    if simple_material_match:
                        part1 = simple_material_match.group(1)  # SS304
                        part2 = simple_material_match.group(2)  # GRAPHITE
                        part3 = simple_material_match.group(3)  # CS
                        part4 = simple_material_match.group(4)  # CS
                        material_spec = f"{part1}/{part2}/{part3}/{part4}"
                
                if not material_spec:
                    # 상세 테이블 정보 사용
                    if gasket_material and gasket_material != 'GRAPHITE':  # 메탈 부분
                        material_spec_parts.append(gasket_material)
                    elif gasket_material == 'GRAPHITE':
                        # GRAPHITE만 있는 경우 description에서 메탈 부분 찾기
                        metal_match = re.search(r'(SS\d+|CS|INCONEL\d*)', description)
                        if metal_match:
                            material_spec_parts.append(metal_match.group(1))
                    
                    if filler_material and filler_material != gasket_material:  # 필러 부분
                        material_spec_parts.append(filler_material)
                    elif 'GRAPHITE' in description and 'GRAPHITE' not in material_spec_parts:
                        material_spec_parts.append('GRAPHITE')
                    
                    if material_spec_parts:
                        material_spec = ' + '.join(material_spec_parts)  # SS304 + GRAPHITE
                    else:
                        material_spec = material.get('material_grade', '')
            else:
                # 일반 가스켓의 경우
                if gasket_material:
                    material_spec_parts.append(gasket_material)
                if filler_material and filler_material != gasket_material:
                    material_spec_parts.append(filler_material)
                
                if material_spec_parts:
                    material_spec = ', '.join(material_spec_parts)
                else:
                    material_spec = material.get('material_grade', '')
            
            if material_spec:
                spec_parts.append(material_spec)
            
            # 두께 정보 추가 (있는 경우)
            if gasket_thickness:
                spec_parts.append(f"THK {gasket_thickness}")
            
            full_spec = ', '.join(spec_parts)
            
            spec_key = f"GASKET|{full_spec}|{material_spec}|{size_display}"
            spec_data = {
                'category': 'GASKET',
                'full_spec': full_spec,
                'material_spec': material_spec,
                'size_display': size_display,
                'unit': 'EA'
            }
            
        elif category == 'INSTRUMENT':
            instrument_type = material.get('instrument_type', 'INSTRUMENT')
            material_spec = material.get('material_grade', '')
            # 상세 테이블의 사이즈 정보 우선 사용
            size_display = material.get('instrument_size') or material.get('size_spec', '')
            
            full_spec = instrument_type.replace('_', ' ')
            
            spec_key = f"INSTRUMENT|{full_spec}|{material_spec}|{size_display}"
            spec_data = {
                'category': 'INSTRUMENT',
                'full_spec': full_spec,
                'material_spec': material_spec, 
                'size_display': size_display,
                'unit': 'EA'
            }
            
        else:
            # 기타 자재
            material_spec = material.get('material_grade', '')
            size_display = material.get('main_nom') or material.get('size_spec', '')
            
            spec_key = f"{category}|{material_spec}|{size_display}"
            spec_data = {
                'category': category,
                'full_spec': material_spec,
                'material_spec': material_spec,
                'size_display': size_display,
                'unit': 'EA'
            }
        
        # 스펙별 수량 집계
        if spec_key not in specs:
            specs[spec_key] = {
                **spec_data,
                'totalQuantity': 0,
                'count': 0,
                'items': [],
                'special_applications': {'PSV': 0, 'LT': 0, 'CK': 0} if category == 'BOLT' else None
            }
        
        specs[spec_key]['totalQuantity'] += material.get('quantity', 0)
        specs[spec_key]['count'] += 1
        specs[spec_key]['items'].append(material)
        
        # 볼트의 경우 특수 용도 정보 누적
        if category == 'BOLT' and 'special_applications' in locals():
            for app_type, count in special_applications.items():
                specs[spec_key]['special_applications'][app_type] += count
    
    return specs

def generate_item_code(category: str, material_spec: str = '', size_spec: str = '', 
                      schedule: str = '') -> str:
    """구매 품목 코드 생성"""
    import hashlib
    
    # 기본 접두사
    prefix = f"PI-{category}"
    
    # 재질 약어 생성
    material_abbr = ''
    if 'A106' in material_spec:
        material_abbr = 'A106'
    elif 'A333' in material_spec:
        material_abbr = 'A333'
    elif 'SS316' in material_spec or '316' in material_spec:
        material_abbr = 'SS316'
    elif 'A105' in material_spec:
        material_abbr = 'A105'
    elif material_spec:
        material_abbr = material_spec.replace(' ', '')[:6]
    
    # 사이즈 약어
    size_abbr = size_spec.replace('"', 'IN').replace(' ', '').replace('x', 'X')[:10]
    
    # 스케줄 (PIPE용)
    schedule_abbr = schedule.replace(' ', '')[:6]
    
    # 유니크 해시 생성 (중복 방지)
    unique_str = f"{category}|{material_spec}|{size_spec}|{schedule}"
    hash_suffix = hashlib.md5(unique_str.encode()).hexdigest()[:4].upper()
    
    # 최종 코드 조합
    code_parts = [prefix]
    if material_abbr: code_parts.append(material_abbr)
    if size_abbr: code_parts.append(size_abbr)
    if schedule_abbr: code_parts.append(schedule_abbr)
    code_parts.append(hash_suffix)
    
    return '-'.join(code_parts)

def save_purchase_items_to_db(db: Session, purchase_items: List[Dict]) -> List[int]:
    """구매 품목을 데이터베이스에 저장"""
    saved_ids = []
    
    for item in purchase_items:
        # 기존 품목 확인 (동일 사양이 있는지)
        existing_query = text("""
            SELECT id FROM purchase_items 
            WHERE job_no = :job_no AND revision = :revision AND item_code = :item_code
        """)
        existing = db.execute(existing_query, {
            'job_no': item['job_no'],
            'revision': item['revision'], 
            'item_code': item['item_code']
        }).fetchone()
        
        if existing:
            # 기존 품목 업데이트
            update_query = text("""
                UPDATE purchase_items SET
                    bom_quantity = :bom_quantity,
                    calculated_qty = :calculated_qty,
                    safety_factor = :safety_factor,
                    cutting_loss = :cutting_loss,
                    pipes_count = :pipes_count,
                    waste_length = :waste_length,
                    detailed_spec = :detailed_spec,
                    updated_at = CURRENT_TIMESTAMP
                WHERE id = :id
            """)
            db.execute(update_query, {
                'id': existing.id,
                'bom_quantity': item['bom_quantity'],
                'calculated_qty': item['calculated_qty'],
                'safety_factor': item.get('safety_factor', 1.0),
                'cutting_loss': item.get('cutting_loss', 0),
                'pipes_count': item.get('pipes_count'),
                'waste_length': item.get('waste_length'),
                'detailed_spec': item.get('detailed_spec', '{}')
            })
            saved_ids.append(existing.id)
        else:
            # 새 품목 생성
            insert_query = text("""
                INSERT INTO purchase_items (
                    item_code, category, specification, material_spec, size_spec, unit,
                    bom_quantity, safety_factor, minimum_order_qty, calculated_qty,
                    cutting_loss, standard_length, pipes_count, waste_length,
                    job_no, revision, file_id, is_active, created_by
                ) VALUES (
                    :item_code, :category, :specification, :material_spec, :size_spec, :unit,
                    :bom_quantity, :safety_factor, :minimum_order_qty, :calculated_qty,
                    :cutting_loss, :standard_length, :pipes_count, :waste_length,
                    :job_no, :revision, :file_id, :is_active, :created_by
                ) RETURNING id
            """)
            
            result = db.execute(insert_query, {
                'item_code': item['item_code'],
                'category': item['category'],
                'specification': item['specification'],
                'material_spec': item['material_spec'],
                'size_spec': item['size_spec'],
                'unit': item['unit'],
                'bom_quantity': item['bom_quantity'],
                'safety_factor': item.get('safety_factor', 1.0),
                'minimum_order_qty': item.get('min_order_qty', 0),
                'calculated_qty': item['calculated_qty'],
                'cutting_loss': item.get('cutting_loss', 0),
                'standard_length': item.get('standard_length', 6000 if item['category'] == 'PIPE' else None),
                'pipes_count': item.get('pipes_count'),
                'waste_length': item.get('waste_length'),
                'job_no': item['job_no'],
                'revision': item['revision'], 
                'file_id': item['file_id'],
                'is_active': True,
                'created_by': 'system'
            })
            
            result_row = result.fetchone()
            new_id = result_row[0] if result_row else None
            saved_ids.append(new_id)
            
            # 개별 자재와 구매 품목 연결
            for material in item['materials']:
                mapping_query = text("""
                    INSERT INTO material_purchase_mapping (material_id, purchase_item_id)
                    VALUES (:material_id, :purchase_item_id)
                    ON CONFLICT (material_id, purchase_item_id) DO NOTHING
                """)
                db.execute(mapping_query, {
                    'material_id': material['id'],
                    'purchase_item_id': new_id
                })
    
    db.commit()
    return saved_ids 