package com.unicorn.hgzero.meeting.infra.cache;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.unicorn.hgzero.meeting.biz.dto.AiAnalysisDTO;
import com.unicorn.hgzero.meeting.infra.dto.response.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * Redis 캐시 서비스
 * 회의, 회의록, Todo 등의 데이터 캐싱
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class CacheService {

    private final RedisTemplate<String, String> redisTemplate;
    private final ObjectMapper objectMapper;

    private static final String MEETING_PREFIX = "meeting:";
    private static final String MINUTES_PREFIX = "minutes:";
    private static final String MINUTES_LIST_PREFIX = "minutes:list:";
    private static final String MINUTES_DETAIL_PREFIX = "minutes:detail:";
    private static final String TODO_PREFIX = "todo:";
    private static final String TODO_LIST_PREFIX = "todo:list:";
    private static final String TODO_DETAIL_PREFIX = "todo:detail:";
    private static final String TEMPLATE_PREFIX = "template:";
    private static final String TEMPLATE_LIST_PREFIX = "template:list:";
    private static final String TEMPLATE_DETAIL_PREFIX = "template:detail:";
    private static final String DASHBOARD_PREFIX = "dashboard:";
    private static final String SESSION_PREFIX = "session:";
    private static final String AI_ANALYSIS_PREFIX = "ai:analysis:";

    /**
     * 회의 정보 캐시 저장
     * 
     * @param meetingId 회의 ID
     * @param data 회의 데이터
     * @param ttl TTL (초)
     */
    public void cacheMeeting(String meetingId, Object data, long ttl) {
        try {
            String key = MEETING_PREFIX + meetingId;
            String value = objectMapper.writeValueAsString(data);
            redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(ttl));
            log.debug("회의 정보 캐시 저장 - meetingId: {}", meetingId);
        } catch (Exception e) {
            log.error("회의 정보 캐시 저장 실패 - meetingId: {}", meetingId, e);
        }
    }

    /**
     * 회의 정보 캐시 조회
     * 
     * @param meetingId 회의 ID
     * @param clazz 반환 타입
     * @return 캐시된 회의 데이터
     */
    public <T> T getCachedMeeting(String meetingId, Class<T> clazz) {
        try {
            String key = MEETING_PREFIX + meetingId;
            String value = redisTemplate.opsForValue().get(key);
            if (value != null) {
                log.debug("회의 정보 캐시 조회 성공 - meetingId: {}", meetingId);
                return objectMapper.readValue(value, clazz);
            }
        } catch (Exception e) {
            log.error("회의 정보 캐시 조회 실패 - meetingId: {}", meetingId, e);
        }
        return null;
    }

    /**
     * 회의록 정보 캐시 저장
     * 
     * @param minutesId 회의록 ID
     * @param data 회의록 데이터
     * @param ttl TTL (초)
     */
    public void cacheMinutes(String minutesId, Object data, long ttl) {
        try {
            String key = MINUTES_PREFIX + minutesId;
            String value = objectMapper.writeValueAsString(data);
            redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(ttl));
            log.debug("회의록 정보 캐시 저장 - minutesId: {}", minutesId);
        } catch (Exception e) {
            log.error("회의록 정보 캐시 저장 실패 - minutesId: {}", minutesId, e);
        }
    }

    /**
     * 회의록 정보 캐시 조회
     * 
     * @param minutesId 회의록 ID
     * @param clazz 반환 타입
     * @return 캐시된 회의록 데이터
     */
    public <T> T getCachedMinutes(String minutesId, Class<T> clazz) {
        try {
            String key = MINUTES_PREFIX + minutesId;
            String value = redisTemplate.opsForValue().get(key);
            if (value != null) {
                log.debug("회의록 정보 캐시 조회 성공 - minutesId: {}", minutesId);
                return objectMapper.readValue(value, clazz);
            }
        } catch (Exception e) {
            log.error("회의록 정보 캐시 조회 실패 - minutesId: {}", minutesId, e);
        }
        return null;
    }

    /**
     * 대시보드 데이터 캐시 저장
     * 
     * @param userId 사용자 ID
     * @param data 대시보드 데이터
     * @param ttl TTL (초)
     */
    public void cacheDashboard(String userId, Object data, long ttl) {
        try {
            String key = DASHBOARD_PREFIX + userId;
            String value = objectMapper.writeValueAsString(data);
            redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(ttl));
            log.debug("대시보드 데이터 캐시 저장 - userId: {}", userId);
        } catch (Exception e) {
            log.error("대시보드 데이터 캐시 저장 실패 - userId: {}", userId, e);
        }
    }

    /**
     * 대시보드 데이터 캐시 조회
     * 
     * @param userId 사용자 ID
     * @param clazz 반환 타입
     * @return 캐시된 대시보드 데이터
     */
    public <T> T getCachedDashboard(String userId, Class<T> clazz) {
        try {
            String key = DASHBOARD_PREFIX + userId;
            String value = redisTemplate.opsForValue().get(key);
            if (value != null) {
                log.debug("대시보드 데이터 캐시 조회 성공 - userId: {}", userId);
                return objectMapper.readValue(value, clazz);
            }
        } catch (Exception e) {
            log.error("대시보드 데이터 캐시 조회 실패 - userId: {}", userId, e);
        }
        return null;
    }

    /**
     * 세션 정보 캐시 저장
     * 
     * @param sessionId 세션 ID
     * @param data 세션 데이터
     * @param ttl TTL (초)
     */
    public void cacheSession(String sessionId, Object data, long ttl) {
        try {
            String key = SESSION_PREFIX + sessionId;
            String value = objectMapper.writeValueAsString(data);
            redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(ttl));
            log.debug("세션 정보 캐시 저장 - sessionId: {}", sessionId);
        } catch (Exception e) {
            log.error("세션 정보 캐시 저장 실패 - sessionId: {}", sessionId, e);
        }
    }

    /**
     * 세션 정보 캐시 조회
     * 
     * @param sessionId 세션 ID
     * @param clazz 반환 타입
     * @return 캐시된 세션 데이터
     */
    public <T> T getCachedSession(String sessionId, Class<T> clazz) {
        try {
            String key = SESSION_PREFIX + sessionId;
            String value = redisTemplate.opsForValue().get(key);
            if (value != null) {
                log.debug("세션 정보 캐시 조회 성공 - sessionId: {}", sessionId);
                return objectMapper.readValue(value, clazz);
            }
        } catch (Exception e) {
            log.error("세션 정보 캐시 조회 실패 - sessionId: {}", sessionId, e);
        }
        return null;
    }

    /**
     * 캐시 삭제
     * 
     * @param prefix 키 접두사
     * @param id 식별자
     */
    public void evictCache(String prefix, String id) {
        try {
            String key = prefix + id;
            redisTemplate.delete(key);
            log.debug("캐시 삭제 - key: {}", key);
        } catch (Exception e) {
            log.error("캐시 삭제 실패 - key: {}", prefix + id, e);
        }
    }

    /**
     * 특정 패턴의 모든 캐시 삭제
     * 
     * @param pattern 패턴
     */
    public void evictCacheByPattern(String pattern) {
        try {
            var keys = redisTemplate.keys(pattern);
            if (keys != null && !keys.isEmpty()) {
                redisTemplate.delete(keys);
                log.debug("패턴 캐시 삭제 - pattern: {}, count: {}", pattern, keys.size());
            }
        } catch (Exception e) {
            log.error("패턴 캐시 삭제 실패 - pattern: {}", pattern, e);
        }
    }

    // 회의록 관련 캐시 메서드
    public void cacheMinutesList(String cacheKey, MinutesListResponse response) {
        try {
            String value = objectMapper.writeValueAsString(response);
            redisTemplate.opsForValue().set(MINUTES_LIST_PREFIX + cacheKey, value, Duration.ofMinutes(10));
            log.debug("회의록 목록 캐시 저장 - key: {}", cacheKey);
        } catch (Exception e) {
            log.error("회의록 목록 캐시 저장 실패 - key: {}", cacheKey, e);
        }
    }

    public MinutesListResponse getCachedMinutesList(String cacheKey) {
        try {
            String value = redisTemplate.opsForValue().get(MINUTES_LIST_PREFIX + cacheKey);
            if (value != null) {
                return objectMapper.readValue(value, MinutesListResponse.class);
            }
        } catch (Exception e) {
            log.error("회의록 목록 캐시 조회 실패 - key: {}", cacheKey, e);
        }
        return null;
    }

    public void cacheMinutesDetail(String minutesId, MinutesDetailResponse response) {
        try {
            String value = objectMapper.writeValueAsString(response);
            redisTemplate.opsForValue().set(MINUTES_DETAIL_PREFIX + minutesId, value, Duration.ofMinutes(5));
            log.debug("회의록 상세 캐시 저장 - minutesId: {}", minutesId);
        } catch (Exception e) {
            log.error("회의록 상세 캐시 저장 실패 - minutesId: {}", minutesId, e);
        }
    }

    public MinutesDetailResponse getCachedMinutesDetail(String minutesId) {
        try {
            String value = redisTemplate.opsForValue().get(MINUTES_DETAIL_PREFIX + minutesId);
            if (value != null) {
                return objectMapper.readValue(value, MinutesDetailResponse.class);
            }
        } catch (Exception e) {
            log.error("회의록 상세 캐시 조회 실패 - minutesId: {}", minutesId, e);
        }
        return null;
    }

    public void evictCacheMinutesDetail(String minutesId) {
        redisTemplate.delete(MINUTES_DETAIL_PREFIX + minutesId);
        log.debug("회의록 상세 캐시 삭제 - minutesId: {}", minutesId);
    }

    public void evictCacheMinutesList(String userId) {
        evictCacheByPattern(MINUTES_LIST_PREFIX + "*" + userId + "*");
        log.debug("회의록 목록 캐시 삭제 - userId: {}", userId);
    }

    // Todo 관련 캐시 메서드
    public void cacheTodoList(String cacheKey, TodoListResponse response) {
        try {
            String value = objectMapper.writeValueAsString(response);
            redisTemplate.opsForValue().set(TODO_LIST_PREFIX + cacheKey, value, Duration.ofMinutes(10));
            log.debug("Todo 목록 캐시 저장 - key: {}", cacheKey);
        } catch (Exception e) {
            log.error("Todo 목록 캐시 저장 실패 - key: {}", cacheKey, e);
        }
    }

    public TodoListResponse getCachedTodoList(String cacheKey) {
        try {
            String value = redisTemplate.opsForValue().get(TODO_LIST_PREFIX + cacheKey);
            if (value != null) {
                return objectMapper.readValue(value, TodoListResponse.class);
            }
        } catch (Exception e) {
            log.error("Todo 목록 캐시 조회 실패 - key: {}", cacheKey, e);
        }
        return null;
    }

    public void evictCacheTodoList(String assigneeId) {
        evictCacheByPattern(TODO_LIST_PREFIX + "*" + assigneeId + "*");
        log.debug("Todo 목록 캐시 삭제 - assigneeId: {}", assigneeId);
    }

    public void evictCacheTodoDetail(String todoId) {
        redisTemplate.delete(TODO_DETAIL_PREFIX + todoId);
        log.debug("Todo 상세 캐시 삭제 - todoId: {}", todoId);
    }

    public void evictCacheDashboard(String userId) {
        redisTemplate.delete(DASHBOARD_PREFIX + userId);
        log.debug("대시보드 캐시 삭제 - userId: {}", userId);
    }

    // 템플릿 관련 캐시 메서드
    public void cacheTemplateList(String cacheKey, TemplateListResponse response) {
        try {
            String value = objectMapper.writeValueAsString(response);
            redisTemplate.opsForValue().set(TEMPLATE_LIST_PREFIX + cacheKey, value, Duration.ofHours(1));
            log.debug("템플릿 목록 캐시 저장 - key: {}", cacheKey);
        } catch (Exception e) {
            log.error("템플릿 목록 캐시 저장 실패 - key: {}", cacheKey, e);
        }
    }

    public TemplateListResponse getCachedTemplateList(String cacheKey) {
        try {
            String value = redisTemplate.opsForValue().get(TEMPLATE_LIST_PREFIX + cacheKey);
            if (value != null) {
                return objectMapper.readValue(value, TemplateListResponse.class);
            }
        } catch (Exception e) {
            log.error("템플릿 목록 캐시 조회 실패 - key: {}", cacheKey, e);
        }
        return null;
    }

    public void cacheTemplateDetail(String templateId, TemplateDetailResponse response) {
        try {
            String value = objectMapper.writeValueAsString(response);
            redisTemplate.opsForValue().set(TEMPLATE_DETAIL_PREFIX + templateId, value, Duration.ofHours(1));
            log.debug("템플릿 상세 캐시 저장 - templateId: {}", templateId);
        } catch (Exception e) {
            log.error("템플릿 상세 캐시 저장 실패 - templateId: {}", templateId, e);
        }
    }

    public TemplateDetailResponse getCachedTemplateDetail(String templateId) {
        try {
            String value = redisTemplate.opsForValue().get(TEMPLATE_DETAIL_PREFIX + templateId);
            if (value != null) {
                return objectMapper.readValue(value, TemplateDetailResponse.class);
            }
        } catch (Exception e) {
            log.error("템플릿 상세 캐시 조회 실패 - templateId: {}", templateId, e);
        }
        return null;
    }

    // AI 분석 관련 캐시 메서드
    public void cacheAiAnalysis(String minutesId, AiAnalysisDTO analysis) {
        try {
            String value = objectMapper.writeValueAsString(analysis);
            redisTemplate.opsForValue().set(AI_ANALYSIS_PREFIX + minutesId, value, Duration.ofHours(1));
            log.debug("AI 분석 결과 캐시 저장 - minutesId: {}", minutesId);
        } catch (Exception e) {
            log.error("AI 분석 결과 캐시 저장 실패 - minutesId: {}", minutesId, e);
        }
    }

    public Optional<AiAnalysisDTO> getAiAnalysis(String minutesId) {
        try {
            String value = redisTemplate.opsForValue().get(AI_ANALYSIS_PREFIX + minutesId);
            if (value != null) {
                AiAnalysisDTO analysis = objectMapper.readValue(value, AiAnalysisDTO.class);
                log.debug("AI 분석 결과 캐시 조회 성공 - minutesId: {}", minutesId);
                return Optional.of(analysis);
            }
        } catch (Exception e) {
            log.error("AI 분석 결과 캐시 조회 실패 - minutesId: {}", minutesId, e);
        }
        return Optional.empty();
    }

    public void evictAiAnalysisCache(String minutesId) {
        try {
            redisTemplate.delete(AI_ANALYSIS_PREFIX + minutesId);
            log.debug("AI 분석 캐시 삭제 - minutesId: {}", minutesId);
        } catch (Exception e) {
            log.error("AI 분석 캐시 삭제 실패 - minutesId: {}", minutesId, e);
        }
    }
}