"""
PIPE 이슈 관리용 스냅샷 시스템

단관 관리 DB의 특정 시점 데이터를 고정하여
이후 리비전이 발생해도 이슈 관리에 영향을 주지 않도록 함
"""

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

from ..database import get_db
from ..models import (
    PipeCuttingPlan, PipeIssueSnapshot, PipeIssueSegment,
    PipeDrawingIssue, PipeSegmentIssue
)

logger = logging.getLogger(__name__)


class PipeIssueSnapshotService:
    """PIPE 이슈 관리용 스냅샷 서비스"""
    
    def __init__(self, db: Session):
        self.db = db
    
    def create_and_lock_snapshot_on_finalize(self, job_no: str, created_by: str = "system") -> Dict[str, Any]:
        """
        Cutting Plan 확정 시 스냅샷 생성 및 즉시 잠금
        
        Args:
            job_no: 작업 번호
            created_by: 생성자
            
        Returns:
            생성된 스냅샷 정보
        """
        try:
            # 1. 기존 활성 스냅샷 확인
            existing_snapshot = self.db.query(PipeIssueSnapshot).filter(
                and_(
                    PipeIssueSnapshot.job_no == job_no,
                    PipeIssueSnapshot.is_active == True
                )
            ).first()
            
            if existing_snapshot:
                return {
                    "success": False,
                    "message": f"이미 확정된 Cutting Plan이 존재합니다: {existing_snapshot.snapshot_name}",
                    "existing_snapshot_id": existing_snapshot.id,
                    "can_manage_issues": True
                }
            
            # 2. 현재 단관 데이터 조회
            current_segments = self._get_current_cutting_plan_data(job_no)
            if not current_segments:
                return {
                    "success": False,
                    "message": "확정할 Cutting Plan 데이터가 없습니다."
                }
            
            # 3. 자동 스냅샷 이름 생성
            snapshot_name = f"Cutting Plan 확정 - {datetime.now().strftime('%Y%m%d_%H%M%S')}"
            
            # 4. 스냅샷 레코드 생성 (즉시 잠금 상태)
            snapshot = PipeIssueSnapshot(
                job_no=job_no,
                snapshot_name=snapshot_name,
                created_by=created_by,
                is_locked=True,  # 확정과 동시에 잠금
                locked_at=datetime.utcnow(),
                locked_by=created_by,
                total_segments=len(current_segments),
                total_drawings=len(set(seg["drawing_name"] for seg in current_segments))
            )
            
            self.db.add(snapshot)
            self.db.flush()  # ID 생성을 위해
            
            # 4. 단관 데이터 스냅샷 저장
            snapshot_segments = []
            for segment_data in current_segments:
                segment = PipeIssueSegment(
                    snapshot_id=snapshot.id,
                    area=segment_data.get("area"),
                    drawing_name=segment_data["drawing_name"],
                    line_no=segment_data["line_no"],
                    material_grade=segment_data.get("material_grade"),
                    schedule_spec=segment_data.get("schedule_spec"),
                    nominal_size=segment_data.get("nominal_size"),
                    length_mm=segment_data["length_mm"],
                    end_preparation=segment_data.get("end_preparation", "무개선"),
                    original_cutting_plan_id=segment_data.get("original_id")
                )
                snapshot_segments.append(segment)
            
            self.db.add_all(snapshot_segments)
            self.db.commit()
            
            logger.info(f"Created snapshot {snapshot.id} for job {job_no} with {len(snapshot_segments)} segments")
            
            return {
                "success": True,
                "snapshot_id": snapshot.id,
                "snapshot_name": snapshot_name,
                "total_segments": len(snapshot_segments),
                "total_drawings": snapshot.total_drawings,
                "is_locked": True,
                "locked_at": snapshot.locked_at,
                "message": f"Cutting Plan이 확정되었습니다! 이제 이슈 관리를 시작할 수 있습니다.",
                "next_action": "start_issue_management"
            }
            
        except Exception as e:
            self.db.rollback()
            logger.error(f"Failed to create snapshot: {e}")
            return {
                "success": False,
                "message": f"스냅샷 생성 실패: {str(e)}"
            }
    
    def lock_snapshot(self, snapshot_id: int, locked_by: str = "system") -> Dict[str, Any]:
        """
        스냅샷 잠금 (이슈 등록 시작)
        잠금 후에는 더 이상 리비전 영향을 받지 않음
        """
        try:
            snapshot = self.db.query(PipeIssueSnapshot).filter(
                PipeIssueSnapshot.id == snapshot_id
            ).first()
            
            if not snapshot:
                return {
                    "success": False,
                    "message": "스냅샷을 찾을 수 없습니다."
                }
            
            if snapshot.is_locked:
                return {
                    "success": False,
                    "message": f"이미 잠긴 스냅샷입니다. (잠금자: {snapshot.locked_by})"
                }
            
            # 스냅샷 잠금
            snapshot.is_locked = True
            snapshot.locked_at = datetime.utcnow()
            snapshot.locked_by = locked_by
            
            self.db.commit()
            
            logger.info(f"Locked snapshot {snapshot_id} by {locked_by}")
            
            return {
                "success": True,
                "message": f"스냅샷 '{snapshot.snapshot_name}'이 잠금되었습니다. 이제 이슈 관리를 시작할 수 있습니다.",
                "locked_at": snapshot.locked_at
            }
            
        except Exception as e:
            self.db.rollback()
            logger.error(f"Failed to lock snapshot: {e}")
            return {
                "success": False,
                "message": f"스냅샷 잠금 실패: {str(e)}"
            }
    
    def get_snapshot_info(self, job_no: str) -> Dict[str, Any]:
        """작업의 스냅샷 정보 조회"""
        try:
            snapshot = self.db.query(PipeIssueSnapshot).filter(
                and_(
                    PipeIssueSnapshot.job_no == job_no,
                    PipeIssueSnapshot.is_active == True
                )
            ).first()
            
            if not snapshot:
                return {
                    "has_snapshot": False,
                    "message": "생성된 스냅샷이 없습니다."
                }
            
            # 이슈 통계 조회
            drawing_issues_count = self.db.query(PipeDrawingIssue).filter(
                PipeDrawingIssue.snapshot_id == snapshot.id
            ).count()
            
            segment_issues_count = self.db.query(PipeSegmentIssue).filter(
                PipeSegmentIssue.snapshot_id == snapshot.id
            ).count()
            
            return {
                "has_snapshot": True,
                "snapshot_id": snapshot.id,
                "snapshot_name": snapshot.snapshot_name,
                "is_locked": snapshot.is_locked,
                "created_at": snapshot.created_at,
                "created_by": snapshot.created_by,
                "locked_at": snapshot.locked_at,
                "locked_by": snapshot.locked_by,
                "total_segments": snapshot.total_segments,
                "total_drawings": snapshot.total_drawings,
                "drawing_issues_count": drawing_issues_count,
                "segment_issues_count": segment_issues_count,
                "can_start_issue_management": not snapshot.is_locked,
                "message": "잠긴 스냅샷 - 이슈 관리 진행 중" if snapshot.is_locked else "스냅샷 준비 완료"
            }
            
        except Exception as e:
            logger.error(f"Failed to get snapshot info: {e}")
            return {
                "has_snapshot": False,
                "message": f"스냅샷 정보 조회 실패: {str(e)}"
            }
    
    def get_snapshot_segments(self, snapshot_id: int, area: str = None, drawing_name: str = None) -> List[Dict[str, Any]]:
        """스냅샷된 단관 데이터 조회"""
        try:
            query = self.db.query(PipeIssueSegment).filter(
                PipeIssueSegment.snapshot_id == snapshot_id
            )
            
            if area:
                query = query.filter(PipeIssueSegment.area == area)
            
            if drawing_name:
                query = query.filter(PipeIssueSegment.drawing_name == drawing_name)
            
            segments = query.order_by(
                PipeIssueSegment.area,
                PipeIssueSegment.drawing_name,
                PipeIssueSegment.line_no
            ).all()
            
            result = []
            for segment in segments:
                result.append({
                    "id": segment.id,
                    "area": segment.area,
                    "drawing_name": segment.drawing_name,
                    "line_no": segment.line_no,
                    "material_grade": segment.material_grade,
                    "schedule_spec": segment.schedule_spec,
                    "nominal_size": segment.nominal_size,
                    "length_mm": float(segment.length_mm) if segment.length_mm else 0,
                    "end_preparation": segment.end_preparation,
                    "material_info": f"{segment.material_grade or ''} {segment.schedule_spec or ''} {segment.nominal_size or ''}".strip()
                })
            
            return result
            
        except Exception as e:
            logger.error(f"Failed to get snapshot segments: {e}")
            return []
    
    def get_available_areas(self, snapshot_id: int) -> List[str]:
        """스냅샷의 사용 가능한 구역 목록"""
        try:
            result = self.db.query(PipeIssueSegment.area).filter(
                and_(
                    PipeIssueSegment.snapshot_id == snapshot_id,
                    PipeIssueSegment.area.isnot(None)
                )
            ).distinct().all()
            
            areas = [row.area for row in result if row.area]
            return sorted(areas)
            
        except Exception as e:
            logger.error(f"Failed to get available areas: {e}")
            return []
    
    def get_available_drawings(self, snapshot_id: int, area: str = None) -> List[str]:
        """스냅샷의 사용 가능한 도면 목록"""
        try:
            query = self.db.query(PipeIssueSegment.drawing_name).filter(
                PipeIssueSegment.snapshot_id == snapshot_id
            )
            
            if area:
                query = query.filter(PipeIssueSegment.area == area)
            
            result = query.distinct().all()
            drawings = [row.drawing_name for row in result if row.drawing_name]
            return sorted(drawings)
            
        except Exception as e:
            logger.error(f"Failed to get available drawings: {e}")
            return []
    
    def _get_current_cutting_plan_data(self, job_no: str) -> List[Dict[str, Any]]:
        """현재 단관 관리 DB에서 데이터 조회"""
        try:
            cutting_plans = self.db.query(PipeCuttingPlan).filter(
                PipeCuttingPlan.job_no == job_no
            ).all()
            
            segments = []
            for plan in cutting_plans:
                segments.append({
                    "original_id": plan.id,
                    "area": plan.area,
                    "drawing_name": plan.drawing_name,
                    "line_no": plan.line_no,
                    "material_grade": plan.material_grade,
                    "schedule_spec": plan.schedule_spec,
                    "nominal_size": plan.nominal_size,
                    "length_mm": float(plan.length_mm) if plan.length_mm else 0,
                    "end_preparation": plan.end_preparation or "무개선"
                })
            
            return segments
            
        except Exception as e:
            logger.error(f"Failed to get current cutting plan data: {e}")
            return []
    
    def check_revision_protection(self, job_no: str) -> Dict[str, Any]:
        """
        리비전 보호 상태 확인
        잠긴 스냅샷이 있으면 더 이상 리비전 영향을 받지 않음
        """
        try:
            snapshot = self.db.query(PipeIssueSnapshot).filter(
                and_(
                    PipeIssueSnapshot.job_no == job_no,
                    PipeIssueSnapshot.is_active == True,
                    PipeIssueSnapshot.is_locked == True
                )
            ).first()
            
            if snapshot:
                return {
                    "is_protected": True,
                    "snapshot_id": snapshot.id,
                    "snapshot_name": snapshot.snapshot_name,
                    "locked_at": snapshot.locked_at,
                    "locked_by": snapshot.locked_by,
                    "message": f"이슈 관리가 진행 중입니다. 스냅샷 '{snapshot.snapshot_name}'이 보호되고 있습니다."
                }
            else:
                return {
                    "is_protected": False,
                    "message": "리비전 보호가 활성화되지 않았습니다."
                }
                
        except Exception as e:
            logger.error(f"Failed to check revision protection: {e}")
            return {
                "is_protected": False,
                "message": f"리비전 보호 상태 확인 실패: {str(e)}"
            }


def get_pipe_issue_snapshot_service(db: Session = None) -> PipeIssueSnapshotService:
    """PipeIssueSnapshotService 인스턴스 생성"""
    if db is None:
        db = next(get_db())
    return PipeIssueSnapshotService(db)
