import os
import asyncio
import aiofiles
import logging
from urllib.parse import urlparse
import shutil
import sys
from concurrent.futures import ThreadPoolExecutor
import re
import cv2
import base64
import requests
import numpy as np
from modules.ocr_module import OCRModule
from modules.mask_module import MaskModule
from modules.text_rendering_module import TextRenderingModule
from modules.postImageManager import PostImageManager

class ImageProcessor:
    """이미지 다운로드, OCR, 번역 처리를 담당하는 클래스"""
    
    def __init__(self, logger, gpt_client, base_dir, font_path):
        self.logger = logger
        self.base_dir = base_dir
        self.gpt_client = gpt_client

        # OCR 관련
        self.inpaint_sv_port = 8080

        self.font_path = font_path
        self.TEMP_IMAGE_DIR = os.path.join(self.base_dir, "temp_images")
        os.makedirs(self.TEMP_IMAGE_DIR, exist_ok=True)

        self.ocr_module = OCRModule(logger=self.logger, base_dir=self.base_dir)
        self.mask_module = MaskModule(logger=self.logger, base_dir=self.base_dir)
        self.text_rendering_module = TextRenderingModule(logger=self.logger, font_path=self.font_path)
        self.postImageManager = PostImageManager(logger=self.logger, font_path=self.font_path)

    def __del__(self):
        """소멸자에서 리소스 정리"""
        self.cleanup()
        
    def cleanup(self):
        """리소스 정리"""
        try:
          
            # 임시 폴더 삭제
            if hasattr(self, 'TEMP_IMAGE_DIR') and os.path.exists(self.TEMP_IMAGE_DIR):
                shutil.rmtree(self.TEMP_IMAGE_DIR)
                self.logger.log(f"임시 폴더 삭제됨: {self.TEMP_IMAGE_DIR}", level=logging.INFO)
        except Exception as e:
            self.logger.log(f"리소스 정리 중 오류: {e}", level=logging.ERROR, exc_info=True)
                
    def is_valid_image_path(self, path):
        # http/https 또는 로컬 파일(.jpg, .png 등) 모두 허용
        if re.match(r'^(http|https)://.*\\.(jpg|jpeg|png|bmp|gif|webp|tiff?)(\\?.*)?$', path, re.IGNORECASE):
            return True
        if os.path.isfile(path) and path.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.webp', '.tif', '.tiff')):
            return True
        return False

    async def process_single_image(self, toggle_states, unwanted_texts, local_image_path, index, file_prefix=""):
        """
        단일 이미지를 처리합니다 (다운로드 -> OCR -> 인페인팅)
        
        Args:
            toggle_states: 토글 상태 딕셔너리
            local_image_path (str): 처리할 이미지 경로
            index (int): 이미지 인덱스
            unwanted_texts: 치환할 텍스트 딕셔너리
            file_prefix (str): 파일명에 추가할 접두사 (예: "detail", "option")
        
        Returns:
            dict: 처리 결과를 포함한 딕셔너리
                - status: 'inpainted', 'original', 'exclude', 'error' 중 하나
                - path: 처리된 이미지 파일 경로 또는 원본 이미지 파일 경로
                - error: 오류 메시지 (status가 'error'인 경우에만 포함)
        """
        ocr_enabled = toggle_states.get('ocr', False)
        unwanted_texts = unwanted_texts
        
        try:
            ocr_results = self.ocr_module.detect_text(local_image_path)

            # 3. 중국어 텍스트 없는 경우 원본 이미지 반환
            if not self.ocr_module.filter_chinese_text(ocr_results):
                self.logger.log(f"이미지 {index+1} 중국어 텍스트 없음, 원본 이미지 반환", level=logging.INFO)
                return local_image_path
            
            # 4. 텍스트 번역 (GPT)
            translated_texts = self.gpt_translate_texts(ocr_results, self.gpt_client)

            if ocr_enabled:
                filtered_translated_texts = self.process_translated_texts(translated_texts, unwanted_texts, local_image_path, index)
                if not filtered_translated_texts:
                    self.logger.log(f"이미지 {index+1} 제외됨", level=logging.INFO)
                    return None
                else:
                    self.logger.log(f"이미지 {index+1} 치환됨", level=logging.INFO)

            # 마스크 생성 (basic 방식만 사용)
            masks = self.mask_module.create_masks(
                image_path=local_image_path, ocr_results=ocr_results, mask_option="basic"
            )
            self.logger.log(f"마스크 생성 완료", level=logging.INFO)

            # 인페인팅
            inpainted_image = self.call_inpaint_api(local_image_path, masks)
            self.logger.log(f"인페인팅 완료", level=logging.INFO)
            
            # 텍스트 렌더링
            text_rendered_image = self.text_rendering_module.render_text(
                inpainted_image, ocr_results, filtered_translated_texts, font_path=self.font_path)
            self.logger.log(f"텍스트 렌더링 완료", level=logging.INFO)

            # 결과 저장
            translated_img_path = await self.postProcess_and_save_image(local_image_path, text_rendered_image, index, file_prefix, toggle_states)
            self.logger.log(f"이미지 {index+1} 번역 완료: {translated_img_path}", level=logging.INFO)
            return translated_img_path

        except Exception as e:
            self.logger.log(f"이미지 {index+1} 처리 중 오류: {e}", level=logging.ERROR, exc_info=True)
            return {'status': 'failed', 'path': local_image_path, 'error': str(e)}

    async def postProcess_and_save_image(self, local_image_path, text_rendered_image, index, file_prefix, toggle_states):
        """로컬 서버 URL을 사용해 이미지를 번역하고 로컬에 저장합니다"""
        try:
            # 파일명에 접두사 포함
            if file_prefix:
                img_path = os.path.join(self.TEMP_IMAGE_DIR, f"translated_{file_prefix}_img_{index+1}.png")
            else:
                img_path = os.path.join(self.TEMP_IMAGE_DIR, f"translated_img_{index+1}.png")

            watermarked_image_data = self.postImageManager.add_watermark(image_data=text_rendered_image, watermark_text=toggle_states.get("watermark_text", "워터마크"))
            final_image_path = self.postImageManager.save_image_to_path(watermarked_image_data, img_path)

            return final_image_path
        
        except Exception as e:
            self.logger.log(f"이미지 {index+1} 번역 처리 중 오류: {e}", level=logging.ERROR, exc_info=True)
            return local_image_path

    def is_valid_image_data(self, image_data):
        """이미지 데이터가 유효한지 확인합니다"""
        if not image_data or len(image_data) < 100:
            return False
            
        # 일반적인 이미지 파일 시그니처 확인
        image_signatures = [
            b'\xFF\xD8\xFF',  # JPEG
            b'\x89PNG\r\n\x1a\n',  # PNG
            b'GIF87a',  # GIF87a
            b'GIF89a',  # GIF89a
            b'RIFF',  # WebP (RIFF 컨테이너)
        ]
        
        return any(image_data.startswith(sig) for sig in image_signatures)

    def call_inpaint_api(self, image, mask):
        """
        인페인팅 API를 호출하여 이미지를 인페인팅합니다.
        """
        try:
            # 이미지 처리
            if isinstance(image, str):
                image_np = cv2.imread(image)
                if image_np is None:
                    self.logger.log(f"이미지 로딩 실패: {image}", level=logging.ERROR)
                    return None
            else:
                image_np = image

            # 마스크 처리
            if isinstance(mask, str):
                mask_np = cv2.imread(mask, cv2.IMREAD_GRAYSCALE)
                if mask_np is None:
                    self.logger.log(f"마스크 로딩 실패: {mask}", level=logging.ERROR)
                    return None
            else:
                mask_np = mask

            api_url = f"http://localhost:{self.inpaint_sv_port}/api/v1/inpaint"
            _, img_encoded = cv2.imencode('.png', image_np)
            _, mask_encoded = cv2.imencode('.png', mask_np)
            img_b64 = base64.b64encode(img_encoded).decode('utf-8')
            mask_b64 = base64.b64encode(mask_encoded).decode('utf-8')
            payload = {
                "image": img_b64,
                "mask": mask_b64
            }
            response = requests.post(api_url, json=payload)
            if response.status_code != 200:
                self.logger.log(f"IOPaint 서버 에러: {response.text}", level=logging.ERROR)
                return None
            nparr = np.frombuffer(response.content, np.uint8)
            result = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
            return result
        except Exception as e:
            self.logger.log(f"인페인팅 API 호출 중 오류: {e}", level=logging.ERROR, exc_info=True)
            return None


    def process_translated_texts(self, translated_texts, unwanted_texts, local_image_path, index):
        """
        번역된 단어 리스트(translated_texts)에서 unwanted_texts의 원본값이
        앞이나 뒤에 포함되면 치환값으로 바꿉니다.
        치환값이 '이미지삭제'라면 None 반환(이미지 제외)
        """

        new_texts = []
        for text in translated_texts:
            replaced = False
            for origin, replace in unwanted_texts.items():
                # 앞/뒤에 원본값이 있는지 확인
                if text.startswith(origin) or text.endswith(origin):
                    self.logger.log(f"[{text}] -> [{replace}] (치환)", level=logging.INFO)
                    if replace == "이미지삭제":
                        self.logger.log(f"이미지 {index+1} 제외됨: {local_image_path}", level=logging.INFO)
                        return None
                    # 앞/뒤 원본값만 치환
                    if text.startswith(origin):
                        new = replace + text[len(origin):]
                    elif text.endswith(origin):
                        new = text[:-len(origin)] + replace
                    new_texts.append(new)
                    replaced = True
                    break
            if not replaced:
                new_texts.append(text)

        self.logger.log(f"최종 치환 결과: {new_texts}", level=logging.INFO)
        return new_texts


    async def process_image_list(self, image_urls, delay=1.0, file_prefix="", use_inpainting=False):
        """
        이미지 리스트를 순차적으로 처리합니다.
        """
        if not image_urls:
            self.logger.log("처리할 이미지가 없습니다.", level=logging.INFO)
            return []
        
        processing_mode = "인페인팅" if use_inpainting else "웨일 번역"
        self.logger.log(f"이미지 {len(image_urls)}개를 {processing_mode} 모드로 처리 시작", level=logging.INFO)
        
        processed_images = []
        
        for i, url in enumerate(image_urls):
            self.logger.log(f"이미지 {i+1}/{len(image_urls)} 처리 중... ({processing_mode} 모드)", level=logging.INFO)
            
            result = await self.process_single_image(
                url, i, delay, file_prefix, use_inpainting
            )
            
            # 결과 처리
            if isinstance(result, dict):
                status = result.get('status')
                path = result.get('path')
                
                if status == 'inpainted':
                    processed_images.append(path)
                    self.logger.log(f"이미지 {i+1} 인페인팅 처리 완료", level=logging.INFO)
                elif status == 'original':
                    processed_images.append(path)
                    self.logger.log(f"이미지 {i+1} 원본 사용", level=logging.INFO)
                elif status == 'exclude':
                    self.logger.log(f"이미지 {i+1} 제외됨", level=logging.INFO)
                    # 제외된 이미지는 리스트에 추가하지 않음
                else:  # failed
                    self.logger.log(f"이미지 {i+1} 처리 실패: {result.get('error', '알 수 없는 오류')}", level=logging.WARNING)
                    # 실패한 이미지는 원본 경로 추가
                    processed_images.append(path)
            else:
                # 이전 버전과의 호환성을 위한 처리
                if result:
                    processed_images.append(result)
        
        self.logger.log(f"이미지 처리 완료: 총 {len(processed_images)}개 ({processing_mode} 모드)", level=logging.INFO)
        return processed_images


    def gpt_translate_texts(self, ocr_results, gpt_client):
        texts = [result['text'] for result in ocr_results]
        if not texts:
            return []
        prompt = (
            "다음 중국어 문장들을 한국어로 자연스럽고 의미가 잘 전달되게 번역해줘. "
            "순서와 개수는 반드시 그대로 유지하고, 결과는 JSON 배열(리스트)로만 반환해. "
            "중국어 리스트:\n" +
            str(texts)
        )
        response = gpt_client.ask(prompt)
        if isinstance(response, list):
            return response
        elif isinstance(response, dict) and 'result' in response:
            return response['result']
        else:
            print("GPT 번역 결과 파싱 실패, 원본 반환")
            return texts
