package com.fs.his.service;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fs.common.exception.CustomException;
import com.fs.common.utils.StringUtils;
import com.fs.his.config.CouponConfig;
import com.fs.his.domain.FsNewcomerQuestionnaire;
import com.fs.his.domain.FsUser;
import com.fs.his.param.FsUserCouponSendParam;
import com.fs.his.utils.PhoneUtil;
import com.fs.his.vo.NewcomerWelfareStateVO;
import com.fs.system.service.ISysConfigService;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 新手福利：手机号奇偶分流、动态问卷、发券
 */
@Service
public class NewcomerWelfareService {

    @Autowired
    private IFsUserService fsUserService;
    @Autowired
    private IFsUserCouponService fsUserCouponService;
    @Autowired
    private NewcomerQuestionnaireService questionnaireService;
    @Autowired
    private ISysConfigService configService;

    @Data
    public static class SubmitBody implements Serializable {
        @NotNull
        private Long questionnaireId;
        @NotNull
        private Map<String, Object> answers;
    }

    @Transactional(rollbackFor = Exception.class)
    public NewcomerWelfareStateVO getState(Long userId) {
        FsUser user = fsUserService.selectFsUserByUserId(userId);
        if (user == null) {
            throw new CustomException("用户不存在");
        }
        assignCouponTypeIfNeeded(user);

        NewcomerWelfareStateVO vo = new NewcomerWelfareStateVO();
        String plain = resolvePlainPhone(user);
        vo.setHasPhone(StringUtils.isNotBlank(plain));
        vo.setNewcomerCouponType(user.getNewcomerCouponType());

        boolean eligible = isEligibleByAccountAge(user);
        boolean granted = isGranted(user);
        vo.setWelfareGranted(granted);

        FsNewcomerQuestionnaire q = questionnaireService.resolveForApp();
        fillQuestionnaire(vo, q);

        boolean pathA = "A".equals(user.getNewcomerCouponType());
        boolean hasForm = q != null && StringUtils.isNotBlank(q.getFormSchema());
        vo.setShowQuestionnaire(vo.isHasPhone() && eligible && pathA && !granted && !isProfileDone(user) && hasForm);

        vo.setJustGranted(false);
        if (vo.isHasPhone() && eligible && "B".equals(user.getNewcomerCouponType()) && !granted) {
            tryGrantAndMark(user, "B");
            vo.setJustGranted(true);
            user = fsUserService.selectFsUserByUserId(userId);
            vo.setWelfareGranted(isGranted(user));
        }
        return vo;
    }

    @Transactional(rollbackFor = Exception.class)
    public void submitQuestionnaire(Long userId, SubmitBody body) {
        FsUser user = fsUserService.selectFsUserByUserId(userId);
        if (user == null) {
            throw new CustomException("用户不存在");
        }
        assignCouponTypeIfNeeded(user);
        if (!"A".equals(user.getNewcomerCouponType())) {
            throw new CustomException("当前账号无需提交问卷");
        }
        if (isGranted(user)) {
            throw new CustomException("新手福利已领取");
        }
        if (isProfileDone(user)) {
            throw new CustomException("问卷已完成");
        }
        if (!isEligibleByAccountAge(user)) {
            throw new CustomException("当前账号不参与新手福利活动");
        }

        FsNewcomerQuestionnaire current = questionnaireService.resolveForApp();
        if (current == null || current.getId() == null) {
            throw new CustomException("问卷未配置");
        }
        if (!current.getId().equals(body.getQuestionnaireId())) {
            throw new CustomException("问卷已更新，请返回刷新后重新填写");
        }
        if (StringUtils.isBlank(current.getFormSchema())) {
            throw new CustomException("问卷表单为空");
        }

        validateAnswers(current.getFormSchema(), body.getAnswers());

        FsUser patch = new FsUser();
        patch.setUserId(userId);
        patch.setNewcomerQuestionnaireId(current.getId());
        patch.setNewcomerAnswersJson(JSONUtil.toJsonStr(body.getAnswers()));
        patch.setNewcomerProfileDone(1);
        syncSexFromAnswers(patch, body.getAnswers());
        fsUserService.updateFsUser(patch);

        user.setNewcomerProfileDone(1);
        tryGrantAndMark(user, "A");
    }

