"""
리비전 비교 및 변경 처리 서비스
- 자재 비교 로직 (구매된/미구매 자재 구분)
- 리비전 액션 결정 (추가구매, 재고이관, 수량변경 등)
- GASKET/BOLT 특별 처리 (BOM 원본 수량 기준)
"""

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

from ..models import Material

logger = logging.getLogger(__name__)


class RevisionComparisonService:
    """리비전 비교 및 변경 처리 서비스"""
    
    def __init__(self, db: Session):
        self.db = db
    
    def compare_materials_by_category(
        self, 
        current_file_id: int, 
        previous_file_id: int, 
        category: str,
        session_id: int
    ) -> Dict[str, Any]:
        """카테고리별 자재 비교 및 변경사항 기록"""
        
        try:
            logger.info(f"카테고리 {category} 자재 비교 시작 (현재: {current_file_id}, 이전: {previous_file_id})")
            
            # 현재 파일의 자재 조회
            current_materials = self._get_materials_by_category(current_file_id, category)
            previous_materials = self._get_materials_by_category(previous_file_id, category)
            
            logger.info(f"현재 자재: {len(current_materials)}개, 이전 자재: {len(previous_materials)}개")
            
            # 자재 그룹화 (동일 자재 식별)
            current_grouped = self._group_materials_by_key(current_materials, category)
            previous_grouped = self._group_materials_by_key(previous_materials, category)
            
            # 비교 결과 저장
            comparison_results = {
                "added": [],
                "removed": [],
                "changed": [],
                "unchanged": []
            }
            
            # 현재 자재 기준으로 비교
            for key, current_group in current_grouped.items():
                if key in previous_grouped:
                    previous_group = previous_grouped[key]
                    
                    # 수량 비교 (GASKET/BOLT는 BOM 원본 수량 기준)
                    current_qty = self._get_comparison_quantity(current_group, category)
                    previous_qty = self._get_comparison_quantity(previous_group, category)
                    
                    if current_qty != previous_qty:
                        # 수량 변경됨
                        change_record = self._create_change_record(
                            current_group, previous_group, "quantity_changed", 
                            current_qty, previous_qty, category, session_id
                        )
                        comparison_results["changed"].append(change_record)
                    else:
                        # 수량 동일
                        unchanged_record = self._create_change_record(
                            current_group, previous_group, "unchanged",
                            current_qty, previous_qty, category, session_id
                        )
                        comparison_results["unchanged"].append(unchanged_record)
                else:
                    # 새로 추가된 자재
                    current_qty = self._get_comparison_quantity(current_group, category)
                    added_record = self._create_change_record(
                        current_group, None, "added",
                        current_qty, 0, category, session_id
                    )
                    comparison_results["added"].append(added_record)
            
            # 제거된 자재 확인
            for key, previous_group in previous_grouped.items():
                if key not in current_grouped:
                    previous_qty = self._get_comparison_quantity(previous_group, category)
                    removed_record = self._create_change_record(
                        None, previous_group, "removed",
                        0, previous_qty, category, session_id
                    )
                    comparison_results["removed"].append(removed_record)
            
            # DB에 변경사항 저장
            self._save_material_changes(comparison_results, session_id)
            
            # 통계 정보
            summary = {
                "category": category,
                "added_count": len(comparison_results["added"]),
                "removed_count": len(comparison_results["removed"]),
                "changed_count": len(comparison_results["changed"]),
                "unchanged_count": len(comparison_results["unchanged"]),
                "total_changes": len(comparison_results["added"]) + len(comparison_results["removed"]) + len(comparison_results["changed"])
            }
            
            logger.info(f"카테고리 {category} 비교 완료: {summary}")
            
            return {
                "summary": summary,
                "changes": comparison_results
            }
            
        except Exception as e:
            logger.error(f"카테고리 {category} 자재 비교 실패: {e}")
            raise
    
    def _get_materials_by_category(self, file_id: int, category: str) -> List[Material]:
        """파일의 특정 카테고리 자재 조회"""
        
        return self.db.query(Material).filter(
            and_(
                Material.file_id == file_id,
                Material.classified_category == category,
                Material.is_active == True
            )
        ).all()
    
    def _group_materials_by_key(self, materials: List[Material], category: str) -> Dict[str, Dict]:
        """자재를 고유 키로 그룹화"""
        
        grouped = {}
        
        for material in materials:
            # 카테고리별 고유 키 생성 전략
            if category == "PIPE":
                # PIPE: description + material_grade + main_nom
                key_parts = [
                    material.original_description.strip().upper(),
                    material.material_grade or '',
                    material.main_nom or ''
                ]
            elif category in ["GASKET", "BOLT"]:
                # GASKET/BOLT: description + main_nom (집계 공식 적용 전 원본 기준)
                key_parts = [
                    material.original_description.strip().upper(),
                    material.main_nom or ''
                ]
            else:
                # 기타: description + drawing + main_nom + red_nom
                key_parts = [
                    material.original_description.strip().upper(),
                    material.drawing_name or '',
                    material.main_nom or '',
                    material.red_nom or ''
                ]
            
            key = "|".join(key_parts)
            
            if key in grouped:
                # 동일한 자재가 있으면 수량 합산
                grouped[key]['total_quantity'] += float(material.quantity)
                grouped[key]['materials'].append(material)
                
                # 구매 확정 상태 확인 (하나라도 구매 확정되면 전체가 구매 확정)
                if getattr(material, 'purchase_confirmed', False):
                    grouped[key]['purchase_confirmed'] = True
                    grouped[key]['purchase_confirmed_at'] = getattr(material, 'purchase_confirmed_at', None)
                    
            else:
                grouped[key] = {
                    'key': key,
                    'representative_material': material,
                    'materials': [material],
                    'total_quantity': float(material.quantity),
                    'purchase_confirmed': getattr(material, 'purchase_confirmed', False),
                    'purchase_confirmed_at': getattr(material, 'purchase_confirmed_at', None),
                    'category': category
                }
        
        return grouped
    
    def _get_comparison_quantity(self, material_group: Dict, category: str) -> Decimal:
        """비교용 수량 계산 (GASKET/BOLT는 집계 공식 적용 전 원본 수량)"""
        
        if category in ["GASKET", "BOLT"]:
            # GASKET/BOLT: BOM 원본 수량 기준 (집계 공식 적용 전)
            # 실제 BOM에서 읽은 원본 수량을 사용
            original_quantity = 0
            for material in material_group['materials']:
                # classification_details에서 원본 수량 추출 시도
                details = getattr(material, 'classification_details', {})
                if isinstance(details, dict) and 'original_quantity' in details:
                    original_quantity += float(details['original_quantity'])
                else:
                    # 원본 수량 정보가 없으면 현재 수량 사용
                    original_quantity += float(material.quantity)
            
            return Decimal(str(original_quantity))
        else:
            # 기타 카테고리: 현재 수량 사용
            return Decimal(str(material_group['total_quantity']))
    
    def _create_change_record(
        self, 
        current_group: Optional[Dict], 
        previous_group: Optional[Dict],
        change_type: str,
        current_qty: Decimal,
        previous_qty: Decimal,
        category: str,
        session_id: int
    ) -> Dict[str, Any]:
        """변경 기록 생성"""
        
        # 대표 자재 정보
        if current_group:
            material = current_group['representative_material']
            material_id = material.id
            description = material.original_description
            purchase_status = 'purchased' if current_group['purchase_confirmed'] else 'not_purchased'
            purchase_confirmed_at = current_group.get('purchase_confirmed_at')
        else:
            material = previous_group['representative_material']
            material_id = None  # 제거된 자재는 현재 material_id가 없음
            description = material.original_description
            purchase_status = 'purchased' if previous_group['purchase_confirmed'] else 'not_purchased'
            purchase_confirmed_at = previous_group.get('purchase_confirmed_at')
        
        # 리비전 액션 결정
        revision_action = self._determine_revision_action(
            change_type, current_qty, previous_qty, purchase_status, category
        )
        
        return {
            "session_id": session_id,
            "material_id": material_id,
            "previous_material_id": material.id if previous_group else None,
            "material_description": description,
            "category": category,
            "change_type": change_type,
            "current_quantity": float(current_qty),
            "previous_quantity": float(previous_qty),
            "quantity_difference": float(current_qty - previous_qty),
            "purchase_status": purchase_status,
            "purchase_confirmed_at": purchase_confirmed_at,
            "revision_action": revision_action
        }
    
    def _determine_revision_action(
        self, 
        change_type: str, 
        current_qty: Decimal, 
        previous_qty: Decimal,
        purchase_status: str,
        category: str
    ) -> str:
        """리비전 액션 결정 로직"""
        
        if change_type == "added":
            return "new_material"
        elif change_type == "removed":
            if purchase_status == "purchased":
                return "inventory_transfer"  # 구매된 자재 → 재고 이관
            else:
                return "purchase_cancel"     # 미구매 자재 → 구매 취소
        elif change_type == "quantity_changed":
            quantity_diff = current_qty - previous_qty
            
            if purchase_status == "purchased":
                if quantity_diff > 0:
                    return "additional_purchase"  # 구매된 자재 수량 증가 → 추가 구매
                else:
                    return "inventory_transfer"   # 구매된 자재 수량 감소 → 재고 이관
            else:
                return "quantity_update"          # 미구매 자재 → 수량 업데이트
        else:
            return "maintain"  # 변경 없음
    
    def _save_material_changes(self, comparison_results: Dict, session_id: int):
        """변경사항을 DB에 저장"""
        
        try:
            all_changes = []
            for change_type, changes in comparison_results.items():
                all_changes.extend(changes)
            
            if not all_changes:
                return
            
            # 배치 삽입
            insert_query = """
                INSERT INTO revision_material_changes (
                    session_id, material_id, previous_material_id, material_description,
                    category, change_type, current_quantity, previous_quantity,
                    quantity_difference, purchase_status, purchase_confirmed_at, revision_action
                ) VALUES (
                    :session_id, :material_id, :previous_material_id, :material_description,
                    :category, :change_type, :current_quantity, :previous_quantity,
                    :quantity_difference, :purchase_status, :purchase_confirmed_at, :revision_action
                )
            """
            
            self.db.execute(text(insert_query), all_changes)
            self.db.commit()
            
            logger.info(f"변경사항 {len(all_changes)}건 저장 완료 (세션: {session_id})")
            
        except Exception as e:
            self.db.rollback()
            logger.error(f"변경사항 저장 실패: {e}")
            raise
    
    def get_session_changes(self, session_id: int, category: str = None) -> List[Dict[str, Any]]:
        """세션의 변경사항 조회"""
        
        try:
            query = """
                SELECT 
                    id, material_id, material_description, category,
                    change_type, current_quantity, previous_quantity, quantity_difference,
                    purchase_status, revision_action, action_status,
                    processed_by, processed_at, processing_notes
                FROM revision_material_changes 
                WHERE session_id = :session_id
            """
            params = {"session_id": session_id}
            
            if category:
                query += " AND category = :category"
                params["category"] = category
            
            query += " ORDER BY category, material_description"
            
            changes = self.db.execute(text(query), params).fetchall()
            
            return [dict(change._mapping) for change in changes]
            
        except Exception as e:
            logger.error(f"세션 변경사항 조회 실패: {e}")
            raise
    
    def process_revision_action(
        self, 
        change_id: int, 
        action: str, 
        username: str,
        notes: str = None
    ) -> Dict[str, Any]:
        """리비전 액션 처리"""
        
        try:
            # 변경사항 조회
            change = self.db.execute(text("""
                SELECT * FROM revision_material_changes WHERE id = :change_id
            """), {"change_id": change_id}).fetchone()
            
            if not change:
                raise ValueError(f"변경사항을 찾을 수 없습니다: {change_id}")
            
            result = {"success": False, "message": ""}
            
            # 액션별 처리
            if action == "additional_purchase":
                result = self._process_additional_purchase(change, username, notes)
            elif action == "inventory_transfer":
                result = self._process_inventory_transfer(change, username, notes)
            elif action == "purchase_cancel":
                result = self._process_purchase_cancel(change, username, notes)
            elif action == "quantity_update":
                result = self._process_quantity_update(change, username, notes)
            else:
                result = {"success": True, "message": "처리 완료"}
            
            # 처리 상태 업데이트
            status = "completed" if result["success"] else "failed"
            self.db.execute(text("""
                UPDATE revision_material_changes 
                SET action_status = :status, processed_by = :username, 
                    processed_at = CURRENT_TIMESTAMP, processing_notes = :notes
                WHERE id = :change_id
            """), {
                "change_id": change_id,
                "status": status,
                "username": username,
                "notes": notes or result["message"]
            })
            
            # 액션 로그 기록
            self.db.execute(text("""
                INSERT INTO revision_action_logs (
                    session_id, revision_change_id, action_type, action_description,
                    executed_by, result, result_message
                ) VALUES (
                    :session_id, :change_id, :action, :description,
                    :username, :result, :message
                )
            """), {
                "session_id": change.session_id,
                "change_id": change_id,
                "action": action,
                "description": f"{change.material_description} - {action}",
                "username": username,
                "result": "success" if result["success"] else "failed",
                "message": result["message"]
            })
            
            self.db.commit()
            
            return result
            
        except Exception as e:
            self.db.rollback()
            logger.error(f"리비전 액션 처리 실패: {e}")
            raise
    
    def _process_additional_purchase(self, change, username: str, notes: str) -> Dict[str, Any]:
        """추가 구매 처리"""
        # 구매 요청 생성 로직 구현
        return {"success": True, "message": f"추가 구매 요청 생성: {change.quantity_difference}개"}
    
    def _process_inventory_transfer(self, change, username: str, notes: str) -> Dict[str, Any]:
        """재고 이관 처리"""
        # 재고 이관 로직 구현
        try:
            self.db.execute(text("""
                INSERT INTO inventory_transfers (
                    revision_change_id, material_description, category,
                    quantity, unit, transferred_by, storage_notes
                ) VALUES (
                    :change_id, :description, :category,
                    :quantity, 'EA', :username, :notes
                )
            """), {
                "change_id": change.id,
                "description": change.material_description,
                "category": change.category,
                "quantity": abs(change.quantity_difference),
                "username": username,
                "notes": notes
            })
            
            return {"success": True, "message": f"재고 이관 완료: {abs(change.quantity_difference)}개"}
            
        except Exception as e:
            return {"success": False, "message": f"재고 이관 실패: {str(e)}"}
    
    def _process_purchase_cancel(self, change, username: str, notes: str) -> Dict[str, Any]:
        """구매 취소 처리"""
        return {"success": True, "message": "구매 취소 완료"}
    
    def _process_quantity_update(self, change, username: str, notes: str) -> Dict[str, Any]:
        """수량 업데이트 처리"""
        return {"success": True, "message": f"수량 업데이트 완료: {change.current_quantity}개"}
