"""
리비전 처리 로직 서비스
구매 상태와 카테고리별 특성을 고려한 스마트 리비전 관리
"""

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

from ..models import Material, File
from ..utils.logger import get_logger
from .database_service import DatabaseService

logger = get_logger(__name__)


class RevisionLogicService:
    """리비전 처리 로직 서비스"""
    
    def __init__(self, db: Session):
        self.db = db
        self.db_service = DatabaseService(db)
    
    def process_revision_by_purchase_status(
        self,
        job_no: str,
        current_file_id: int,
        previous_file_id: Optional[int] = None
    ) -> Dict[str, Any]:
        """
        구매 상태별 리비전 처리
        
        Returns:
            {
                "needs_revision_page": bool,  # 리비전 페이지 필요 여부
                "can_use_bom_page": bool,     # 기존 BOM 페이지 사용 가능 여부
                "processing_results": dict,   # 처리 결과
                "revision_materials": list,   # 리비전 페이지에서 관리할 자재
                "inventory_materials": list,  # 재고로 분류할 자재
                "deleted_materials": list     # 삭제할 자재
            }
        """
        
        if not previous_file_id:
            previous_file_id = self._get_previous_file_id(job_no, current_file_id)
        
        if not previous_file_id:
            return self._handle_first_revision(current_file_id)
        
        # 이전/현재 자재 조회
        previous_materials = self._get_materials_with_details(previous_file_id)
        current_materials = self._get_materials_with_details(current_file_id)
        
        # 카테고리별 처리
        processing_results = {}
        revision_materials = []
        inventory_materials = []
        deleted_materials = []
        
        # 각 카테고리별로 처리
        categories = ['PIPE', 'FITTING', 'FLANGE', 'VALVE', 'GASKET', 'BOLT', 'SUPPORT', 'SPECIAL']
        
        for category in categories:
            category_result = self._process_category_revision(
                category, previous_materials, current_materials
            )
            
            processing_results[category] = category_result
            
            # 자재 분류
            revision_materials.extend(category_result['revision_materials'])
            inventory_materials.extend(category_result['inventory_materials'])
            deleted_materials.extend(category_result['deleted_materials'])
        
        return {
            "needs_revision_page": True,  # 리비전이면 항상 리비전 페이지 필요
            "can_use_bom_page": False,    # 리비전이면 기존 BOM 페이지 사용 불가
            "processing_results": processing_results,
            "revision_materials": revision_materials,
            "inventory_materials": inventory_materials,
            "deleted_materials": deleted_materials,
            "summary": self._generate_revision_summary(processing_results)
        }
    
    def _process_category_revision(
        self,
        category: str,
        previous_materials: Dict[str, Dict],
        current_materials: Dict[str, Dict]
    ) -> Dict[str, Any]:
        """카테고리별 리비전 처리"""
        
        # 카테고리별 자재 필터링
        prev_category_materials = {
            k: v for k, v in previous_materials.items() 
            if v.get('classified_category') == category
        }
        
        curr_category_materials = {
            k: v for k, v in current_materials.items() 
            if v.get('classified_category') == category
        }
        
        result = {
            "category": category,
            "revision_materials": [],
            "inventory_materials": [],
            "deleted_materials": [],
            "unchanged_materials": [],
            "processing_summary": {
                "purchased_unchanged": 0,
                "purchased_excess": 0,
                "purchased_insufficient": 0,
                "unpurchased_deleted": 0,
                "unpurchased_unchanged": 0,
                "unpurchased_updated": 0,
                "new_materials": 0
            }
        }
        
        # 이전 자재 기준으로 비교
        for key, prev_material in prev_category_materials.items():
            if key in curr_category_materials:
                curr_material = curr_category_materials[key]
                
                # GASKET, BOLT는 규칙 적용 전 수량으로 비교
                if category in ['GASKET', 'BOLT']:
                    comparison = self._compare_materials_pre_calculation(prev_material, curr_material, category)
                else:
                    comparison = self._compare_materials_standard(prev_material, curr_material, category)
                
                # 구매 완료 자재 처리
                if prev_material.get('purchase_confirmed', False):
                    processed = self._process_purchased_material(prev_material, curr_material, comparison, category)
                else:
                    # 구매 미완료 자재 처리
                    processed = self._process_unpurchased_material(prev_material, curr_material, comparison, category)
                
                # 결과 분류
                if processed['action'] == 'revision_management':
                    result['revision_materials'].append(processed)
                elif processed['action'] == 'inventory':
                    result['inventory_materials'].append(processed)
                elif processed['action'] == 'unchanged':
                    result['unchanged_materials'].append(processed)
                
                # 통계 업데이트
                result['processing_summary'][processed['summary_key']] += 1
                
            else:
                # 삭제된 자재 (현재 리비전에 없음)
                if prev_material.get('purchase_confirmed', False):
                    # 구매 완료된 자재가 삭제됨 → 재고로 분류
                    result['inventory_materials'].append({
                        'material': prev_material,
                        'action': 'inventory',
                        'reason': 'purchased_but_removed_in_revision',
                        'category': category
                    })
                else:
                    # 구매 미완료 자재가 삭제됨 → 완전 삭제
                    result['deleted_materials'].append({
                        'material': prev_material,
                        'action': 'delete',
                        'reason': 'no_longer_needed',
                        'category': category
                    })
                    result['processing_summary']['unpurchased_deleted'] += 1
        
        # 신규 자재 처리
        for key, curr_material in curr_category_materials.items():
            if key not in prev_category_materials:
                result['revision_materials'].append({
                    'material': curr_material,
                    'action': 'revision_management',
                    'reason': 'new_material',
                    'category': category,
                    'summary_key': 'new_materials'
                })
                result['processing_summary']['new_materials'] += 1
        
        return result
    
    def _process_purchased_material(
        self,
        prev_material: Dict,
        curr_material: Dict,
        comparison: Dict,
        category: str
    ) -> Dict[str, Any]:
        """구매 완료 자재 처리"""
        
        if comparison['change_type'] == 'no_change':
            # 변동 없음 → 구매 완료 상태 유지, 더 이상 관리 불필요
            return {
                'material': curr_material,
                'action': 'unchanged',
                'reason': 'purchased_no_change',
                'category': category,
                'summary_key': 'purchased_unchanged'
            }
        
        elif comparison['change_type'] == 'decreased':
            # 수량 감소/불필요 → 재고 자재로 분류
            excess_quantity = abs(comparison['quantity_change'])
            return {
                'material': prev_material,  # 이전 자재 정보 사용
                'action': 'inventory',
                'reason': 'purchased_excess',
                'category': category,
                'excess_quantity': excess_quantity,
                'current_needed': curr_material.get('quantity', 0),
                'summary_key': 'purchased_excess'
            }
        
        else:  # increased
            # 수량 부족 → 리비전 페이지에서 추가 구매 관리
            additional_needed = comparison['quantity_change']
            return {
                'material': curr_material,
                'action': 'revision_management',
                'reason': 'purchased_insufficient',
                'category': category,
                'additional_needed': additional_needed,
                'already_purchased': prev_material.get('quantity', 0),
                'summary_key': 'purchased_insufficient'
            }
    
    def _process_unpurchased_material(
        self,
        prev_material: Dict,
        curr_material: Dict,
        comparison: Dict,
        category: str
    ) -> Dict[str, Any]:
        """구매 미완료 자재 처리"""
        
        if comparison['change_type'] == 'no_change':
            # 수량 동일 → 리비전 페이지에서 구매 관리 계속
            return {
                'material': curr_material,
                'action': 'revision_management',
                'reason': 'unpurchased_unchanged',
                'category': category,
                'summary_key': 'unpurchased_unchanged'
            }
        
        else:
            # 수량 변경 → 필요 수량만큼 리비전 페이지에서 관리
            return {
                'material': curr_material,
                'action': 'revision_management',
                'reason': 'unpurchased_quantity_changed',
                'category': category,
                'quantity_change': comparison['quantity_change'],
                'previous_quantity': prev_material.get('quantity', 0),
                'summary_key': 'unpurchased_updated'
            }
    
    def _compare_materials_standard(
        self,
        prev_material: Dict,
        curr_material: Dict,
        category: str
    ) -> Dict[str, Any]:
        """표준 자재 비교 (PIPE 제외)"""
        
        prev_qty = float(prev_material.get('quantity', 0))
        curr_qty = float(curr_material.get('quantity', 0))
        
        quantity_change = curr_qty - prev_qty
        
        if abs(quantity_change) < 0.001:  # 부동소수점 오차 고려
            change_type = 'no_change'
        elif quantity_change > 0:
            change_type = 'increased'
        else:
            change_type = 'decreased'
        
        return {
            'quantity_change': quantity_change,
            'change_type': change_type,
            'previous_quantity': prev_qty,
            'current_quantity': curr_qty
        }
    
    def _compare_materials_pre_calculation(
        self,
        prev_material: Dict,
        curr_material: Dict,
        category: str
    ) -> Dict[str, Any]:
        """규칙 적용 전 수량으로 비교 (GASKET, BOLT)"""
        
        # 원본 수량 (규칙 적용 전)으로 비교
        prev_original_qty = float(prev_material.get('original_quantity', prev_material.get('quantity', 0)))
        curr_original_qty = float(curr_material.get('original_quantity', curr_material.get('quantity', 0)))
        
        quantity_change = curr_original_qty - prev_original_qty
        
        if abs(quantity_change) < 0.001:
            change_type = 'no_change'
        elif quantity_change > 0:
            change_type = 'increased'
        else:
            change_type = 'decreased'
        
        # 최종 계산된 수량도 포함
        final_prev_qty = float(prev_material.get('quantity', 0))
        final_curr_qty = float(curr_material.get('quantity', 0))
        
        return {
            'quantity_change': quantity_change,
            'change_type': change_type,
            'previous_quantity': prev_original_qty,
            'current_quantity': curr_original_qty,
            'final_previous_quantity': final_prev_qty,
            'final_current_quantity': final_curr_qty,
            'calculation_rule_applied': True
        }
    
    def _get_materials_with_details(self, file_id: int) -> Dict[str, Dict]:
        """파일의 자재를 상세 정보와 함께 조회"""
        
        query = """
            SELECT 
                m.id, m.original_description, m.classified_category,
                m.material_grade, m.schedule, m.size_spec, m.main_nom, m.red_nom,
                m.quantity, m.unit, m.length, m.drawing_name, m.line_no,
                m.purchase_confirmed, m.confirmed_quantity, m.purchase_status,
                m.material_hash, m.revision_status, m.brand, m.user_requirement,
                -- PIPE 자재 특별 키 생성
                CASE 
                    WHEN m.classified_category = 'PIPE' THEN 
                        CONCAT(m.drawing_name, '|', m.line_no, '|', COALESCE(m.length, 0))
                    ELSE 
                        m.material_hash
                END as comparison_key
            FROM materials m
            WHERE m.file_id = :file_id AND m.is_active = true
            ORDER BY m.line_number
        """
        
        result = self.db_service.execute_query(query, {"file_id": file_id})
        materials = {}
        
        for row in result.fetchall():
            row_dict = dict(row._mapping)
            comparison_key = row_dict['comparison_key']
            
            # PIPE 자재의 경우 도면-라인넘버별로 길이 합산
            if row_dict['classified_category'] == 'PIPE':
                if comparison_key in materials:
                    # 기존 자재에 길이 합산
                    materials[comparison_key]['quantity'] += row_dict['quantity']
                    materials[comparison_key]['total_length'] = (
                        materials[comparison_key].get('total_length', 0) + 
                        (row_dict['length'] or 0) * row_dict['quantity']
                    )
                else:
                    row_dict['total_length'] = (row_dict['length'] or 0) * row_dict['quantity']
                    materials[comparison_key] = row_dict
            else:
                materials[comparison_key] = row_dict
        
        return materials
    
    def _get_previous_file_id(self, job_no: str, current_file_id: int) -> Optional[int]:
        """이전 파일 ID 자동 탐지"""
        
        query = """
            SELECT id, revision
            FROM files
            WHERE job_no = :job_no AND id != :current_file_id AND is_active = true
            ORDER BY upload_date DESC
            LIMIT 1
        """
        
        result = self.db_service.execute_query(query, {
            "job_no": job_no,
            "current_file_id": current_file_id
        })
        
        row = result.fetchone()
        return row.id if row else None
    
    def _handle_first_revision(self, current_file_id: int) -> Dict[str, Any]:
        """첫 번째 리비전 처리"""
        
        materials = self._get_materials_with_details(current_file_id)
        
        return {
            "needs_revision_page": True,  # 첫 리비전은 항상 리비전 페이지 필요
            "can_use_bom_page": False,
            "processing_results": {},
            "revision_materials": [{"material": mat, "action": "revision_management", "reason": "first_revision"} for mat in materials.values()],
            "inventory_materials": [],
            "deleted_materials": [],
            "summary": {
                "is_first_revision": True,
                "total_materials": len(materials)
            }
        }
    
    def _generate_revision_summary(self, processing_results: Dict) -> Dict[str, Any]:
        """리비전 처리 요약 생성"""
        
        summary = {
            "total_categories": len(processing_results),
            "total_revision_materials": 0,
            "total_inventory_materials": 0,
            "total_deleted_materials": 0,
            "by_category": {}
        }
        
        for category, result in processing_results.items():
            summary["total_revision_materials"] += len(result['revision_materials'])
            summary["total_inventory_materials"] += len(result['inventory_materials'])
            summary["total_deleted_materials"] += len(result['deleted_materials'])
            
            summary["by_category"][category] = {
                "revision_count": len(result['revision_materials']),
                "inventory_count": len(result['inventory_materials']),
                "deleted_count": len(result['deleted_materials']),
                "processing_summary": result['processing_summary']
            }
        
        return summary
    
    def should_redirect_to_revision_page(
        self,
        job_no: str,
        current_file_id: int,
        previous_file_id: Optional[int] = None
    ) -> Tuple[bool, str]:
        """
        리비전 페이지로 리다이렉트해야 하는지 판단
        실제 변경사항이 있을 때만 리비전 페이지로 이동
        
        Returns:
            (should_redirect: bool, reason: str)
        """
        
        try:
            # 이전 파일이 있는지 확인 (리비전 여부 판단)
            if not previous_file_id:
                previous_file_id = self._get_previous_file_id(job_no, current_file_id)
            
            if not previous_file_id:
                # 첫 번째 파일 (리비전 아님) → 기존 BOM 페이지 사용
                return False, "첫 번째 BOM 파일이므로 기존 페이지에서 관리합니다."
            
            # 실제 변경사항이 있는지 확인
            processing_results = self.process_revision_by_purchase_status(
                job_no, current_file_id, previous_file_id
            )
            
            # 변경사항 통계 확인
            summary = processing_results.get('summary', {})
            total_changes = (
                summary.get('revision_materials', 0) +
                summary.get('inventory_materials', 0) +
                summary.get('deleted_materials', 0)
            )
            
            if total_changes > 0:
                # 실제 변경사항이 있으면 리비전 페이지로
                return True, f"리비전 변경사항이 감지되었습니다 (변경: {total_changes}개). 리비전 페이지에서 관리해야 합니다."
            else:
                # 변경사항이 없으면 기존 BOM 페이지 사용
                return False, "리비전 파일이지만 변경사항이 없어 기존 페이지에서 관리합니다."
            
        except Exception as e:
            logger.error(f"Failed to determine revision redirect: {e}")
            return False, "리비전 상태 확인 실패 - 기존 페이지 사용"