    private void fillQuestionnaire(NewcomerWelfareStateVO vo, FsNewcomerQuestionnaire q) {
        if (q == null) {
            vo.setQuestionnaireId(null);
            vo.setFormSchema(null);
            vo.setSchemaVersion(1);
            return;
        }
        vo.setQuestionnaireId(q.getId());
        vo.setFormSchema(q.getFormSchema());
        vo.setSchemaVersion(parseSchemaVersion(q.getFormSchema()));
    }

    private static int parseSchemaVersion(String formSchema) {
        if (StringUtils.isBlank(formSchema)) {
            return 1;
        }
        try {
            Integer v = JSONUtil.parseObj(formSchema).getInt("version");
            return v != null && v > 0 ? v : 1;
        } catch (Exception e) {
            return 1;
        }
    }

    private void assignCouponTypeIfNeeded(FsUser user) {
        if (StringUtils.isNotBlank(user.getNewcomerCouponType())) {
            return;
        }
        String plain = resolvePlainPhone(user);
        if (StringUtils.isBlank(plain)) {
            return;
        }
        char last = plain.charAt(plain.length() - 1);
        int d = (last >= '0' && last <= '9') ? (last - '0') : 0;
        String type = (last >= '0' && last <= '9' && d % 2 == 1) ? "A" : "B";

        FsUser patch = new FsUser();
        patch.setUserId(user.getUserId());
        patch.setNewcomerCouponType(type);
        fsUserService.updateFsUser(patch);
        user.setNewcomerCouponType(type);
    }

    private String resolvePlainPhone(FsUser user) {
        if (user == null || StringUtils.isBlank(user.getPhone())) {
            return null;
        }
        String p = user.getPhone().trim();
        if (p.matches("^1\\d{10}$")) {
            return p;
        }
        String d = PhoneUtil.decryptPhone(p);
        if (StringUtils.isNotBlank(d) && d.matches("^1\\d{10}$")) {
            return d;
        }
        d = PhoneUtil.decryptPhoneOldKey(p);
        if (StringUtils.isNotBlank(d) && d.matches("^1\\d{10}$")) {
            return d;
        }
        return null;
    }

    private boolean isEligibleByAccountAge(FsUser user) {
        String daysStr = configService.selectConfigByKey("newcomer.welfare.max_account_age_days");
        if (StringUtils.isBlank(daysStr)) {
            return true;
        }
        int days;
        try {
            days = Integer.parseInt(daysStr.trim());
        } catch (NumberFormatException e) {
            return true;
        }
        if (days <= 0 || user.getCreateTime() == null) {
            return true;
        }
        long cutoff = System.currentTimeMillis() - (long) days * 86_400_000L;
        return user.getCreateTime().getTime() >= cutoff;
    }

    private static boolean isGranted(FsUser user) {
        return user.getNewcomerWelfareGranted() != null && user.getNewcomerWelfareGranted() == 1;
    }

    private static boolean isProfileDone(FsUser user) {
        return user.getNewcomerProfileDone() != null && user.getNewcomerProfileDone() == 1;
    }

    private void tryGrantAndMark(FsUser user, String path) {
        Long couponId = resolveCouponId(path);
        if (couponId == null) {
            throw new CustomException("未配置新手福利券，请在参数 his.coupon 中配置 newcomerCoupon" + path);
        }
        FsUserCouponSendParam sendParam = new FsUserCouponSendParam();
        sendParam.setUserId(user.getUserId());
        sendParam.setCouponId(couponId);
        sendParam.setSystemSend(true);
        fsUserCouponService.sendFsUserCoupon(sendParam);

        FsUser patch = new FsUser();
        patch.setUserId(user.getUserId());
        patch.setNewcomerWelfareGranted(1);
        fsUserService.updateFsUser(patch);
        user.setNewcomerWelfareGranted(1);
    }

    private Long resolveCouponId(String path) {
        String json = configService.selectConfigByKey("his.coupon");
        if (StringUtils.isBlank(json)) {
            return null;
        }
        CouponConfig cfg = JSONUtil.toBean(json, CouponConfig.class);
        if (cfg == null) {
            return null;
        }
        return "A".equals(path) ? cfg.getNewcomerCouponA() : cfg.getNewcomerCouponB();
    }

    private void syncSexFromAnswers(FsUser patch, Map<String, Object> answers) {
        if (answers == null) {
            return;
        }
        Object s = answers.get("sex");
        if (s == null) {
            return;
        }
        try {
            int v = Integer.parseInt(String.valueOf(s).trim());
            if (v == 1 || v == 2) {
                patch.setSex(v);
            }
        } catch (NumberFormatException ignored) {
            // ignore
        }
    }

