"""
데이터베이스 공통 서비스 레이어
중복된 DB 쿼리 로직을 통합하고 재사용 가능한 서비스 제공
"""

from sqlalchemy.orm import Session
from sqlalchemy import text, and_, or_
from typing import List, Dict, Any, Optional, Union
from ..models import Material, File, User, Project
from ..utils.logger import get_logger
from ..utils.error_handlers import ErrorResponse

logger = get_logger(__name__)


class DatabaseService:
    """데이터베이스 공통 서비스"""
    
    def __init__(self, db: Session):
        self.db = db
    
    def execute_query(self, query: str, params: Dict = None) -> Any:
        """안전한 쿼리 실행"""
        try:
            result = self.db.execute(text(query), params or {})
            return result
        except Exception as e:
            logger.error(f"Query execution failed: {query[:100]}... Error: {e}")
            raise
    
    def get_materials_with_details(
        self, 
        file_id: Optional[int] = None,
        job_no: Optional[str] = None,
        limit: int = 1000,
        offset: int = 0,
        exclude_requested: bool = False
    ) -> Dict[str, Any]:
        """자재 상세 정보 조회 (통합된 쿼리)"""
        
        where_conditions = ["1=1"]
        params = {"limit": limit, "offset": offset}
        
        if file_id:
            where_conditions.append("m.file_id = :file_id")
            params["file_id"] = file_id
            
        if job_no:
            where_conditions.append("f.job_no = :job_no")
            params["job_no"] = job_no
            
        if exclude_requested:
            where_conditions.append("(m.purchase_confirmed IS NULL OR m.purchase_confirmed = false)")
        
        # 통합된 자재 조회 쿼리
        query = f"""
            SELECT 
                m.id, m.file_id, m.line_number, m.row_number,
                m.original_description, m.classified_category, m.classified_subcategory,
                m.material_grade, m.full_material_grade, m.schedule, m.size_spec,
                m.main_nom, m.red_nom, m.quantity, m.unit, m.length,
                m.drawing_name, m.area_code, m.line_no,
                m.classification_confidence, m.classification_details,
                m.purchase_confirmed, m.confirmed_quantity, m.purchase_status,
                m.purchase_confirmed_by, m.purchase_confirmed_at,
                m.revision_status, m.material_hash, m.normalized_description,
                m.drawing_reference, m.notes, m.created_at,
                
                -- 파일 정보
                f.filename, f.original_filename, f.job_no, f.revision, f.bom_name,
                f.upload_date, f.uploaded_by,
                
                -- 프로젝트 정보
                p.project_name, p.client_name,
                
                -- 상세 정보들 (LEFT JOIN)
                pd.material_standard as pipe_material_standard,
                pd.manufacturing_method, pd.end_preparation, pd.wall_thickness,
                
                fd.fitting_type, fd.fitting_subtype, fd.connection_type as fitting_connection_type,
                fd.main_size as fitting_main_size, fd.reduced_size as fitting_reduced_size,
                
                fld.flange_type, fld.flange_subtype, fld.pressure_rating as flange_pressure_rating,
                fld.face_type, fld.connection_method,
                
                vd.valve_type, vd.valve_subtype, vd.actuation_type,
                vd.pressure_rating as valve_pressure_rating, vd.temperature_rating,
                
                bd.bolt_type, bd.bolt_subtype, bd.thread_type, bd.head_type,
                bd.material_standard as bolt_material_standard,
                bd.pressure_rating as bolt_pressure_rating,
                
                gd.gasket_type, gd.gasket_subtype, gd.material_type as gasket_material_type,
                gd.filler_material, gd.pressure_rating as gasket_pressure_rating,
                gd.size_inches as gasket_size_inches, gd.thickness as gasket_thickness,
                gd.temperature_range as gasket_temperature_range, gd.fire_safe,
                
                -- 구매 추적 정보
                mpt.confirmed_quantity as tracking_confirmed_quantity,
                mpt.purchase_status as tracking_purchase_status,
                mpt.confirmed_by as tracking_confirmed_by,
                mpt.confirmed_at as tracking_confirmed_at,
                
                -- 최종 분류 (구매 추적 정보 우선)
                CASE 
                    WHEN mpt.id IS NOT NULL THEN 
                        CASE 
                            WHEN mpt.description LIKE '%PIPE%' OR mpt.description LIKE '%파이프%' THEN 'PIPE'
                            WHEN mpt.description LIKE '%FITTING%' OR mpt.description LIKE '%피팅%' THEN 'FITTING'
                            WHEN mpt.description LIKE '%VALVE%' OR mpt.description LIKE '%밸브%' THEN 'VALVE'
                            WHEN mpt.description LIKE '%FLANGE%' OR mpt.description LIKE '%플랜지%' THEN 'FLANGE'
                            WHEN mpt.description LIKE '%BOLT%' OR mpt.description LIKE '%볼트%' THEN 'BOLT'
                            WHEN mpt.description LIKE '%GASKET%' OR mpt.description LIKE '%가스켓%' THEN 'GASKET'
                            WHEN mpt.description LIKE '%INSTRUMENT%' OR mpt.description LIKE '%계기%' THEN 'INSTRUMENT'
                            ELSE m.classified_category
                        END
                    ELSE m.classified_category
                END as final_classified_category,
                
                -- 검증 상태
                CASE WHEN mpt.id IS NOT NULL THEN true ELSE m.is_verified END as final_is_verified,
                CASE WHEN mpt.id IS NOT NULL THEN 'purchase_calculation' ELSE m.verified_by END as final_verified_by
                
            FROM materials m
            LEFT JOIN files f ON m.file_id = f.id
            LEFT JOIN projects p ON f.project_id = p.id
            LEFT JOIN pipe_details pd ON m.id = pd.material_id
            LEFT JOIN fitting_details fd ON m.id = fd.material_id
            LEFT JOIN flange_details fld ON m.id = fld.material_id
            LEFT JOIN valve_details vd ON m.id = vd.material_id
            LEFT JOIN bolt_details bd ON m.id = bd.material_id
            LEFT JOIN gasket_details gd ON m.id = gd.material_id
            LEFT JOIN material_purchase_tracking mpt ON (
                m.material_hash = mpt.material_hash 
                AND f.job_no = mpt.job_no 
                AND f.revision = mpt.revision
            )
            WHERE {' AND '.join(where_conditions)}
            ORDER BY m.line_number ASC, m.id ASC
            LIMIT :limit OFFSET :offset
        """
        
        try:
            result = self.execute_query(query, params)
            materials = [dict(row._mapping) for row in result.fetchall()]
            
            # 총 개수 조회
            count_query = f"""
                SELECT COUNT(*) as total
                FROM materials m
                LEFT JOIN files f ON m.file_id = f.id
                WHERE {' AND '.join(where_conditions[:-1])}  -- LIMIT/OFFSET 제외
            """
            
            count_result = self.execute_query(count_query, {k: v for k, v in params.items() if k not in ['limit', 'offset']})
            total_count = count_result.scalar()
            
            return {
                "materials": materials,
                "total_count": total_count,
                "limit": limit,
                "offset": offset
            }
            
        except Exception as e:
            logger.error(f"Failed to get materials with details: {e}")
            raise
    
    def get_purchase_request_materials(
        self,
        job_no: str,
        category: Optional[str] = None,
        limit: int = 1000
    ) -> List[Dict]:
        """구매 신청 자재 조회"""
        
        where_conditions = ["f.job_no = :job_no", "m.purchase_confirmed = true"]
        params = {"job_no": job_no, "limit": limit}
        
        if category and category != 'ALL':
            where_conditions.append("m.classified_category = :category")
            params["category"] = category
        
        query = f"""
            SELECT 
                m.id, m.original_description, m.classified_category,
                m.material_grade, m.schedule, m.size_spec, m.main_nom, m.red_nom,
                CAST(m.quantity AS INTEGER) as requested_quantity,
                CAST(m.confirmed_quantity AS INTEGER) as original_quantity,
                m.unit, m.drawing_name, m.line_no,
                f.job_no, f.revision, f.bom_name
            FROM materials m
            JOIN files f ON m.file_id = f.id
            WHERE {' AND '.join(where_conditions)}
            ORDER BY m.classified_category, m.original_description
            LIMIT :limit
        """
        
        try:
            result = self.execute_query(query, params)
            return [dict(row._mapping) for row in result.fetchall()]
        except Exception as e:
            logger.error(f"Failed to get purchase request materials: {e}")
            raise
    
    def safe_delete_related_data(self, file_id: int) -> Dict[str, Any]:
        """파일 관련 데이터 안전 삭제"""
        
        deletion_results = {}
        
        # 삭제 순서 (외래키 제약 조건 고려)
        deletion_queries = [
            ("support_details", "DELETE FROM support_details WHERE file_id = :file_id"),
            ("fitting_details", "DELETE FROM fitting_details WHERE material_id IN (SELECT id FROM materials WHERE file_id = :file_id)"),
            ("flange_details", "DELETE FROM flange_details WHERE material_id IN (SELECT id FROM materials WHERE file_id = :file_id)"),
            ("valve_details", "DELETE FROM valve_details WHERE material_id IN (SELECT id FROM materials WHERE file_id = :file_id)"),
            ("gasket_details", "DELETE FROM gasket_details WHERE material_id IN (SELECT id FROM materials WHERE file_id = :file_id)"),
            ("bolt_details", "DELETE FROM bolt_details WHERE material_id IN (SELECT id FROM materials WHERE file_id = :file_id)"),
            ("instrument_details", "DELETE FROM instrument_details WHERE material_id IN (SELECT id FROM materials WHERE file_id = :file_id)"),
            ("user_requirements", "DELETE FROM user_requirements WHERE file_id = :file_id"),
            ("purchase_request_items", "DELETE FROM purchase_request_items WHERE material_id IN (SELECT id FROM materials WHERE file_id = :file_id)"),
            ("purchase_requests", "DELETE FROM purchase_requests WHERE file_id = :file_id"),
            ("user_activity_logs", "DELETE FROM user_activity_logs WHERE target_id = :file_id AND target_type = 'file'"),
            ("exported_materials", """
                DELETE FROM exported_materials 
                WHERE export_id IN (SELECT id FROM excel_exports WHERE file_id = :file_id)
            """),
            ("excel_exports", "DELETE FROM excel_exports WHERE file_id = :file_id"),
            ("materials", "DELETE FROM materials WHERE file_id = :file_id"),
        ]
        
        for table_name, query in deletion_queries:
            try:
                result = self.execute_query(query, {"file_id": file_id})
                deleted_count = result.rowcount
                deletion_results[table_name] = {"success": True, "deleted_count": deleted_count}
                logger.info(f"Deleted {deleted_count} records from {table_name}")
            except Exception as e:
                deletion_results[table_name] = {"success": False, "error": str(e)}
                logger.warning(f"Failed to delete from {table_name}: {e}")
        
        return deletion_results
    
    def bulk_insert_materials(self, materials_data: List[Dict], file_id: int) -> int:
        """자재 데이터 대량 삽입"""
        
        if not materials_data:
            return 0
        
        try:
            # 대량 삽입을 위한 쿼리 준비
            insert_query = """
                INSERT INTO materials (
                    file_id, line_number, row_number, original_description,
                    classified_category, classified_subcategory, material_grade,
                    full_material_grade, schedule, size_spec, main_nom, red_nom,
                    quantity, unit, length, drawing_name, area_code, line_no,
                    classification_confidence, classification_details,
                    revision_status, material_hash, normalized_description,
                    created_at
                ) VALUES (
                    :file_id, :line_number, :row_number, :original_description,
                    :classified_category, :classified_subcategory, :material_grade,
                    :full_material_grade, :schedule, :size_spec, :main_nom, :red_nom,
                    :quantity, :unit, :length, :drawing_name, :area_code, :line_no,
                    :classification_confidence, :classification_details,
                    :revision_status, :material_hash, :normalized_description,
                    CURRENT_TIMESTAMP
                )
            """
            
            # 데이터 준비
            insert_data = []
            for material in materials_data:
                insert_data.append({
                    "file_id": file_id,
                    "line_number": material.get("line_number"),
                    "row_number": material.get("row_number"),
                    "original_description": material.get("original_description", ""),
                    "classified_category": material.get("classified_category"),
                    "classified_subcategory": material.get("classified_subcategory"),
                    "material_grade": material.get("material_grade"),
                    "full_material_grade": material.get("full_material_grade"),
                    "schedule": material.get("schedule"),
                    "size_spec": material.get("size_spec"),
                    "main_nom": material.get("main_nom"),
                    "red_nom": material.get("red_nom"),
                    "quantity": material.get("quantity", 0),
                    "unit": material.get("unit", "EA"),
                    "length": material.get("length"),
                    "drawing_name": material.get("drawing_name"),
                    "area_code": material.get("area_code"),
                    "line_no": material.get("line_no"),
                    "classification_confidence": material.get("classification_confidence"),
                    "classification_details": material.get("classification_details"),
                    "revision_status": material.get("revision_status", "new"),
                    "material_hash": material.get("material_hash"),
                    "normalized_description": material.get("normalized_description"),
                })
            
            # 대량 삽입 실행
            self.db.execute(text(insert_query), insert_data)
            self.db.commit()
            
            logger.info(f"Successfully inserted {len(insert_data)} materials")
            return len(insert_data)
            
        except Exception as e:
            self.db.rollback()
            logger.error(f"Failed to bulk insert materials: {e}")
            raise


class MaterialQueryBuilder:
    """자재 쿼리 빌더"""
    
    @staticmethod
    def build_materials_query(
        filters: Dict[str, Any] = None,
        joins: List[str] = None,
        order_by: str = "m.line_number ASC"
    ) -> str:
        """동적 자재 쿼리 생성"""
        
        base_query = """
            SELECT m.*, f.job_no, f.revision, f.bom_name
            FROM materials m
            LEFT JOIN files f ON m.file_id = f.id
        """
        
        # 추가 조인
        if joins:
            for join in joins:
                base_query += f" {join}"
        
        # 필터 조건
        where_conditions = ["1=1"]
        if filters:
            for key, value in filters.items():
                if value is not None:
                    where_conditions.append(f"m.{key} = :{key}")
        
        if where_conditions:
            base_query += f" WHERE {' AND '.join(where_conditions)}"
        
        # 정렬
        if order_by:
            base_query += f" ORDER BY {order_by}"
        
        return base_query
