"""
파일 업로드 검증 유틸리티
보안 강화를 위한 파일 검증 로직
"""
import os
import magic
from pathlib import Path
from typing import List, Optional, Tuple
from fastapi import UploadFile, HTTPException

from ..config import get_settings
from .logger import get_logger

settings = get_settings()
logger = get_logger(__name__)


class FileValidator:
    """파일 업로드 검증 클래스"""
    
    def __init__(self):
        self.max_file_size = settings.security.max_file_size
        self.allowed_extensions = settings.security.allowed_file_extensions
        
        # MIME 타입 매핑
        self.mime_type_mapping = {
            '.xlsx': [
                'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
                'application/octet-stream'  # 일부 브라우저에서 xlsx를 이렇게 인식
            ],
            '.xls': [
                'application/vnd.ms-excel',
                'application/octet-stream'
            ],
            '.csv': [
                'text/csv',
                'text/plain',
                'application/csv'
            ]
        }
    
    def validate_file_extension(self, filename: str) -> bool:
        """파일 확장자 검증"""
        file_ext = Path(filename).suffix.lower()
        is_valid = file_ext in self.allowed_extensions
        
        if not is_valid:
            logger.warning(f"허용되지 않은 파일 확장자: {file_ext}, 파일: {filename}")
        
        return is_valid
    
    def validate_file_size(self, file_size: int) -> bool:
        """파일 크기 검증"""
        is_valid = file_size <= self.max_file_size
        
        if not is_valid:
            logger.warning(f"파일 크기 초과: {file_size} bytes (최대: {self.max_file_size} bytes)")
        
        return is_valid
    
    def validate_filename(self, filename: str) -> bool:
        """파일명 검증 (보안 위험 문자 체크)"""
        # 위험한 문자들
        dangerous_chars = ['..', '/', '\\', ':', '*', '?', '"', '<', '>', '|']
        
        for char in dangerous_chars:
            if char in filename:
                logger.warning(f"위험한 문자 포함된 파일명: {filename}")
                return False
        
        # 파일명 길이 체크 (255자 제한)
        if len(filename) > 255:
            logger.warning(f"파일명이 너무 긺: {len(filename)} 문자")
            return False
        
        return True
    
    def validate_mime_type(self, file_content: bytes, filename: str) -> bool:
        """MIME 타입 검증 (파일 내용 기반)"""
        try:
            # python-magic을 사용한 MIME 타입 검증
            detected_mime = magic.from_buffer(file_content, mime=True)
            file_ext = Path(filename).suffix.lower()
            
            expected_mimes = self.mime_type_mapping.get(file_ext, [])
            
            if detected_mime in expected_mimes:
                return True
            
            logger.warning(f"MIME 타입 불일치 - 파일: {filename}, 감지된 타입: {detected_mime}, 예상 타입: {expected_mimes}")
            return False
            
        except Exception as e:
            logger.error(f"MIME 타입 검증 실패: {e}")
            # magic 라이브러리 오류 시 확장자 검증으로 대체
            return self.validate_file_extension(filename)
    
    def sanitize_filename(self, filename: str) -> str:
        """파일명 정화 (안전한 파일명으로 변환)"""
        # 위험한 문자들을 언더스코어로 대체
        dangerous_chars = ['..', '/', '\\', ':', '*', '?', '"', '<', '>', '|']
        sanitized = filename
        
        for char in dangerous_chars:
            sanitized = sanitized.replace(char, '_')
        
        # 연속된 언더스코어 제거
        while '__' in sanitized:
            sanitized = sanitized.replace('__', '_')
        
        # 앞뒤 공백 및 점 제거
        sanitized = sanitized.strip(' .')
        
        return sanitized
    
    async def validate_upload_file(self, file: UploadFile) -> Tuple[bool, Optional[str]]:
        """
        업로드 파일 종합 검증
        
        Returns:
            Tuple[bool, Optional[str]]: (검증 성공 여부, 에러 메시지)
        """
        try:
            # 1. 파일명 검증
            if not self.validate_filename(file.filename):
                return False, f"유효하지 않은 파일명: {file.filename}"
            
            # 2. 확장자 검증
            if not self.validate_file_extension(file.filename):
                return False, f"허용되지 않은 파일 형식입니다. 허용 형식: {', '.join(self.allowed_extensions)}"
            
            # 3. 파일 내용 읽기
            file_content = await file.read()
            await file.seek(0)  # 파일 포인터 리셋
            
            # 4. 파일 크기 검증
            if not self.validate_file_size(len(file_content)):
                return False, f"파일 크기가 너무 큽니다. 최대 크기: {self.max_file_size // (1024*1024)}MB"
            
            # 5. MIME 타입 검증
            if not self.validate_mime_type(file_content, file.filename):
                return False, "파일 형식이 올바르지 않습니다."
            
            logger.info(f"파일 검증 성공: {file.filename} ({len(file_content)} bytes)")
            return True, None
            
        except Exception as e:
            logger.error(f"파일 검증 중 오류 발생: {e}", exc_info=True)
            return False, f"파일 검증 중 오류가 발생했습니다: {str(e)}"


# 전역 파일 검증기 인스턴스
file_validator = FileValidator()


async def validate_uploaded_file(file: UploadFile) -> None:
    """
    파일 검증 헬퍼 함수 (HTTPException 발생)
    
    Args:
        file: 업로드된 파일
        
    Raises:
        HTTPException: 검증 실패 시
    """
    is_valid, error_message = await file_validator.validate_upload_file(file)
    
    if not is_valid:
        raise HTTPException(status_code=400, detail=error_message)
