"""
강화된 리비전 관리 서비스
구매 상태 기반 리비전 비교 및 처리
"""

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 EnhancedRevisionService:
    """강화된 리비전 관리 서비스"""
    
    def __init__(self, db: Session):
        self.db = db
        self.db_service = DatabaseService(db)
    
    def compare_revisions_with_purchase_status(
        self,
        job_no: str,
        current_file_id: int,
        previous_file_id: Optional[int] = None
    ) -> Dict[str, Any]:
        """
        구매 상태를 고려한 리비전 비교
        
        Args:
            job_no: 작업 번호
            current_file_id: 현재 파일 ID
            previous_file_id: 이전 파일 ID (None이면 자동 탐지)
            
        Returns:
            비교 결과 딕셔너리
        """
        
        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_purchase_status(previous_file_id)
        
        # 현재 리비전 자재 조회
        current_materials = self._get_materials_with_purchase_status(current_file_id)
        
        # 자재별 비교 수행
        comparison_result = self._perform_detailed_comparison(
            previous_materials, current_materials, job_no
        )
        
        return comparison_result
    
    def _get_materials_with_purchase_status(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,
                -- 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
            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 _perform_detailed_comparison(
        self,
        previous_materials: Dict[str, Dict],
        current_materials: Dict[str, Dict],
        job_no: str
    ) -> Dict[str, Any]:
        """상세 비교 수행"""
        
        result = {
            "job_no": job_no,
            "comparison_date": datetime.now().isoformat(),
            "summary": {
                "total_previous": len(previous_materials),
                "total_current": len(current_materials),
                "purchased_maintained": 0,
                "purchased_increased": 0,
                "purchased_decreased": 0,
                "unpurchased_maintained": 0,
                "unpurchased_increased": 0,
                "unpurchased_decreased": 0,
                "new_materials": 0,
                "deleted_materials": 0
            },
            "changes": {
                "purchased_materials": {
                    "maintained": [],
                    "additional_purchase_needed": [],
                    "excess_inventory": []
                },
                "unpurchased_materials": {
                    "maintained": [],
                    "quantity_updated": [],
                    "quantity_reduced": []
                },
                "new_materials": [],
                "deleted_materials": []
            }
        }
        
        # 이전 자재 기준으로 비교
        for key, prev_material in previous_materials.items():
            if key in current_materials:
                curr_material = current_materials[key]
                change_info = self._analyze_material_change(prev_material, curr_material)
                
                if prev_material.get('purchase_confirmed', False):
                    # 구매 완료된 자재 처리
                    self._process_purchased_material_change(result, change_info, prev_material, curr_material)
                else:
                    # 구매 미완료 자재 처리
                    self._process_unpurchased_material_change(result, change_info, prev_material, curr_material)
            else:
                # 삭제된 자재
                result["changes"]["deleted_materials"].append({
                    "material": prev_material,
                    "reason": "removed_from_new_revision"
                })
                result["summary"]["deleted_materials"] += 1
        
        # 신규 자재 처리
        for key, curr_material in current_materials.items():
            if key not in previous_materials:
                result["changes"]["new_materials"].append({
                    "material": curr_material,
                    "action": "new_material_added"
                })
                result["summary"]["new_materials"] += 1
        
        return result
    
    def _analyze_material_change(self, prev_material: Dict, curr_material: Dict) -> Dict:
        """자재 변경 사항 분석"""
        
        prev_qty = float(prev_material.get('quantity', 0))
        curr_qty = float(curr_material.get('quantity', 0))
        
        # PIPE 자재의 경우 총 길이로 비교
        if prev_material.get('classified_category') == 'PIPE':
            prev_total = prev_material.get('total_length', 0)
            curr_total = curr_material.get('total_length', 0)
            
            return {
                "quantity_change": curr_qty - prev_qty,
                "length_change": curr_total - prev_total,
                "change_type": "length_based" if abs(curr_total - prev_total) > 0.01 else "no_change"
            }
        else:
            return {
                "quantity_change": curr_qty - prev_qty,
                "change_type": "increased" if curr_qty > prev_qty else "decreased" if curr_qty < prev_qty else "no_change"
            }
    
    def _process_purchased_material_change(
        self,
        result: Dict,
        change_info: Dict,
        prev_material: Dict,
        curr_material: Dict
    ):
        """구매 완료 자재 변경 처리"""
        
        if change_info["change_type"] == "no_change":
            result["changes"]["purchased_materials"]["maintained"].append({
                "material": curr_material,
                "action": "maintain_inventory"
            })
            result["summary"]["purchased_maintained"] += 1
            
        elif change_info["change_type"] == "increased":
            additional_qty = change_info["quantity_change"]
            result["changes"]["purchased_materials"]["additional_purchase_needed"].append({
                "material": curr_material,
                "previous_quantity": prev_material.get('quantity'),
                "current_quantity": curr_material.get('quantity'),
                "additional_needed": additional_qty,
                "action": "additional_purchase_required"
            })
            result["summary"]["purchased_increased"] += 1
            
        else:  # decreased
            excess_qty = abs(change_info["quantity_change"])
            result["changes"]["purchased_materials"]["excess_inventory"].append({
                "material": curr_material,
                "previous_quantity": prev_material.get('quantity'),
                "current_quantity": curr_material.get('quantity'),
                "excess_quantity": excess_qty,
                "action": "mark_as_excess_inventory"
            })
            result["summary"]["purchased_decreased"] += 1
    
    def _process_unpurchased_material_change(
        self,
        result: Dict,
        change_info: Dict,
        prev_material: Dict,
        curr_material: Dict
    ):
        """구매 미완료 자재 변경 처리"""
        
        if change_info["change_type"] == "no_change":
            result["changes"]["unpurchased_materials"]["maintained"].append({
                "material": curr_material,
                "action": "maintain_purchase_pending"
            })
            result["summary"]["unpurchased_maintained"] += 1
            
        elif change_info["change_type"] == "increased":
            result["changes"]["unpurchased_materials"]["quantity_updated"].append({
                "material": curr_material,
                "previous_quantity": prev_material.get('quantity'),
                "current_quantity": curr_material.get('quantity'),
                "quantity_change": change_info["quantity_change"],
                "action": "update_purchase_quantity"
            })
            result["summary"]["unpurchased_increased"] += 1
            
        else:  # decreased
            result["changes"]["unpurchased_materials"]["quantity_reduced"].append({
                "material": curr_material,
                "previous_quantity": prev_material.get('quantity'),
                "current_quantity": curr_material.get('quantity'),
                "quantity_change": change_info["quantity_change"],
                "action": "reduce_purchase_quantity"
            })
            result["summary"]["unpurchased_decreased"] += 1
    
    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_purchase_status(current_file_id)
        
        return {
            "job_no": None,
            "comparison_date": datetime.now().isoformat(),
            "is_first_revision": True,
            "summary": {
                "total_materials": len(materials),
                "all_new": True
            },
            "changes": {
                "new_materials": [{"material": mat, "action": "first_revision"} for mat in materials.values()]
            }
        }
    
    def apply_revision_changes(self, comparison_result: Dict, current_file_id: int) -> Dict[str, Any]:
        """리비전 변경사항을 DB에 적용"""
        
        try:
            # 각 변경사항별로 DB 업데이트
            updates_applied = {
                "purchased_materials": 0,
                "unpurchased_materials": 0,
                "new_materials": 0,
                "deleted_materials": 0
            }
            
            changes = comparison_result.get("changes", {})
            
            # 구매 완료 자재 처리
            purchased = changes.get("purchased_materials", {})
            for category, materials in purchased.items():
                for item in materials:
                    material = item["material"]
                    action = item["action"]
                    
                    if action == "additional_purchase_required":
                        self._mark_additional_purchase_needed(material, item)
                    elif action == "mark_as_excess_inventory":
                        self._mark_excess_inventory(material, item)
                    
                    updates_applied["purchased_materials"] += 1
            
            # 구매 미완료 자재 처리
            unpurchased = changes.get("unpurchased_materials", {})
            for category, materials in unpurchased.items():
                for item in materials:
                    material = item["material"]
                    action = item["action"]
                    
                    if action == "update_purchase_quantity":
                        self._update_purchase_quantity(material, item)
                    elif action == "reduce_purchase_quantity":
                        self._reduce_purchase_quantity(material, item)
                    
                    updates_applied["unpurchased_materials"] += 1
            
            # 신규 자재 처리
            for item in changes.get("new_materials", []):
                self._mark_new_material(item["material"])
                updates_applied["new_materials"] += 1
            
            # 삭제된 자재 처리
            for item in changes.get("deleted_materials", []):
                self._mark_deleted_material(item["material"])
                updates_applied["deleted_materials"] += 1
            
            self.db.commit()
            
            return {
                "success": True,
                "updates_applied": updates_applied,
                "message": "리비전 변경사항이 성공적으로 적용되었습니다."
            }
            
        except Exception as e:
            self.db.rollback()
            logger.error(f"Failed to apply revision changes: {e}")
            return {
                "success": False,
                "error": str(e),
                "message": "리비전 변경사항 적용 중 오류가 발생했습니다."
            }
    
    def _mark_additional_purchase_needed(self, material: Dict, change_info: Dict):
        """추가 구매 필요 표시"""
        
        update_query = """
            UPDATE materials 
            SET revision_status = 'additional_purchase_needed',
                notes = CONCAT(COALESCE(notes, ''), 
                    '\n추가 구매 필요: ', :additional_qty, ' ', unit)
            WHERE id = :material_id
        """
        
        self.db_service.execute_query(update_query, {
            "material_id": material["id"],
            "additional_qty": change_info["additional_needed"]
        })
    
    def _mark_excess_inventory(self, material: Dict, change_info: Dict):
        """잉여 재고 표시"""
        
        update_query = """
            UPDATE materials 
            SET revision_status = 'excess_inventory',
                notes = CONCAT(COALESCE(notes, ''), 
                    '\n잉여 재고: ', :excess_qty, ' ', unit)
            WHERE id = :material_id
        """
        
        self.db_service.execute_query(update_query, {
            "material_id": material["id"],
            "excess_qty": change_info["excess_quantity"]
        })
    
    def _update_purchase_quantity(self, material: Dict, change_info: Dict):
        """구매 수량 업데이트"""
        
        update_query = """
            UPDATE materials 
            SET quantity = :new_quantity,
                revision_status = 'quantity_updated',
                notes = CONCAT(COALESCE(notes, ''), 
                    '\n수량 변경: ', :prev_qty, ' → ', :new_qty)
            WHERE id = :material_id
        """
        
        self.db_service.execute_query(update_query, {
            "material_id": material["id"],
            "new_quantity": change_info["current_quantity"],
            "prev_qty": change_info["previous_quantity"],
            "new_qty": change_info["current_quantity"]
        })
    
    def _reduce_purchase_quantity(self, material: Dict, change_info: Dict):
        """구매 수량 감소"""
        
        if change_info["current_quantity"] <= 0:
            # 수량이 0 이하면 삭제 표시
            update_query = """
                UPDATE materials 
                SET revision_status = 'deleted',
                    is_active = false,
                    notes = CONCAT(COALESCE(notes, ''), '\n리비전에서 삭제됨')
                WHERE id = :material_id
            """
        else:
            # 수량만 감소
            update_query = """
                UPDATE materials 
                SET quantity = :new_quantity,
                    revision_status = 'quantity_reduced',
                    notes = CONCAT(COALESCE(notes, ''), 
                        '\n수량 감소: ', :prev_qty, ' → ', :new_qty)
                WHERE id = :material_id
            """
        
        self.db_service.execute_query(update_query, {
            "material_id": material["id"],
            "new_quantity": change_info.get("current_quantity", 0),
            "prev_qty": change_info.get("previous_quantity", 0),
            "new_qty": change_info.get("current_quantity", 0)
        })
    
    def _mark_new_material(self, material: Dict):
        """신규 자재 표시"""
        
        update_query = """
            UPDATE materials 
            SET revision_status = 'new_in_revision'
            WHERE id = :material_id
        """
        
        self.db_service.execute_query(update_query, {
            "material_id": material["id"]
        })
    
    def _mark_deleted_material(self, material: Dict):
        """삭제된 자재 표시 (이전 리비전에서)"""
        
        update_query = """
            UPDATE materials 
            SET revision_status = 'removed_in_new_revision',
                notes = CONCAT(COALESCE(notes, ''), '\n신규 리비전에서 제거됨')
            WHERE id = :material_id
        """
        
        self.db_service.execute_query(update_query, {
            "material_id": material["id"]
        })
