"""
PIPE 시스템 공통 유틸리티

모든 PIPE 관련 서비스에서 공통으로 사용되는 함수들을 모아놓은 유틸리티 모듈
"""

import logging
import math
from typing import Dict, List, Optional, Any, Tuple
from decimal import Decimal
from sqlalchemy.orm import Session
from sqlalchemy import text

logger = logging.getLogger(__name__)


# ========== PIPE 상수 정의 ==========

class PipeConstants:
    """PIPE 시스템에서 사용되는 상수들"""
    
    # 길이 관련
    STANDARD_PIPE_LENGTH_MM = 6000  # 표준 파이프 길이 (6M)
    CUTTING_LOSS_PER_CUT_MM = 2     # 절단당 손실 (2mm)
    
    # 분류 관련
    PIPE_CATEGORY = "PIPE"
    
    # 끝단 처리 타입
    END_PREPARATION_TYPES = {
        "무개선": "PLAIN",
        "한개선": "SINGLE_BEVEL", 
        "양개선": "DOUBLE_BEVEL"
    }
    
    # 상태 관련
    REVISION_TYPES = {
        "NO_REVISION": "no_revision",
        "PRE_CUTTING_PLAN": "pre_cutting_plan", 
        "POST_CUTTING_PLAN": "post_cutting_plan"
    }
    
    CHANGE_TYPES = {
        "ADDED": "added",
        "REMOVED": "removed", 
        "MODIFIED": "modified",
        "UNCHANGED": "unchanged"
    }


# ========== 데이터 추출 유틸리티 ==========

class PipeDataExtractor:
    """PIPE 데이터 추출 관련 유틸리티"""
    
    @staticmethod
    def extract_pipe_materials_from_file(db: Session, file_id: int) -> List[Dict[str, Any]]:
        """
        파일에서 PIPE 자재 데이터 추출
        
        Args:
            db: 데이터베이스 세션
            file_id: 파일 ID
            
        Returns:
            PIPE 자재 리스트
        """
        try:
            query = text("""
                SELECT 
                    m.id,
                    m.drawing_name,
                    m.line_no,
                    m.description,
                    m.classified_category,
                    m.full_material_grade,
                    m.main_nom,
                    m.red_nom,
                    m.length,
                    m.total_length,
                    m.quantity,
                    m.row_number,
                    m.original_description
                FROM materials m
                WHERE m.file_id = :file_id 
                AND m.classified_category = 'PIPE'
                AND m.is_active = true
                ORDER BY m.drawing_name, m.line_no, m.row_number
            """)
            
            result = db.execute(query, {"file_id": file_id})
            materials = []
            
            for row in result:
                materials.append({
                    "id": row.id,
                    "drawing_name": row.drawing_name or "UNKNOWN",
                    "line_no": row.line_no or "",
                    "description": row.description or "",
                    "original_description": row.original_description or "",
                    "material_grade": row.full_material_grade or "UNKNOWN",
                    "main_nom": row.main_nom or "",
                    "red_nom": row.red_nom or "",
                    "length": float(row.length or 0),
                    "total_length": float(row.total_length or 0),
                    "quantity": int(row.quantity or 1),
                    "row_number": row.row_number or 0
                })
            
            logger.info(f"✅ {len(materials)}개 PIPE 자재 추출 완료 (파일 ID: {file_id})")
            return materials
            
        except Exception as e:
            logger.error(f"❌ PIPE 자재 추출 실패: {e}")
            raise
    
    @staticmethod
    def parse_pipe_description(description: str) -> Dict[str, Any]:
        """
        PIPE 설명에서 정보 추출
        
        Args:
            description: 자재 설명
            
        Returns:
            추출된 정보 딕셔너리
        """
        # 기본값 설정
        result = {
            "material_grade": "UNKNOWN",
            "schedule": "UNKNOWN", 
            "nominal_size": "UNKNOWN",
            "length_info": None,
            "end_preparation": "무개선"
        }
        
        if not description:
            return result
        
        # 간단한 파싱 로직 (실제로는 더 복잡할 수 있음)
        description_upper = description.upper()
        
        # 재질 추출 (A106, A53 등)
        if "A106" in description_upper:
            result["material_grade"] = "A106 GR.B"
        elif "A53" in description_upper:
            result["material_grade"] = "A53 GR.B"
        
        # 스케줄 추출 (SCH40, SCH80 등)
        if "SCH40" in description_upper:
            result["schedule"] = "SCH40"
        elif "SCH80" in description_upper:
            result["schedule"] = "SCH80"
        
        # 끝단 처리 추출
        if "양개선" in description or "DOUBLE" in description_upper:
            result["end_preparation"] = "양개선"
        elif "한개선" in description or "SINGLE" in description_upper:
            result["end_preparation"] = "한개선"
        
        return result


