package com.rj.service.impl;

import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rj.entity.LbDeductionAmount;
import com.rj.mapper.LbDeductionAmountMapper;
import com.rj.service.ILbDeductionAmountService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Service
public class LbDeductionAmountServiceImpl
        extends ServiceImpl<LbDeductionAmountMapper, LbDeductionAmount>
        implements ILbDeductionAmountService {

    @Autowired
    private ObjectMapper objectMapper;

    @Value("${langchain4j.open-ai.chat-model.model-name:qwen-plus}")
    private String dashScopeChatModel;

    private static final DateTimeFormatter DATE_TIME_FORMATTER =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /** 常见正文：首行「M月d日」，次行「姓名 手机号」，含「申请…抵扣…金额」与「已提现金额」。 */
    private static final Pattern MONTH_DAY_PATTERN = Pattern.compile("(\\d{1,2})月(\\d{1,2})日");
    private static final Pattern MOBILE_PHONE_PATTERN = Pattern.compile("1[3-9]\\d{9}");
    /** 优先匹配「申请…抵扣…金额数字」，避免误用「已提现金额」。 */
    private static final Pattern DIKOU_AMT_PATTERN =
            Pattern.compile("申请[^\\n]*?抵扣[^\\n]*?金额\\s*(\\d+(?:\\.\\d+)?)", Pattern.CASE_INSENSITIVE);
    private static final Pattern TIXIAN_AMT_PATTERN =
            Pattern.compile("已提现金额\\s*(\\d+(?:\\.\\d+)?)", Pattern.CASE_INSENSITIVE);

    private static final String PARSE_SYSTEM_PROMPT = """
            你是信息抽取助手。用户会提供「抵扣金记录」自然语言，常见为多行固定格式，例如：
            5月21日
            刘兰兰 13261700877
            申请FXJ抵扣SJF金额2678
            已提现金额2678

            解析规则：
            1. 首行或文中「M月d日」（如 5月21日）仅作业务日期参考，不要写入 JSON（服务端会从原文补 createTime）。
            2. 「姓名 + 空格 + 11位手机号」同一行：userName 为姓名，userPhone 为手机号。
            3. dikouAmt 取含「申请」且含「抵扣」且含「金额」那一行中的数字（如上例 2678）；不要用「已提现金额」作为 dikouAmt。
            4. tixianAmt 取含「已提现金额」那一行中的数字（如上例 2678）；若无该行可省略。
            5. FXJ、SJF 等字母为业务代号，忽略即可。
            6. 金额只输出数字，不要带「元」等单位。

            请只输出一个 JSON 对象，不要 markdown 代码块，不要解释性文字。
            字段名（必须完全一致）：userName、userPhone、dikouAmt、tixianAmt。
            createTime 若填写，格式 yyyy-MM-dd HH:mm:ss；服务端仅将非当前年的年份校正为系统当前年，月日不变。
            不要输出 id、tenantId、originalText、updateTime。
            """;

    @Override
    public Map<String, Object> parseFromTextByLlmAndSave(String tenantId, String text) {
        Map<String, Object> result = new HashMap<>();
        try {
            if (text == null || text.trim().isEmpty()) {
                result.put("success", false);
                result.put("message", "文本不能为空");
                return result;
            }
            if (tenantId == null || tenantId.trim().isEmpty()) {
                result.put("success", false);
                result.put("message", "tenantId不能为空");
                return result;
            }

            String apiKey = System.getenv("DASHSCOPE_API_KEY");
            if (apiKey == null || apiKey.isEmpty()) {
                result.put("success", false);
                result.put("message", "未配置环境变量 DASHSCOPE_API_KEY，无法调用大模型");
                return result;
            }

            String trimmedText = text.trim();
            Generation gen = new Generation();
            Message systemMsg = Message.builder()
                    .role(Role.SYSTEM.getValue())
                    .content(PARSE_SYSTEM_PROMPT)
                    .build();
            Message userMsg = Message.builder()
                    .role(Role.USER.getValue())
                    .content("抵扣金信息如下：\n" + trimmedText)
                    .build();

            GenerationParam param = GenerationParam.builder()
                    .apiKey(apiKey)
                    .model(dashScopeChatModel)
                    .messages(Arrays.asList(systemMsg, userMsg))
                    .resultFormat(GenerationParam.ResultFormat.MESSAGE)
                    .build();

            GenerationResult call = gen.call(param);
            String raw = call.getOutput().getChoices().get(0).getMessage().getContent();
            if (raw == null || raw.trim().isEmpty()) {
                result.put("success", false);
                result.put("message", "大模型返回内容为空");
                return result;
            }

            String json = normalizeJson(raw);
            LbDeductionAmount entity = objectMapper.readValue(json, LbDeductionAmount.class);
            alignCreateTimeYearToCurrent(entity);

            entity.setTenantId(tenantId.trim());
            entity.setOriginalText(trimmedText);
            entity.setId(null);
            entity.setUpdateTime(null);
            entity.setTixianAmt(BigDecimal.ZERO);
            enrichFromDeductionText(entity, trimmedText);
            log.debug(
                    "parseFromTextByLlmAndSave：userName={} userPhone={} dikouAmt={} tixianAmt={} createTime={}",
                    entity.getUserName(),
                    entity.getUserPhone(),
                    entity.getDikouAmt(),
                    entity.getTixianAmt(),
                    entity.getCreateTime());
            entity.setTixianAmt(BigDecimal.ZERO);
            return add(entity);
        } catch (NoApiKeyException e) {
            log.error("DashScope API Key 异常", e);
            result.put("success", false);
            result.put("message", "API密钥异常：" + e.getMessage());
            return result;
        } catch (InputRequiredException e) {
            log.error("DashScope 入参异常", e);
            result.put("success", false);
            result.put("message", "调用大模型入参错误：" + e.getMessage());
            return result;
        } catch (Exception e) {
            log.error("抵扣金信息大模型解析失败", e);
            result.put("success", false);
            result.put("message", "解析失败：" + e.getMessage());
            return result;
        }
    }

    /**
     * 大模型若返回 createTime 且年份不是当前年，仅将年份改为系统当前年，月日及时分秒不变。
     */
    private void alignCreateTimeYearToCurrent(LbDeductionAmount entity) {
        if (entity == null || entity.getCreateTime() == null) {
            return;
        }
        int currentYear = LocalDate.now().getYear();
        LocalDate parsedDate = entity.getCreateTime().toLocalDate();
        if (parsedDate.getYear() == currentYear) {
            return;
        }
        LocalDate adjustedDate = parsedDate.withYear(currentYear);
        LocalDateTime adjusted = LocalDateTime.of(adjustedDate, entity.getCreateTime().toLocalTime());
        log.info("大模型返回年份 {} 与系统年份 {} 不一致，createTime 已调整为 {}", parsedDate.getYear(), currentYear, adjusted);
        entity.setCreateTime(adjusted);
    }

    /**
     * 按业务常见多行格式补全 LLM 可能漏填的字段（日期、姓名电话、抵扣金额）。
     */
    private void enrichFromDeductionText(LbDeductionAmount entity, String text) {
        if (entity == null || text == null || text.isBlank()) {
            return;
        }
        fillCreateTimeFromText(entity, text);
        // 含「申请…抵扣…金额」时以该行为准，避免模型误用「已提现金额」
        fillDikouAmtFromText(entity, text);
        fillTixianAmtFromText(entity, text);
        if (entity.getUserPhone() == null || entity.getUserPhone().isBlank()) {
            fillPhoneFromText(entity, text);
        }
        if (entity.getUserName() == null || entity.getUserName().isBlank()) {
            fillUserNameFromText(entity, text);
        }
    }

    private void fillCreateTimeFromText(LbDeductionAmount entity, String text) {
        if (entity.getCreateTime() != null) {
            return;
        }
        Matcher matcher = MONTH_DAY_PATTERN.matcher(text);
        if (!matcher.find()) {
            return;
        }
        int month = Integer.parseInt(matcher.group(1));
        int day = Integer.parseInt(matcher.group(2));
        LocalDate date = LocalDate.of(LocalDate.now().getYear(), month, day);
        entity.setCreateTime(date.atStartOfDay());
    }

    private void fillDikouAmtFromText(LbDeductionAmount entity, String text) {
        Matcher matcher = DIKOU_AMT_PATTERN.matcher(text);
        if (matcher.find()) {
            entity.setDikouAmt(new BigDecimal(matcher.group(1)));
            return;
        }
        if (entity.getDikouAmt() == null || entity.getDikouAmt().signum() == 0) {
            Matcher fallback = Pattern.compile("金额\\s*(\\d+(?:\\.\\d+)?)").matcher(text);
            if (fallback.find()) {
                entity.setDikouAmt(new BigDecimal(fallback.group(1)));
            }
        }
    }

    private void fillTixianAmtFromText(LbDeductionAmount entity, String text) {
        Matcher matcher = TIXIAN_AMT_PATTERN.matcher(text);
        if (matcher.find()) {
            entity.setTixianAmt(new BigDecimal(matcher.group(1)));
        }
    }

    private void fillPhoneFromText(LbDeductionAmount entity, String text) {
        Matcher matcher = MOBILE_PHONE_PATTERN.matcher(text);
        if (matcher.find()) {
            entity.setUserPhone(matcher.group());
        }
    }

    private void fillUserNameFromText(LbDeductionAmount entity, String text) {
        if (entity.getUserPhone() == null || entity.getUserPhone().isBlank()) {
            fillPhoneFromText(entity, text);
        }
        String phone = entity.getUserPhone();
        if (phone == null || phone.isBlank()) {
            return;
        }
        for (String line : text.split("\\R")) {
            String trimmedLine = line.trim();
            if (!trimmedLine.contains(phone)) {
                continue;
            }
            String namePart = trimmedLine.replace(phone, "").trim();
            if (!namePart.isEmpty()) {
                entity.setUserName(namePart);
                return;
            }
        }
    }

    private static String normalizeJson(String content) {
        if (content == null) {
            return "";
        }
        String trimmed = content.trim();
        if (trimmed.startsWith("```")) {
            int firstLineBreak = trimmed.indexOf('\n');
            int lastFence = trimmed.lastIndexOf("```");
            if (firstLineBreak >= 0 && lastFence > firstLineBreak) {
                trimmed = trimmed.substring(firstLineBreak + 1, lastFence).trim();
            }
        }
        return trimmed;
    }

    @Override
    public Map<String, Object> add(LbDeductionAmount entity) {
        Map<String, Object> result = new HashMap<>();
        try {
            if (entity.getTenantId() == null || entity.getTenantId().trim().isEmpty()) {
                result.put("success", false);
                result.put("message", "tenantId不能为空");
                return result;
            }
            entity.setTenantId(entity.getTenantId().trim());
            if (entity.getUserPhone() != null) {
                entity.setUserPhone(entity.getUserPhone().trim());
            }
            if (entity.getUserName() != null) {
                entity.setUserName(entity.getUserName().trim());
            }
            if (entity.getDikouAmt() == null) {
                entity.setDikouAmt(BigDecimal.ZERO);
            }
            if (entity.getTixianAmt() == null) {
                entity.setTixianAmt(BigDecimal.ZERO);
            }

            if (entity.getId() == null || entity.getId().trim().isEmpty()) {
                entity.setId(UUID.randomUUID().toString());
            }
            LocalDateTime now = LocalDateTime.now();
            if (entity.getCreateTime() == null) {
                entity.setCreateTime(now);
            }
            entity.setUpdateTime(now);

            boolean ok = this.save(entity);
            result.put("success", ok);
            result.put("message", ok ? "新增成功" : "新增失败");
            if (ok) {
                result.put("data", entity);
            }
            return result;
        } catch (Exception e) {
            log.error("抵扣金记录新增异常", e);
            result.put("success", false);
            result.put("message", "新增异常：" + e.getMessage());
            return result;
        }
    }

    @Override
    public Map<String, Object> update(LbDeductionAmount entity) {
        Map<String, Object> result = new HashMap<>();
        try {
            if (entity.getId() == null || entity.getId().trim().isEmpty()) {
                result.put("success", false);
                result.put("message", "id不能为空");
                return result;
            }
            if (entity.getTenantId() != null) {
                entity.setTenantId(entity.getTenantId().trim());
            }
            if (entity.getUserPhone() != null) {
                entity.setUserPhone(entity.getUserPhone().trim());
            }
            if (entity.getUserName() != null) {
                entity.setUserName(entity.getUserName().trim());
            }
            entity.setUpdateTime(LocalDateTime.now());
            boolean ok = this.updateById(entity);
            result.put("success", ok);
            result.put("message", ok ? "编辑成功" : "编辑失败");
            if (ok) {
                result.put("data", this.getById(entity.getId()));
            }
            return result;
        } catch (Exception e) {
            log.error("抵扣金记录编辑异常", e);
            result.put("success", false);
            result.put("message", "编辑异常：" + e.getMessage());
            return result;
        }
    }

    @Override
    public Map<String, Object> deleteById(String id) {
        Map<String, Object> result = new HashMap<>();
        try {
            if (id == null || id.trim().isEmpty()) {
                result.put("success", false);
                result.put("message", "id不能为空");
                return result;
            }
            boolean ok = this.removeById(id.trim());
            result.put("success", ok);
            result.put("message", ok ? "删除成功" : "删除失败");
            return result;
        } catch (Exception e) {
            log.error("抵扣金记录删除异常", e);
            result.put("success", false);
            result.put("message", "删除异常：" + e.getMessage());
            return result;
        }
    }

    @Override
    public Map<String, Object> pageQuery(Integer current,
                                         Integer size,
                                         String tenantId,
                                         String userName,
                                         String userPhone,
                                         String createTimeStart,
                                         String createTimeEnd) {
        Map<String, Object> result = new HashMap<>();
        try {
            if (current == null || current < 1) {
                current = 1;
            }
            if (size == null || size < 1) {
                size = 10;
            }

            LambdaQueryWrapper<LbDeductionAmount> queryWrapper = new LambdaQueryWrapper<>();
            if (tenantId != null && !tenantId.trim().isEmpty()) {
                queryWrapper.eq(LbDeductionAmount::getTenantId, tenantId.trim());
            }
            if (userName != null && !userName.trim().isEmpty()) {
                queryWrapper.like(LbDeductionAmount::getUserName, userName.trim());
            }
            if (userPhone != null && !userPhone.trim().isEmpty()) {
                queryWrapper.like(LbDeductionAmount::getUserPhone, userPhone.trim());
            }

            LocalDateTime start = parseDateTime(createTimeStart);
            if (createTimeStart != null && !createTimeStart.trim().isEmpty() && start == null) {
                result.put("success", false);
                result.put("message", "createTimeStart 格式错误，请使用 yyyy-MM-dd HH:mm:ss");
                return result;
            }
            LocalDateTime end = parseDateTime(createTimeEnd);
            if (createTimeEnd != null && !createTimeEnd.trim().isEmpty() && end == null) {
                result.put("success", false);
                result.put("message", "createTimeEnd 格式错误，请使用 yyyy-MM-dd HH:mm:ss");
                return result;
            }
            if (start != null) {
                queryWrapper.ge(LbDeductionAmount::getCreateTime, start);
            }
            if (end != null) {
                queryWrapper.le(LbDeductionAmount::getCreateTime, end);
            }

            queryWrapper.orderByDesc(LbDeductionAmount::getUpdateTime)
                    .orderByDesc(LbDeductionAmount::getCreateTime);

            Page<LbDeductionAmount> page = this.page(new Page<>(current, size), queryWrapper);
            result.put("success", true);
            result.put("message", "查询成功");
            result.put("data", page.getRecords());
            result.put("total", page.getTotal());
            result.put("current", page.getCurrent());
            result.put("size", page.getSize());
            result.put("pages", page.getPages());
            return result;
        } catch (Exception e) {
            log.error("抵扣金记录查询异常", e);
            result.put("success", false);
            result.put("message", "查询异常：" + e.getMessage());
            return result;
        }
    }

    @Override
    public Map<String, BigDecimal> sumDikouAmtByUserNameForReportDate(String tenantId, LocalDate reportDate) {
        Map<String, BigDecimal> byUserName = new HashMap<>();
        if (tenantId == null || tenantId.trim().isEmpty() || reportDate == null) {
            return byUserName;
        }
        LocalDateTime start = reportDate.atStartOfDay();
        LocalDateTime end = reportDate.plusDays(1).atStartOfDay();
        LambdaQueryWrapper<LbDeductionAmount> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(LbDeductionAmount::getTenantId, tenantId.trim())
                .ge(LbDeductionAmount::getCreateTime, start)
                .lt(LbDeductionAmount::getCreateTime, end);
        List<LbDeductionAmount> records = list(queryWrapper);
        for (LbDeductionAmount record : records) {
            if (record.getUserName() == null || record.getUserName().trim().isEmpty()) {
                continue;
            }
            String key = record.getUserName().trim();
            BigDecimal amt = record.getDikouAmt() == null ? BigDecimal.ZERO : record.getDikouAmt();
            byUserName.merge(key, amt, BigDecimal::add);
        }
        return byUserName;
    }

    private static LocalDateTime parseDateTime(String text) {
        if (text == null || text.trim().isEmpty()) {
            return null;
        }
        try {
            return LocalDateTime.parse(text.trim(), DATE_TIME_FORMATTER);
        } catch (Exception e) {
            return null;
        }
    }
}
