"""
캐시 서비스 - 카모두 검색 결과 캐싱 및 필터링
"""
import asyncio
import json
from datetime import datetime, timedelta
from typing import List, Optional, Dict, Any, Tuple, TYPE_CHECKING
from sqlalchemy.orm import Session
from sqlalchemy import and_

from ..models.cache import CarCache, CarDetailCache, CacheRequestQueue

if TYPE_CHECKING:
    from ..api.carmodoo import CarmodooClient

# 캐시 TTL 설정 (시간 단위)
CACHE_TTL_HOURS = 2

# 요청 큐 락
_request_lock = asyncio.Lock()
_pending_requests: Dict[str, asyncio.Event] = {}


class CacheService:
    def __init__(self, db: Session, carmodoo_client: "CarmodooClient" = None):
        self.db = db
        self.carmodoo_client = carmodoo_client

    def get_cache_key(self, maker_code: str, model_code: str) -> str:
        """캐시 키 생성"""
        return f"{maker_code}_{model_code}"

    def get_cache(self, cache_key: str) -> Optional[CarCache]:
        """캐시 조회 (만료 확인)"""
        cache = self.db.query(CarCache).filter(
            CarCache.cache_key == cache_key
        ).first()

        if cache:
            # 만료 확인
            if cache.expires_at < datetime.utcnow():
                # 만료된 캐시 삭제
                self.db.delete(cache)
                self.db.commit()
                return None
            return cache
        return None

    def save_cache(
        self,
        cache_key: str,
        maker_code: str,
        maker_name: str,
        model_code: str,
        model_name: str,
        cars: List[Dict[str, Any]]
    ) -> CarCache:
        """캐시 저장"""
        expires_at = datetime.utcnow() + timedelta(hours=CACHE_TTL_HOURS)

        # 기존 캐시 삭제
        existing = self.db.query(CarCache).filter(
            CarCache.cache_key == cache_key
        ).first()
        if existing:
            self.db.delete(existing)
            self.db.commit()

        # 새 캐시 저장
        cache = CarCache(
            cache_key=cache_key,
            maker_code=maker_code,
            maker_name=maker_name,
            model_code=model_code,
            model_name=model_name,
            total_count=len(cars),
            cars_data=json.dumps(cars, ensure_ascii=False),
            expires_at=expires_at
        )
        self.db.add(cache)
        self.db.commit()
        self.db.refresh(cache)
        return cache

    def get_cars_from_cache(self, cache: CarCache) -> List[Dict[str, Any]]:
        """캐시에서 차량 목록 가져오기"""
        return json.loads(cache.cars_data)

    def filter_cars(
        self,
        cars: List[Dict[str, Any]],
        year_min: Optional[int] = None,
        year_max: Optional[int] = None,
        mileage_min: Optional[int] = None,
        mileage_max: Optional[int] = None,
        price_min: Optional[int] = None,
        price_max: Optional[int] = None,
        fuel: Optional[str] = None,
        transmission: Optional[str] = None,
        displacement_min: Optional[int] = None,
        displacement_max: Optional[int] = None
    ) -> List[Dict[str, Any]]:
        """캐시된 데이터에서 필터링"""
        filtered = cars

        if year_min:
            filtered = [c for c in filtered if c.get('year') and c['year'] >= year_min]
        if year_max:
            filtered = [c for c in filtered if c.get('year') and c['year'] <= year_max]
        if mileage_min:
            filtered = [c for c in filtered if c.get('mileage') and c['mileage'] >= mileage_min]
        if mileage_max:
            filtered = [c for c in filtered if c.get('mileage') and c['mileage'] <= mileage_max]
        if price_min:
            # 'price' 또는 'original_price' 키 둘 다 체크 (카모두 파싱 결과는 'price', 변환 후에는 'original_price')
            filtered = [c for c in filtered if (c.get('price') or c.get('original_price')) and (c.get('price') or c.get('original_price', 0)) >= price_min]
        if price_max:
            filtered = [c for c in filtered if (c.get('price') or c.get('original_price')) and (c.get('price') or c.get('original_price', 0)) <= price_max]
        if fuel:
            # 연료 타입 매핑 (프론트엔드 값 -> 카모두 값)
            fuel_map = {
                '가솔린': ['휘발유', '가솔린'],
                '디젤': ['경유', '디젤'],
                'LPG': ['LPG'],
                '하이브리드': ['하이브리드'],
                '전기': ['전기'],
                '휘발유': ['휘발유', '가솔린'],
                '경유': ['경유', '디젤'],
            }
            allowed_fuels = fuel_map.get(fuel, [fuel])
            filtered = [c for c in filtered if c.get('fuel') in allowed_fuels]
        if transmission:
            # 변속기 타입 매핑
            trans_map = {
                '자동': ['오토', '자동'],
                '수동': ['수동'],
                '세미오토': ['세미오토'],
                'CVT': ['CVT'],
            }
            allowed_trans = trans_map.get(transmission, [transmission])
            filtered = [c for c in filtered if c.get('transmission') in allowed_trans]
        if displacement_min:
            filtered = [c for c in filtered if c.get('displacement') and c['displacement'] >= displacement_min]
        if displacement_max:
            filtered = [c for c in filtered if c.get('displacement') and c['displacement'] <= displacement_max]

        return filtered

    def paginate_cars(
        self,
        cars: List[Dict[str, Any]],
        page: int = 1,
        page_size: int = 20
    ) -> Tuple[List[Dict[str, Any]], int]:
        """페이징 처리"""
        total = len(cars)
        start = (page - 1) * page_size
        end = start + page_size
        return cars[start:end], total

    async def fetch_all_cars_for_cache(
        self,
        maker_code: str,
        model_code: str,
        maker_name: str = "",
        model_name: str = ""
    ) -> List[Dict[str, Any]]:
        """캐시용 전체 데이터 수집 (연도별 분할 검색)

        카모두 API는 페이징이 제대로 동작하지 않아 한 번에 최대 50대만 반환합니다.
        연도별로 나누어 검색하여 더 많은 차량을 수집합니다.
        """
        if not self.carmodoo_client:
            return []

        try:
            # 연도별 분할 검색 사용 (최근 15년간)
            all_cars = await self.carmodoo_client.search_cars_by_year_segment(
                maker_code=maker_code,
                model_code=model_code,
                year_start=2010,  # 2010년부터
                year_end=None  # 현재 연도까지
            )
            return all_cars

        except Exception as e:
            print(f"Error fetching cars for cache: {e}")
            return []

    async def get_or_fetch_cache(
        self,
        maker_code: str,
        model_code: str,
        maker_name: str = "",
        model_name: str = ""
    ) -> Optional[CarCache]:
        """캐시 조회 또는 새로 가져오기 (요청 병합 포함)"""
        cache_key = self.get_cache_key(maker_code, model_code)

        # 1. 캐시 확인
        cache = self.get_cache(cache_key)
        if cache:
            return cache

        # 2. 요청 락으로 동시 요청 병합
        async with _request_lock:
            # 다른 요청이 이미 처리 중인지 확인
            if cache_key in _pending_requests:
                event = _pending_requests[cache_key]
            else:
                # 새 이벤트 생성
                event = asyncio.Event()
                _pending_requests[cache_key] = event

                # 백그라운드에서 데이터 가져오기
                asyncio.create_task(
                    self._fetch_and_cache(cache_key, maker_code, model_code, maker_name, model_name, event)
                )

        # 3. 완료 대기
        await event.wait()

        # 4. 캐시 반환
        return self.get_cache(cache_key)

    async def _fetch_and_cache(
        self,
        cache_key: str,
        maker_code: str,
        model_code: str,
        maker_name: str,
        model_name: str,
        event: asyncio.Event
    ):
        """데이터 가져와서 캐시에 저장"""
        try:
            cars = await self.fetch_all_cars_for_cache(
                maker_code, model_code, maker_name, model_name
            )

            if cars:
                self.save_cache(
                    cache_key=cache_key,
                    maker_code=maker_code,
                    maker_name=maker_name,
                    model_code=model_code,
                    model_name=model_name,
                    cars=cars
                )
        except Exception as e:
            print(f"Error caching {cache_key}: {e}")
        finally:
            # 완료 시그널
            event.set()
            # 대기열에서 제거
            if cache_key in _pending_requests:
                del _pending_requests[cache_key]

    def cleanup_expired_cache(self):
        """만료된 캐시 정리"""
        expired = self.db.query(CarCache).filter(
            CarCache.expires_at < datetime.utcnow()
        ).all()

        for cache in expired:
            self.db.delete(cache)

        self.db.commit()

        return len(expired)

    # 상세 정보 캐시 관련
    def get_detail_cache(self, car_id: str) -> Optional[CarDetailCache]:
        """상세 정보 캐시 조회"""
        cache = self.db.query(CarDetailCache).filter(
            CarDetailCache.car_id == car_id
        ).first()

        if cache:
            if cache.expires_at < datetime.utcnow():
                self.db.delete(cache)
                self.db.commit()
                return None
            return cache
        return None

    def save_detail_cache(self, car_id: str, detail_data: Dict[str, Any]) -> CarDetailCache:
        """상세 정보 캐시 저장"""
        expires_at = datetime.utcnow() + timedelta(hours=CACHE_TTL_HOURS)

        existing = self.db.query(CarDetailCache).filter(
            CarDetailCache.car_id == car_id
        ).first()
        if existing:
            self.db.delete(existing)
            self.db.commit()

        cache = CarDetailCache(
            car_id=car_id,
            detail_data=json.dumps(detail_data, ensure_ascii=False),
            expires_at=expires_at
        )
        self.db.add(cache)
        self.db.commit()
        self.db.refresh(cache)
        return cache

    def get_detail_from_cache(self, cache: CarDetailCache) -> Dict[str, Any]:
        """상세 정보 캐시에서 데이터 가져오기"""
        return json.loads(cache.detail_data)