# ========== 계산 유틸리티 ==========

class PipeCalculator:
    """PIPE 관련 계산 유틸리티"""
    
    @staticmethod
    def calculate_pipe_purchase_quantity(materials: List[Dict]) -> Dict[str, Any]:
        """
        PIPE 구매 수량 계산
        
        Args:
            materials: PIPE 자재 리스트
            
        Returns:
            계산 결과
        """
        total_bom_length = 0
        cutting_count = 0
        pipe_details = []
        
        for material in materials:
            # 길이 정보 추출 (Decimal 타입 처리)
            length_mm = float(material.get('length', 0) or 0)
            if not length_mm:
                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('description', ''),
                    'original_description': material.get('original_description', ''),
                    'drawing_name': material.get('drawing_name', ''),
                    'line_no': material.get('line_no', ''),
                    'length_mm': length_mm,
                    'quantity': quantity,
                    'total_length': total_length
                })
        
        # 절단 손실 계산
        cutting_loss = cutting_count * PipeConstants.CUTTING_LOSS_PER_CUT_MM
        
        # 총 필요 길이
        required_length = total_bom_length + cutting_loss
        
        # 6M 단위로 올림 계산
        pipes_needed = math.ceil(required_length / PipeConstants.STANDARD_PIPE_LENGTH_MM) if required_length > 0 else 0
        total_purchase_length = pipes_needed * PipeConstants.STANDARD_PIPE_LENGTH_MM
        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,
            'summary': {
                'total_materials': len(materials),
                'total_drawings': len(set(m.get('drawing_name', '') for m in materials if m.get('drawing_name'))),
                'average_length': total_bom_length / len(materials) if materials else 0
            }
        }
    
    @staticmethod
    def calculate_length_difference(old_length: float, new_length: float) -> Dict[str, Any]:
        """
        길이 변화량 계산
        
        Args:
            old_length: 이전 길이
            new_length: 새로운 길이
            
        Returns:
            변화량 정보
        """
        difference = new_length - old_length
        percentage = (difference / old_length * 100) if old_length > 0 else 0
        
        return {
            'old_length': old_length,
            'new_length': new_length,
            'difference': difference,
            'percentage': percentage,
            'change_type': 'increased' if difference > 0 else 'decreased' if difference < 0 else 'unchanged'
        }


# ========== 비교 유틸리티 ==========

