package com.smyhub.infogenie.infogeniebackendjava.service;

import com.smyhub.infogenie.infogeniebackendjava.config.AppProperties;
import com.smyhub.infogenie.infogeniebackendjava.dto.response.SiteSourceResponse;
import com.smyhub.infogenie.infogeniebackendjava.entity.Site60sDisabled;
import com.smyhub.infogenie.infogeniebackendjava.entity.Site60sUpstream;
import com.smyhub.infogenie.infogeniebackendjava.entity.SiteAIModelDisabled;
import com.smyhub.infogenie.infogeniebackendjava.repository.Site60sDisabledRepository;
import com.smyhub.infogenie.infogeniebackendjava.repository.Site60sUpstreamRepository;
import com.smyhub.infogenie.infogeniebackendjava.repository.SiteAIModelDisabledRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.util.List;
import java.util.Map;

/**
 * Manages all public site-configuration reads and admin writes.
 * Implements a Redis cache-aside pattern identical to the Go backend:
 * reads check Redis first, fall back to MySQL, write back with TTL.
 * Admin writes update MySQL then evict the corresponding Redis key.
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SiteConfigService {

    private static final String KEY_60S_DISABLED = "site:60s-disabled";
    private static final String KEY_60S_SOURCE = "site:60s-source";
    private static final String KEY_AI_MODEL_DISABLED = "site:ai-model-disabled";

    private static final Map<String, String> SOURCE_URLS = Map.of(
            "self", "https://60s.api.shumengya.top",
            "official", "https://60s.viki.moe"
    );
    private static final Map<String, String> SOURCE_LABELS = Map.of(
            "self", "自建源",
            "official", "官方源"
    );

    private final Site60sDisabledRepository site60sDisabledRepo;
    private final SiteAIModelDisabledRepository siteAIModelDisabledRepo;
    private final Site60sUpstreamRepository site60sUpstreamRepo;
    private final RedisTemplate<String, Object> redisTemplate;
    private final AppProperties appProperties;

    public List<String> get60sDisabled() {
        String cacheKey = cacheKey(KEY_60S_DISABLED);
        if (appProperties.isRedisEnabled()) {
            try {
                Object cached = redisTemplate.opsForValue().get(cacheKey);
                if (cached instanceof List<?> list) {
                    return list.stream().map(Object::toString).toList();
                }
            } catch (Exception e) {
                log.warn("Redis read failed for {}: {}", cacheKey, e.getMessage());
            }
        }
        List<String> result = site60sDisabledRepo.findAll()
                .stream().map(Site60sDisabled::getFeatureId).toList();
        cacheWrite(cacheKey, result);
        return result;
    }

    @Transactional
    public int update60sDisabled(List<String> ids) {
        site60sDisabledRepo.deleteAll();
        List<Site60sDisabled> rows = ids.stream().map(Site60sDisabled::new).toList();
        site60sDisabledRepo.saveAll(rows);
        evict(cacheKey(KEY_60S_DISABLED));
        return rows.size();
    }

    public List<String> getAiModelDisabled() {
        String cacheKey = cacheKey(KEY_AI_MODEL_DISABLED);
        if (appProperties.isRedisEnabled()) {
            try {
                Object cached = redisTemplate.opsForValue().get(cacheKey);
                if (cached instanceof List<?> list) {
                    return list.stream().map(Object::toString).toList();
                }
            } catch (Exception e) {
                log.warn("Redis read failed for {}: {}", cacheKey, e.getMessage());
            }
        }
        List<String> result = siteAIModelDisabledRepo.findAll()
                .stream().map(SiteAIModelDisabled::getAppId).toList();
        cacheWrite(cacheKey, result);
        return result;
    }

    @Transactional
    public int updateAiModelDisabled(List<String> ids) {
        siteAIModelDisabledRepo.deleteAll();
        List<SiteAIModelDisabled> rows = ids.stream().map(SiteAIModelDisabled::new).toList();
        siteAIModelDisabledRepo.saveAll(rows);
        evict(cacheKey(KEY_AI_MODEL_DISABLED));
        return rows.size();
    }

    public SiteSourceResponse get60sSource() {
        String cacheKey = cacheKey(KEY_60S_SOURCE);
        if (appProperties.isRedisEnabled()) {
            try {
                Object cached = redisTemplate.opsForValue().get(cacheKey);
                if (cached instanceof SiteSourceResponse r) {
                    return r;
                }
            } catch (Exception e) {
                log.warn("Redis read failed for {}: {}", cacheKey, e.getMessage());
            }
        }
        Site60sUpstream row = site60sUpstreamRepo.findById(1L).orElseGet(() -> {
            Site60sUpstream def = new Site60sUpstream();
            def.setId(1L);
            def.setSourceId("self");
            return site60sUpstreamRepo.save(def);
        });
        SiteSourceResponse resp = buildSourceResponse(row.getSourceId());
        cacheWrite(cacheKey, resp);
        return resp;
    }

    @Transactional
    public SiteSourceResponse update60sSource(String sourceId) {
        if (!SOURCE_URLS.containsKey(sourceId)) {
            throw new IllegalArgumentException("Invalid source_id: " + sourceId);
        }
        Site60sUpstream row = site60sUpstreamRepo.findById(1L).orElse(new Site60sUpstream());
        row.setId(1L);
        row.setSourceId(sourceId);
        site60sUpstreamRepo.save(row);
        evict(cacheKey(KEY_60S_SOURCE));
        return buildSourceResponse(sourceId);
    }

    public String get60sBaseUrl() {
        return SOURCE_URLS.getOrDefault(get60sSource().getSourceId(),
                SOURCE_URLS.get("self"));
    }

    private SiteSourceResponse buildSourceResponse(String sourceId) {
        SiteSourceResponse r = new SiteSourceResponse();
        r.setSourceId(sourceId);
        r.setBaseUrl(SOURCE_URLS.getOrDefault(sourceId, SOURCE_URLS.get("self")));
        r.setLabel(SOURCE_LABELS.getOrDefault(sourceId, ""));
        return r;
    }

    private String cacheKey(String suffix) {
        return appProperties.getRedisKeyPrefix() + suffix;
    }

    private void cacheWrite(String key, Object value) {
        if (!appProperties.isRedisEnabled()) return;
        try {
            redisTemplate.opsForValue().set(key, value,
                    Duration.ofSeconds(appProperties.getRedisSiteTtl()));
        } catch (Exception e) {
            log.warn("Redis write failed for {}: {}", key, e.getMessage());
        }
    }

    private void evict(String key) {
        if (!appProperties.isRedisEnabled()) return;
        try {
            redisTemplate.delete(key);
        } catch (Exception e) {
            log.warn("Redis evict failed for {}: {}", key, e.getMessage());
        }
    }
}
