"""
리비전 비교 서비스
- 기존 확정 자재와 신규 자재 비교
- 변경된 자재만 분류 처리
- 리비전 업로드 최적화
"""

from sqlalchemy.orm import Session
from sqlalchemy import text
from typing import List, Dict, Tuple, Optional
import hashlib
import logging

logger = logging.getLogger(__name__)

class RevisionComparator:
    """리비전 비교 및 차이 분석 클래스"""
    
    def __init__(self, db: Session):
        self.db = db
    
    def get_previous_confirmed_materials(self, job_no: str, current_revision: str) -> Optional[Dict]:
        """
        이전 확정된 자재 목록 조회
        
        Args:
            job_no: 프로젝트 번호
            current_revision: 현재 리비전 (예: Rev.1)
            
        Returns:
            확정된 자재 정보 딕셔너리 또는 None
        """
        try:
            # 현재 리비전 번호 추출
            current_rev_num = self._extract_revision_number(current_revision)
            
            # 이전 리비전들 중 확정된 것 찾기 (역순으로 검색)
            for prev_rev_num in range(current_rev_num - 1, -1, -1):
                prev_revision = f"Rev.{prev_rev_num}"
                
                # 해당 리비전의 확정 데이터 조회
                query = text("""
                    SELECT pc.id, pc.revision, pc.confirmed_at, pc.confirmed_by,
                           COUNT(cpi.id) as confirmed_items_count
                    FROM purchase_confirmations pc
                    LEFT JOIN confirmed_purchase_items cpi ON pc.id = cpi.confirmation_id
                    WHERE pc.job_no = :job_no 
                    AND pc.revision = :revision 
                    AND pc.is_active = TRUE
                    GROUP BY pc.id, pc.revision, pc.confirmed_at, pc.confirmed_by
                    ORDER BY pc.confirmed_at DESC
                    LIMIT 1
                """)
                
                result = self.db.execute(query, {
                    "job_no": job_no,
                    "revision": prev_revision
                }).fetchone()
                
                if result and result.confirmed_items_count > 0:
                    logger.info(f"이전 확정 자료 발견: {job_no} {prev_revision} ({result.confirmed_items_count}개 품목)")
                    
                    # 확정된 품목들 상세 조회
                    items_query = text("""
                        SELECT cpi.item_code, cpi.category, cpi.specification,
                               cpi.size, cpi.material, cpi.bom_quantity, 
                               cpi.calculated_qty, cpi.unit, cpi.safety_factor
                        FROM confirmed_purchase_items cpi
                        WHERE cpi.confirmation_id = :confirmation_id
                        ORDER BY cpi.category, cpi.specification
                    """)
                    
                    items_result = self.db.execute(items_query, {
                        "confirmation_id": result.id
                    }).fetchall()
                    
                    return {
                        "confirmation_id": result.id,
                        "revision": result.revision,
                        "confirmed_at": result.confirmed_at,
                        "confirmed_by": result.confirmed_by,
                        "items": [dict(item) for item in items_result],
                        "items_count": len(items_result)
                    }
            
            logger.info(f"이전 확정 자료 없음: {job_no} (현재: {current_revision})")
            return None
            
        except Exception as e:
            logger.error(f"이전 확정 자료 조회 실패: {str(e)}")
            return None
    
    def compare_materials(self, previous_confirmed: Dict, new_materials: List[Dict]) -> Dict:
        """
        기존 확정 자재와 신규 자재 비교
        
        Args:
            previous_confirmed: 이전 확정 자재 정보
            new_materials: 신규 업로드된 자재 목록
            
        Returns:
            비교 결과 딕셔너리
        """
        try:
            # 이전 확정 자재를 해시맵으로 변환 (빠른 검색을 위해)
            confirmed_materials = {}
            for item in previous_confirmed["items"]:
                material_hash = self._generate_material_hash(
                    item["specification"], 
                    item["size"], 
                    item["material"]
                )
                confirmed_materials[material_hash] = item
            
            # 신규 자재 분석
            unchanged_materials = []  # 변경 없음 (분류 불필요)
            changed_materials = []    # 변경됨 (재분류 필요)
            new_materials_list = []   # 신규 추가 (분류 필요)
            
            for new_material in new_materials:
                # 자재 해시 생성 (description 기반)
                description = new_material.get("description", "")
                size = self._extract_size_from_description(description)
                material = self._extract_material_from_description(description)
                
                material_hash = self._generate_material_hash(description, size, material)
                
                if material_hash in confirmed_materials:
                    confirmed_item = confirmed_materials[material_hash]
                    
                    # 수량 비교
                    new_qty = float(new_material.get("quantity", 0))
                    confirmed_qty = float(confirmed_item["bom_quantity"])
                    
                    if abs(new_qty - confirmed_qty) > 0.001:  # 수량 변경
                        changed_materials.append({
                            **new_material,
                            "change_type": "QUANTITY_CHANGED",
                            "previous_quantity": confirmed_qty,
                            "previous_item": confirmed_item
                        })
                    else:
                        # 수량 동일 - 기존 분류 결과 재사용
                        unchanged_materials.append({
                            **new_material,
                            "reuse_classification": True,
                            "previous_item": confirmed_item
                        })
                else:
                    # 신규 자재
                    new_materials_list.append({
                        **new_material,
                        "change_type": "NEW_MATERIAL"
                    })
            
            # 삭제된 자재 찾기 (이전에는 있었지만 현재는 없는 것)
            new_material_hashes = set()
            for material in new_materials:
                description = material.get("description", "")
                size = self._extract_size_from_description(description)
                material_grade = self._extract_material_from_description(description)
                hash_key = self._generate_material_hash(description, size, material_grade)
                new_material_hashes.add(hash_key)
            
            removed_materials = []
            for hash_key, confirmed_item in confirmed_materials.items():
                if hash_key not in new_material_hashes:
                    removed_materials.append({
                        "change_type": "REMOVED",
                        "previous_item": confirmed_item
                    })
            
            comparison_result = {
                "has_previous_confirmation": True,
                "previous_revision": previous_confirmed["revision"],
                "previous_confirmed_at": previous_confirmed["confirmed_at"],
                "unchanged_count": len(unchanged_materials),
                "changed_count": len(changed_materials),
                "new_count": len(new_materials_list),
                "removed_count": len(removed_materials),
                "total_materials": len(new_materials),
                "classification_needed": len(changed_materials) + len(new_materials_list),
                "unchanged_materials": unchanged_materials,
                "changed_materials": changed_materials,
                "new_materials": new_materials_list,
                "removed_materials": removed_materials
            }
            
            logger.info(f"리비전 비교 완료: 변경없음 {len(unchanged_materials)}, "
                       f"변경됨 {len(changed_materials)}, 신규 {len(new_materials_list)}, "
                       f"삭제됨 {len(removed_materials)}")
            
            return comparison_result
            
        except Exception as e:
            logger.error(f"자재 비교 실패: {str(e)}")
            raise
    
    def _extract_revision_number(self, revision: str) -> int:
        """리비전 문자열에서 숫자 추출 (Rev.1 → 1)"""
        try:
            if revision.startswith("Rev."):
                return int(revision.replace("Rev.", ""))
            return 0
        except ValueError:
            return 0
    
    def _generate_material_hash(self, description: str, size: str, material: str) -> str:
        """자재 고유성 판단을 위한 해시 생성"""
        # RULES.md의 코딩 컨벤션 준수
        hash_input = f"{description}|{size}|{material}".lower().strip()
        return hashlib.md5(hash_input.encode()).hexdigest()
    
    def _extract_size_from_description(self, description: str) -> str:
        """자재 설명에서 사이즈 정보 추출"""
        # 간단한 사이즈 패턴 추출 (실제로는 더 정교한 로직 필요)
        import re
        size_patterns = [
            r'(\d+(?:\.\d+)?)\s*(?:mm|MM|인치|inch|")',
            r'(\d+(?:\.\d+)?)\s*x\s*(\d+(?:\.\d+)?)',
            r'DN\s*(\d+)',
            r'(\d+)\s*A'
        ]
        
        for pattern in size_patterns:
            match = re.search(pattern, description, re.IGNORECASE)
            if match:
                return match.group(0)
        
        return ""
    
    def _extract_material_from_description(self, description: str) -> str:
        """자재 설명에서 재질 정보 추출"""
        # 일반적인 재질 패턴
        materials = ["SS304", "SS316", "SS316L", "A105", "WCB", "CF8M", "CF8", "CS"]
        
        description_upper = description.upper()
        for material in materials:
            if material in description_upper:
                return material
        
        return ""

def get_revision_comparison(db: Session, job_no: str, current_revision: str, 
                          new_materials: List[Dict]) -> Dict:
    """
    리비전 비교 수행 (편의 함수)
    
    Args:
        db: 데이터베이스 세션
        job_no: 프로젝트 번호  
        current_revision: 현재 리비전
        new_materials: 신규 자재 목록
        
    Returns:
        비교 결과 또는 전체 분류 필요 정보
    """
    comparator = RevisionComparator(db)
    
    # 이전 확정 자료 조회
    previous_confirmed = comparator.get_previous_confirmed_materials(job_no, current_revision)
    
    if previous_confirmed is None:
        # 이전 확정 자료가 없으면 전체 분류 필요
        return {
            "has_previous_confirmation": False,
            "classification_needed": len(new_materials),
            "all_materials_need_classification": True,
            "materials_to_classify": new_materials,
            "message": "이전 확정 자료가 없어 전체 자재를 분류합니다."
        }
    
    # 이전 확정 자료가 있으면 비교 수행
    return comparator.compare_materials(previous_confirmed, new_materials)





