class PipeComparator:
    """PIPE 데이터 비교 유틸리티"""
    
    @staticmethod
    def compare_pipe_segments(old_segments: List[Dict], new_segments: List[Dict]) -> Dict[str, Any]:
        """
        단관 데이터 비교
        
        Args:
            old_segments: 이전 단관 데이터
            new_segments: 새로운 단관 데이터
            
        Returns:
            비교 결과
        """
        # 키 생성 함수
        def create_segment_key(segment):
            return (
                segment.get('drawing_name', ''),
                segment.get('material_grade', ''),
                segment.get('length', 0),
                segment.get('end_preparation', '무개선')
            )
        
        # 기존 데이터를 키로 매핑
        old_map = {}
        for segment in old_segments:
            key = create_segment_key(segment)
            if key not in old_map:
                old_map[key] = []
            old_map[key].append(segment)
        
        # 새로운 데이터를 키로 매핑
        new_map = {}
        for segment in new_segments:
            key = create_segment_key(segment)
            if key not in new_map:
                new_map[key] = []
            new_map[key].append(segment)
        
        # 비교 결과 생성
        changes = {
            'added': [],
            'removed': [],
            'modified': [],
            'unchanged': []
        }
        
        all_keys = set(old_map.keys()) | set(new_map.keys())
        
        for key in all_keys:
            old_count = len(old_map.get(key, []))
            new_count = len(new_map.get(key, []))
            
            if old_count == 0:
                # 새로 추가된 항목
                for segment in new_map[key]:
                    changes['added'].append({
                        **segment,
                        'change_type': 'added',
                        'quantity_change': new_count
                    })
            elif new_count == 0:
                # 삭제된 항목
                for segment in old_map[key]:
                    changes['removed'].append({
                        **segment,
                        'change_type': 'removed',
                        'quantity_change': -old_count
                    })
            elif old_count != new_count:
                # 수량이 변경된 항목
                base_segment = new_map[key][0] if new_map[key] else old_map[key][0]
                changes['modified'].append({
                    **base_segment,
                    'change_type': 'modified',
                    'old_quantity': old_count,
                    'new_quantity': new_count,
                    'quantity_change': new_count - old_count
                })
            else:
                # 변경되지 않은 항목
                base_segment = new_map[key][0]
                changes['unchanged'].append({
                    **base_segment,
                    'change_type': 'unchanged',
                    'quantity': old_count
                })
        
        # 통계 생성
        stats = {
            'total_old': len(old_segments),
            'total_new': len(new_segments),
            'added_count': len(changes['added']),
            'removed_count': len(changes['removed']),
            'modified_count': len(changes['modified']),
            'unchanged_count': len(changes['unchanged']),
            'changed_drawings': len(set(
                item.get('drawing_name', '') for change_list in [changes['added'], changes['removed'], changes['modified']]
                for item in change_list if item.get('drawing_name')
            ))
        }
        
        return {
            'changes': changes,
            'statistics': stats,
            'has_changes': stats['added_count'] + stats['removed_count'] + stats['modified_count'] > 0
        }


# ========== 검증 유틸리티 ==========

class PipeValidator:
    """PIPE 데이터 검증 유틸리티"""
    
    @staticmethod
    def validate_pipe_data(pipe_data: Dict[str, Any]) -> Dict[str, Any]:
        """
        PIPE 데이터 유효성 검증
        
        Args:
            pipe_data: 검증할 PIPE 데이터
            
        Returns:
            검증 결과
        """
        errors = []
        warnings = []
        
        # 필수 필드 검증
        required_fields = ['drawing_name', 'material_grade', 'length']
        for field in required_fields:
            if not pipe_data.get(field):
                errors.append(f"필수 필드 누락: {field}")
        
        # 길이 검증
        length = pipe_data.get('length', 0)
        if length <= 0:
            errors.append("길이는 0보다 커야 합니다")
        elif length > 20000:  # 20m 초과시 경고
            warnings.append(f"길이가 비정상적으로 큽니다: {length}mm")
        
        # 수량 검증
        quantity = pipe_data.get('quantity', 1)
        if quantity <= 0:
            errors.append("수량은 0보다 커야 합니다")
        
        # 도면명 검증
        drawing_name = pipe_data.get('drawing_name', '')
        if drawing_name == 'UNKNOWN':
            warnings.append("도면명이 지정되지 않았습니다")
        
        return {
            'is_valid': len(errors) == 0,
            'errors': errors,
            'warnings': warnings,
            'error_count': len(errors),
            'warning_count': len(warnings)
        }
    
    @staticmethod
    def validate_cutting_plan_data(cutting_plan: List[Dict]) -> Dict[str, Any]:
        """
        Cutting Plan 데이터 전체 검증
        
        Args:
            cutting_plan: Cutting Plan 데이터 리스트
            
        Returns:
            검증 결과
        """
        total_errors = []
        total_warnings = []
        valid_items = 0
        
        for i, item in enumerate(cutting_plan):
            validation = PipeValidator.validate_pipe_data(item)
            
            if validation['is_valid']:
                valid_items += 1
            else:
                for error in validation['errors']:
                    total_errors.append(f"항목 {i+1}: {error}")
            
            for warning in validation['warnings']:
                total_warnings.append(f"항목 {i+1}: {warning}")
        
        return {
            'is_valid': len(total_errors) == 0,
            'total_items': len(cutting_plan),
            'valid_items': valid_items,
            'invalid_items': len(cutting_plan) - valid_items,
            'errors': total_errors,
            'warnings': total_warnings,
            'validation_rate': (valid_items / len(cutting_plan) * 100) if cutting_plan else 0
        }


