package com.zhentao.service.impl;

import com.zhentao.service.SmsService;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Service
public class SmsServiceImpl implements SmsService {
    private static final Logger log = LoggerFactory.getLogger(SmsServiceImpl.class);

    @Value("${sms.provider:simple}")
    private String provider;

    @Value("${sms.api.host:https://dfsns.market.alicloudapi.com}")
    private String smsHost;

    @Value("${sms.api.path:/data/send_sms}")
    private String smsPath;

    @Value("${sms.api.template-id:CST_ptdie100}")
    private String templateId;

    // 兼容 application.yml 中使用的 app-code 命名
    @Value("${sms.api.app-code:${sms.api.appcode:}}")
    private String appcode;

    @Autowired(required = false)
    private StringRedisTemplate stringRedisTemplate;

    @Value("${redis.enabled:true}")
    private boolean redisEnabled;

    // 开发环境内存回退存储（仅 provider=mock 时使用）
    private final ConcurrentHashMap<String, String> memoryStore = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, Long> memoryExpire = new ConcurrentHashMap<>();

    private String redisKey(String phone) {
        return "login:sms:" + phone;
    }

    @Override
    public boolean sendLoginCode(String phone) {
        // 生成6位验证码
        String code = String.format("%06d", new Random().nextInt(1000000));

        // 显眼标记验证码输出到控制台，便于联调与查看
        // 注意：生产环境不建议长期保留此日志，可能存在安全风险
        log.warn("==================== LOGIN_SMS_CODE ==================== phone={} | CODE={} ====================", phone, code);

        // 构建请求
        String url = smsHost + smsPath;
        try (CloseableHttpClient client = HttpClients.createDefault()) {
            // 开发/联调环境支持Mock：不调用外部短信网关，直接缓存验证码
            if ("mock".equalsIgnoreCase(provider) || appcode == null || appcode.trim().isEmpty()) {
                log.warn("[MOCK SMS] phone={} code={} (stored 5min)", phone, code);
                if (redisEnabled && stringRedisTemplate != null) {
                    try {
                        stringRedisTemplate.opsForValue().set(redisKey(phone), code, Duration.ofMinutes(5));
                    } catch (Exception re) {
                        log.warn("Redis unavailable, falling back to in-memory store for SMS codes", re);
                        memoryStore.put(phone, code);
                        memoryExpire.put(phone, System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(5));
                    }
                } else {
                    // Redis禁用或不可用，走内存回退
                    memoryStore.put(phone, code);
                    memoryExpire.put(phone, System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(5));
                }
                return true;
            }

            HttpPost post = new HttpPost(url);
            post.setHeader("Authorization", "APPCODE " + appcode);
            // API市场多数短信接口使用表单编码
            post.setHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");

            // 表单参数：mobile、templateId、value（如 code:123456）。不同供应商可能有差异。
            ArrayList<NameValuePair> params = new ArrayList<>();
            params.add(new BasicNameValuePair("mobile", phone));
            params.add(new BasicNameValuePair("templateId", templateId));
            params.add(new BasicNameValuePair("value", "code:" + code));
            post.setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8));

            try (CloseableHttpResponse resp = client.execute(post)) {
                int status = resp.getStatusLine().getStatusCode();
                String respText = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8);
                log.info("SMS send response status={} body={}", status, respText);
                if (status == 200) {
                    // 缓存验证码，过期5分钟
                    stringRedisTemplate.opsForValue().set(redisKey(phone), code, Duration.ofMinutes(5));
                    return true;
                }
                log.error("SMS gateway responded non-200. status={} body={}", status, respText);
                return false;
            }
        } catch (Exception e) {
            log.error("Send SMS failed", e);
            return false;
        }
    }

    @Override
    public boolean verifyLoginCode(String phone, String code) {
        // 开发/联调环境允许万能码 000000：当 provider=mock 或未配置 appcode
        if ("000000".equals(code)) {
            if ("mock".equalsIgnoreCase(provider) || appcode == null || appcode.trim().isEmpty()) {
                return true;
            }
        }

        String key = redisKey(phone);
        String cached = null;
        if (redisEnabled && stringRedisTemplate != null) {
            try {
                cached = stringRedisTemplate.opsForValue().get(key);
            } catch (Exception re) {
                log.warn("Redis unavailable during verify, checking in-memory store", re);
            }
        }

        // 与发送逻辑保持一致：当Redis不可用或未命中时，统一检查内存回退存储
        // 说明：sendLoginCode 在 provider=mock 或 appcode 为空、或 Redis 不可用时会写入内存。
        // 为避免前后不一致，这里不再限制 provider，始终尝试读取内存回退。
        if (cached == null) {
            Long expireAt = memoryExpire.get(phone);
            if (expireAt != null && expireAt > System.currentTimeMillis()) {
                cached = memoryStore.get(phone);
            } else {
                memoryStore.remove(phone);
                memoryExpire.remove(phone);
            }
        }

        if (cached == null) {
            return false;
        }
        boolean ok = cached.equals(code);
        if (ok) {
            // 一次性验证码，验证成功后删除
            if (redisEnabled && stringRedisTemplate != null) {
                try {
                    stringRedisTemplate.delete(key);
                } catch (Exception ignore) {
                }
            }
            memoryStore.remove(phone);
            memoryExpire.remove(phone);
        }
        return ok;
    }
}