package shop.alien.store.service.impl;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import shop.alien.store.dto.ShortLinkShortenResponse;
import shop.alien.store.service.ShortLinkService;
import shop.alien.store.util.Base62Util;
import shop.alien.store.util.ShortLinkUrlValidator;

import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@RequiredArgsConstructor
public class ShortLinkServiceImpl implements ShortLinkService {

    private static final String REDIS_KEY_PREFIX = "short:link:";
    private static final int MAX_GENERATE_RETRY = 12;

    private final StringRedisTemplate stringRedisTemplate;

    /**
     * 对外展示的短链前缀（不含末尾斜杠），如 https://m.example.com 或 https://api.example.com/app
     */
    @Value("${short-link.public-base-url:}")
    private String publicBaseUrl;

    /**
     * 逗号分隔的允许长链域名白名单（小写 host），为空则不校验域名仅拦截内网
     */
    @Value("${short-link.allowed-hosts:}")
    private String allowedHostsCsv;

    /**
     * 仅允许 https 长链，默认 true
     */
    @Value("${short-link.https-only:true}")
    private boolean httpsOnly;

    /**
     * 是否允许长链指向 localhost/内网（仅本地调试；生产必须为 false）
     */
    @Value("${short-link.allow-local-network-target:false}")
    private boolean allowLocalNetworkTarget;

    /**
     * Redis 过期天数，默认 30
     */
    @Value("${short-link.ttl-days:30}")
    private int ttlDays;

    @Override
    public ShortLinkShortenResponse shorten(String longUrl) {
        Set<String> allowed = ShortLinkUrlValidator.parseAllowedHostsCsv(allowedHostsCsv);
        String normalized = ShortLinkUrlValidator.validateAndNormalize(longUrl, allowed, httpsOnly, allowLocalNetworkTarget);
        long ttlSeconds = Math.max(1L, (long) ttlDays * 24L * 3600L);

        String prefix = normalizePublicBaseUrl(publicBaseUrl);
        if (!StringUtils.hasText(prefix)) {
            throw new IllegalArgumentException("请配置 short-link.public-base-url（对外完整域名前缀，无尾斜杠，例如 https://m.example.com）");
        }

        for (int i = 0; i < MAX_GENERATE_RETRY; i++) {
            long id = ThreadLocalRandom.current().nextLong(1L, Long.MAX_VALUE);
            String code = Base62Util.encodePositiveLong(id);
            String key = REDIS_KEY_PREFIX + code;
            Boolean created = stringRedisTemplate.opsForValue().setIfAbsent(key, normalized, ttlSeconds, TimeUnit.SECONDS);
            if (Boolean.TRUE.equals(created)) {
                String path = "/store/short-link/s/" + code;
                String shortUrl = prefix + path;
                return new ShortLinkShortenResponse(shortUrl, code);
            }
        }
        throw new IllegalStateException("短链生成失败，请稍后重试");
    }

    @Override
    public String resolveLongUrl(String code) {
        if (!StringUtils.hasText(code)) {
            return null;
        }
        String trimmed = code.trim();
        if (trimmed.length() > 64 || !trimmed.matches("[0-9A-Za-z]+")) {
            return null;
        }
        return stringRedisTemplate.opsForValue().get(REDIS_KEY_PREFIX + trimmed);
    }

    private static String normalizePublicBaseUrl(String raw) {
        if (!StringUtils.hasText(raw)) {
            return "";
        }
        String s = raw.trim();
        while (s.endsWith("/")) {
            s = s.substring(0, s.length() - 1);
        }
        return s;
    }
}