# ========== 포맷팅 유틸리티 ==========

class PipeFormatter:
    """PIPE 데이터 포맷팅 유틸리티"""
    
    @staticmethod
    def format_length(length_mm: float, unit: str = 'mm') -> str:
        """
        길이 포맷팅
        
        Args:
            length_mm: 길이 (mm)
            unit: 표시 단위
            
        Returns:
            포맷된 길이 문자열
        """
        if unit == 'm':
            return f"{length_mm / 1000:.3f}m"
        elif unit == 'mm':
            return f"{length_mm:.0f}mm"
        else:
            return f"{length_mm}"
    
    @staticmethod
    def format_pipe_description(pipe_data: Dict[str, Any]) -> str:
        """
        PIPE 설명 포맷팅
        
        Args:
            pipe_data: PIPE 데이터
            
        Returns:
            포맷된 설명
        """
        parts = []
        
        if pipe_data.get('material_grade'):
            parts.append(pipe_data['material_grade'])
        
        if pipe_data.get('main_nom'):
            parts.append(f"{pipe_data['main_nom']}")
        
        if pipe_data.get('schedule'):
            parts.append(pipe_data['schedule'])
        
        if pipe_data.get('length'):
            parts.append(PipeFormatter.format_length(pipe_data['length']))
        
        return " ".join(parts) if parts else "PIPE"
    
    @staticmethod
    def format_change_summary(changes: Dict[str, List]) -> str:
        """
        변경사항 요약 포맷팅
        
        Args:
            changes: 변경사항 딕셔너리
            
        Returns:
            포맷된 요약 문자열
        """
        summary_parts = []
        
        if changes.get('added'):
            summary_parts.append(f"추가 {len(changes['added'])}개")
        
        if changes.get('removed'):
            summary_parts.append(f"삭제 {len(changes['removed'])}개")
        
        if changes.get('modified'):
            summary_parts.append(f"수정 {len(changes['modified'])}개")
        
        if not summary_parts:
            return "변경사항 없음"
        
        return ", ".join(summary_parts)


# ========== 로깅 유틸리티 ==========

class PipeLogger:
    """PIPE 시스템 전용 로거 유틸리티"""
    
    @staticmethod
    def log_pipe_operation(operation: str, job_no: str, details: Dict[str, Any] = None):
        """
        PIPE 작업 로깅
        
        Args:
            operation: 작업 유형
            job_no: 작업 번호
            details: 상세 정보
        """
        message = f"🔧 PIPE {operation} | Job: {job_no}"
        
        if details:
            detail_parts = []
            for key, value in details.items():
                detail_parts.append(f"{key}: {value}")
            message += f" | {', '.join(detail_parts)}"
        
        logger.info(message)
    
    @staticmethod
    def log_pipe_error(operation: str, job_no: str, error: Exception, context: Dict[str, Any] = None):
        """
        PIPE 오류 로깅
        
        Args:
            operation: 작업 유형
            job_no: 작업 번호
            error: 오류 객체
            context: 컨텍스트 정보
        """
        message = f"❌ PIPE {operation} 실패 | Job: {job_no} | Error: {str(error)}"
        
        if context:
            context_parts = []
            for key, value in context.items():
                context_parts.append(f"{key}: {value}")
            message += f" | Context: {', '.join(context_parts)}"
        
        logger.error(message)