    // ---------- 答案与 form_schema 校验（原独立类内聚至此） ----------
    /** 展示名：优先 label，兼容仅设计器使用的 title */
    private static String fieldDisplay(JSONObject f, String keyFallback) {
        if (f == null) {
            return keyFallback;
        }
        if (StringUtils.isNotBlank(f.getStr("label"))) {
            return f.getStr("label");
        }
        if (StringUtils.isNotBlank(f.getStr("title"))) {
            return f.getStr("title");
        }
        return keyFallback;
    }

    private void validateAnswers(String formSchemaJson, Map<String, Object> answers) {
        if (StringUtils.isBlank(formSchemaJson)) {
            throw new CustomException("问卷未配置表单");
        }
        JSONArray fields = JSONUtil.parseObj(formSchemaJson).getJSONArray("fields");
        if (fields == null || fields.isEmpty()) {
            return;
        }
        for (int i = 0; i < fields.size(); i++) {
            JSONObject f = fields.getJSONObject(i);
            if (f == null) {
                continue;
            }
            String key = f.getStr("key");
            if (StringUtils.isBlank(key)) {
                throw new CustomException("问卷配置异常：题目缺少 key");
            }
            String type = StringUtils.defaultString(f.getStr("type"), "text").toLowerCase();
            boolean required = f.getBool("required", false);
            Object raw = answers == null ? null : answers.get(key);

            if (required && isAnswerEmpty(raw, type)) {
                throw new CustomException("请填写：" + fieldDisplay(f, key));
            }
            if (isAnswerEmpty(raw, type)) {
                continue;
            }
            switch (type) {
                case "radio":
                    assertRadio(f, raw);
                    break;
                case "checkbox":
                    assertCheckbox(f, raw);
                    break;
                case "number":
                    assertNumber(raw, fieldDisplay(f, key));
                    break;
                case "text":
                default:
                    if (!(raw instanceof String) && raw != null) {
                        throw new CustomException("题目格式不正确：" + fieldDisplay(f, key));
                    }
                    break;
            }
        }
    }

    private static boolean isAnswerEmpty(Object raw, String type) {
        if (raw == null) {
            return true;
        }
        if ("checkbox".equals(type)) {
            List<?> list = normalizeList(raw);
            return list == null || list.isEmpty();
        }
        if (raw instanceof String) {
            return StringUtils.isBlank((String) raw);
        }
        return false;
    }

    private static void assertRadio(JSONObject field, Object raw) {
        String v = raw == null ? null : String.valueOf(raw).trim();
        if (!optionValues(field).contains(v)) {
            throw new CustomException("选项不合法：" + fieldDisplay(field, field.getStr("key")));
        }
    }

    private static void assertCheckbox(JSONObject field, Object raw) {
        List<?> list = normalizeList(raw);
        if (list == null || list.isEmpty()) {
            return;
        }
        Set<String> allowed = optionValues(field);
        for (Object o : list) {
            String v = o == null ? "" : String.valueOf(o).trim();
            if (!allowed.contains(v)) {
                throw new CustomException("多选项不合法：" + fieldDisplay(field, field.getStr("key")));
            }
        }
    }

    private static void assertNumber(Object raw, String label) {
        if (raw instanceof Number) {
            return;
        }
        if (raw instanceof String) {
            try {
                Double.parseDouble(((String) raw).trim());
                return;
            } catch (NumberFormatException ignored) {
                // fall through
            }
        }
        throw new CustomException("请输入有效数字：" + label);
    }

    private static Set<String> optionValues(JSONObject field) {
        Set<String> set = new HashSet<>();
        JSONArray opts = field.getJSONArray("options");
        if (opts == null) {
            return set;
        }
        for (int j = 0; j < opts.size(); j++) {
            JSONObject o = opts.getJSONObject(j);
            if (o != null && o.get("value") != null) {
                set.add(String.valueOf(o.get("value")).trim());
            }
        }
        return set;
    }

    private static List<?> normalizeList(Object raw) {
        if (raw == null) {
            return null;
        }
        if (raw instanceof List) {
            return (List<?>) raw;
        }
        if (raw instanceof Collection) {
            return new ArrayList<>((Collection<?>) raw);
        }
        if (raw instanceof JSONArray) {
            JSONArray ja = (JSONArray) raw;
            List<Object> list = new ArrayList<>(ja.size());
            for (int i = 0; i < ja.size(); i++) {
                list.add(ja.get(i));
            }
            return list;
        }
        return null;
    }
}
