package com.pharmacopoeia.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.pharmacopoeia.entity.Drug;
import com.pharmacopoeia.entity.KnowledgePoint;
import com.pharmacopoeia.entity.Question;
import com.pharmacopoeia.repository.DrugRepository;
import com.pharmacopoeia.repository.DrugChunkRepository;
import com.pharmacopoeia.repository.KnowledgePointRepository;
import com.pharmacopoeia.repository.QuestionRepository;
import com.pharmacopoeia.repository.QuickAskRepository;
import com.pharmacopoeia.repository.QuickAskAuditLogRepository;
import com.pharmacopoeia.entity.QuickAsk;
import com.pharmacopoeia.entity.QuickAskAuditLog;
import org.springframework.data.domain.PageRequest;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.pharmacopoeia.util.IpUtils;


import java.util.*;
import java.util.function.Consumer;

@Service
public class AdminKnowledgeService {

    private final DrugRepository drugRepository;
    private final DrugChunkRepository drugChunkRepository;
    private final KnowledgePointRepository knowledgePointRepository;
    private final QuestionRepository questionRepository;
    private final JdbcTemplate jdbc;
    private final ObjectMapper mapper = new ObjectMapper();

    private final QuickAskRepository quickAskRepository;
    private final QuickAskAuditLogRepository auditLogRepository;

    public AdminKnowledgeService(DrugRepository drugRepository,
                                  DrugChunkRepository drugChunkRepository,
                                  KnowledgePointRepository knowledgePointRepository,
                                  QuestionRepository questionRepository,
                                  QuickAskRepository quickAskRepository,
                                  QuickAskAuditLogRepository auditLogRepository,
                                  JdbcTemplate jdbc) {
        this.drugRepository = drugRepository;
        this.drugChunkRepository = drugChunkRepository;
        this.knowledgePointRepository = knowledgePointRepository;
        this.questionRepository = questionRepository;
        this.quickAskRepository = quickAskRepository;
        this.auditLogRepository = auditLogRepository;
        this.jdbc = jdbc;
    }

    // ==================== Drug CRUD ====================

    public Map<String, Object> listDrugs(String keyword, String category, int page, int pageSize) {
        var pageable = PageRequest.of(page - 1, pageSize);
        var result = (keyword != null && !keyword.isBlank())
                ? drugRepository.searchByKeyword(keyword, pageable)
                : (category != null && !category.isBlank())
                        ? drugRepository.findByCategoryAndIsActiveTrue(category, pageable)
                        : drugRepository.findByIsActiveTrue(pageable);

        var items = result.getContent().stream()
                .map(this::drugToMap)
                .collect(java.util.stream.Collectors.toList());

        Map<String, Object> response = new LinkedHashMap<>();
        response.put("items", items);
        response.put("total", result.getTotalElements());
        response.put("page", page);
        response.put("page_size", pageSize);
        return response;
    }

    public Optional<Map<String, Object>> getDrug(String drugId) {
        return drugRepository.findByDrugId(drugId).map(this::drugToMap);
    }

    @Transactional
    public Map<String, Object> createDrug(Map<String, Object> req) {
        Drug drug = Drug.builder()
                .drugId((String) req.get("drugId"))
                .name((String) req.get("name"))
                .nameEn((String) req.get("nameEn"))
                .pinyin((String) req.get("pinyin"))
                .category((String) req.get("category"))
                .subcategory((String) req.get("subcategory"))
                .approvalNumber((String) req.get("approvalNumber"))
                .sourceVersion((String) req.get("sourceVersion"))
                .sourceVolume((String) req.get("sourceVolume"))
                .sourcePage((String) req.get("sourcePage"))
                .isActive(true)
                .build();
        drug.setSections(castToMap(req.get("sections")));
        drugRepository.save(drug);
        return toMap("drug_id", drug.getDrugId(), "已创建");
    }

    @Transactional
    public Map<String, Object> updateDrug(String drugId, Map<String, Object> req) {
        return drugRepository.findByDrugId(drugId).map(d -> {
            applyStrIfPresent(req, "name", d::setName);
            applyStrIfPresent(req, "nameEn", d::setNameEn);
            applyStrIfPresent(req, "pinyin", d::setPinyin);
            applyStrIfPresent(req, "category", d::setCategory);
            applyStrIfPresent(req, "subcategory", d::setSubcategory);
            applyStrIfPresent(req, "approvalNumber", d::setApprovalNumber);
            applyStrIfPresent(req, "sourceVersion", d::setSourceVersion);
            applyStrIfPresent(req, "sourceVolume", d::setSourceVolume);
            applyStrIfPresent(req, "sourcePage", d::setSourcePage);
            applyIfPresent(req, "sections", v -> d.setSections(castToMap(v)));
            applyIfPresent(req, "isActive", v -> d.setIsActive((Boolean) v));
            drugRepository.save(d);
            return toMap("drug_id", drugId, "已更新");
        }).orElseGet(() -> toMap("drug_id", drugId, "药品不存在"));
    }

    @Transactional
    public Map<String, Object> deleteDrug(String drugId) {
        return drugRepository.findByDrugId(drugId).map(d -> {
            d.setIsActive(false);
            drugRepository.save(d);
            return toMap("drug_id", drugId, "已标记删除");
        }).orElseGet(() -> toMap("drug_id", drugId, "药品不存在"));
    }

    // ==================== KnowledgePoint CRUD ====================

    public Map<String, Object> listKnowledgePoints(String subject, String chapterId,
                                                    Integer difficulty, String frequency,
                                                    int page, int pageSize) {
        StringBuilder sql = new StringBuilder("SELECT * FROM knowledge_points WHERE 1=1");
        List<Object> params = new ArrayList<>();

        appendFilters(sql, params,
                new SqlFilter("subject", subject),
                new SqlFilter("chapter_id", chapterId),
                new SqlFilter("difficulty", difficulty),
                new SqlFilter("frequency", frequency));

        sql.append(" ORDER BY created_at DESC LIMIT ? OFFSET ?");
        params.add(pageSize);
        params.add((page - 1) * pageSize);

        List<Map<String, Object>> items = jdbc.queryForList(sql.toString(), params.toArray());

        Map<String, Object> response = new LinkedHashMap<>();
        response.put("items", items);
        response.put("page", page);
        response.put("page_size", pageSize);
        return response;
    }

    public Optional<KnowledgePoint> getKnowledgePoint(String pointId) {
        return knowledgePointRepository.findByPointId(pointId);
    }

    @Transactional
    public Map<String, Object> createKnowledgePoint(Map<String, Object> req) {
        KnowledgePoint kp = KnowledgePoint.builder()
                .pointId((String) req.get("pointId"))
                .subject((String) req.get("subject"))
                .chapterId((String) req.get("chapterId"))
                .chapterName((String) req.get("chapterName"))
                .title((String) req.get("title"))
                .content((String) req.get("content"))
                .difficulty(req.get("difficulty") != null ? (Integer) req.get("difficulty") : 1)
                .frequency((String) req.get("frequency"))
                .source((String) req.get("source"))
                .build();
        writeJsonIfPresent(req, "relatedDrugs", kp::setRelatedDrugs);
        knowledgePointRepository.save(kp);
        return toMap("point_id", kp.getPointId(), "已创建");
    }

    @Transactional
    public Map<String, Object> updateKnowledgePoint(String pointId, Map<String, Object> req) {
        return knowledgePointRepository.findByPointId(pointId).map(kp -> {
            applyStrIfPresent(req, "chapterName", kp::setChapterName);
            applyStrIfPresent(req, "title", kp::setTitle);
            applyStrIfPresent(req, "content", kp::setContent);
            applyIfPresent(req, "difficulty", v -> kp.setDifficulty((Integer) v));
            applyStrIfPresent(req, "frequency", kp::setFrequency);
            applyStrIfPresent(req, "source", kp::setSource);
            writeJsonIfPresent(req, "relatedDrugs", kp::setRelatedDrugs);
            knowledgePointRepository.save(kp);
            return toMap("point_id", pointId, "已更新");
        }).orElseGet(() -> toMap("point_id", pointId, "知识点不存在"));
    }

    @Transactional
    public Map<String, Object> deleteKnowledgePoint(String pointId) {
        knowledgePointRepository.findByPointId(pointId).ifPresent(knowledgePointRepository::delete);
        return toMap("point_id", pointId, "已删除");
    }

    // ==================== Question CRUD ====================

    public Map<String, Object> listQuestions(String subject, String chapterId,
                                              String questionType, Integer difficulty,
                                              Boolean audited, int page, int pageSize) {
        StringBuilder sql = new StringBuilder("SELECT * FROM questions WHERE 1=1");
        List<Object> params = new ArrayList<>();

        appendFilters(sql, params,
                new SqlFilter("subject", subject),
                new SqlFilter("chapter_id", chapterId),
                new SqlFilter("question_type", questionType),
                new SqlFilter("difficulty", difficulty),
                new SqlFilter("audited", audited));

        sql.append(" ORDER BY created_at DESC LIMIT ? OFFSET ?");
        params.add(pageSize);
        params.add((page - 1) * pageSize);

        List<Map<String, Object>> items = jdbc.queryForList(sql.toString(), params.toArray());

        Map<String, Object> response = new LinkedHashMap<>();
        response.put("items", items);
        response.put("page", page);
        response.put("page_size", pageSize);
        return response;
    }

    public Optional<Question> getQuestion(String questionId) {
        return questionRepository.findByQuestionId(questionId);
    }

    @Transactional
    public Map<String, Object> createQuestion(Map<String, Object> req) {
        Question q = Question.builder()
                .questionId((String) req.get("questionId"))
                .questionType((String) req.get("questionType"))
                .subject((String) req.get("subject"))
                .chapterId((String) req.get("chapterId"))
                .difficulty(req.get("difficulty") != null ? (Integer) req.get("difficulty") : 1)
                .content((String) req.get("content"))
                .answer((String) req.get("answer"))
                .explanation((String) req.get("explanation"))
                .frequency((String) req.get("frequency"))
                .source((String) req.get("source"))
                .audited(false)
                .correctCount(0)
                .attemptCount(0)
                .build();
        writeJsonIfPresent(req, "options", q::setOptions);
        writeJsonIfPresent(req, "knowledgePointIds", q::setKnowledgePointIds);
        questionRepository.save(q);
        return toMap("question_id", q.getQuestionId(), "已创建");
    }

    @Transactional
    public Map<String, Object> updateQuestion(String questionId, Map<String, Object> req) {
        return questionRepository.findByQuestionId(questionId).map(q -> {
            applyStrIfPresent(req, "questionType", q::setQuestionType);
            applyStrIfPresent(req, "subject", q::setSubject);
            applyStrIfPresent(req, "chapterId", q::setChapterId);
            applyIfPresent(req, "difficulty", v -> q.setDifficulty((Integer) v));
            applyStrIfPresent(req, "content", q::setContent);
            applyStrIfPresent(req, "answer", q::setAnswer);
            applyStrIfPresent(req, "explanation", q::setExplanation);
            applyStrIfPresent(req, "frequency", q::setFrequency);
            applyStrIfPresent(req, "source", q::setSource);
            applyIfPresent(req, "audited", v -> q.setAudited((Boolean) v));
            writeJsonIfPresent(req, "options", q::setOptions);
            writeJsonIfPresent(req, "knowledgePointIds", q::setKnowledgePointIds);
            questionRepository.save(q);
            return toMap("question_id", questionId, "已更新");
        }).orElseGet(() -> toMap("question_id", questionId, "题目不存在"));
    }

    @Transactional
    public Map<String, Object> deleteQuestion(String questionId) {
        questionRepository.findByQuestionId(questionId).ifPresent(questionRepository::delete);
        return toMap("question_id", questionId, "已删除");
    }

    @Transactional
    public Map<String, Object> batchAudit(List<String> questionIds, boolean audited) {
        for (String qid : questionIds) {
            questionRepository.findByQuestionId(qid).ifPresent(q -> {
                q.setAudited(audited);
                questionRepository.save(q);
            });
        }
        Map<String, Object> r = new LinkedHashMap<>();
        r.put("message", "批量审核完成");
        r.put("question_ids", questionIds);
        r.put("audited", audited);
        return r;
    }

    // ==================== 快捷提问标签管理 ====================

    /** 获取所有激活的标签，按 sort_order 排序（公开接口，无需鉴权） */
    public List<QuickAsk> listQuickAsks() {
        return quickAskRepository.findByIsActiveTrueOrderBySortOrderAscIdAsc();
    }

    /** 管理端：获取所有标签（含禁用） */
    public List<QuickAsk> listAllQuickAsks() {
        return quickAskRepository.findAllByOrderBySortOrderAscIdAsc();
    }

    /** 新增标签 */
    @Transactional
    public Map<String, Object> createQuickAsk(String label, String question, int sortOrder) {
        if (label == null || label.isBlank()) {
            return Map.of("ok", false, "message", "请输入推荐位文字");
        }
        String trimmedLabel = label.trim();
        if (trimmedLabel.length() < 2 || trimmedLabel.length() > 20) {
            return Map.of("ok", false, "message", "推荐位文字长度需在2-20个字符之间");
        }
        if (quickAskRepository.count() >= 6) {
            return Map.of("ok", false, "message", "最多只能添加6个推荐位");
        }
        if (quickAskRepository.countByLabel(trimmedLabel) > 0) {
            return Map.of("ok", false, "message", "推荐位文字不能重复");
        }
        String operator = getCurrentOperator();
        QuickAsk qa = QuickAsk.builder()
                .label(trimmedLabel)
                .question(question != null ? question.trim() : trimmedLabel)
                .sortOrder(sortOrder)
                .isActive(true)
                .createdBy(operator)
                .build();
        qa = quickAskRepository.save(qa);
        writeAuditLog(qa.getId(), "CREATE", operator, qa.getLabel(), qa.getQuestion(), qa.getSortOrder(), qa.getIsActive());
        return Map.of("ok", true, "id", qa.getId(), "message", "已创建");
    }

    /** 更新标签 */
    @Transactional
    public Map<String, Object> updateQuickAsk(int id, String label, String question, Integer sortOrder, Boolean isActive) {
        var opt = quickAskRepository.findById(id);
        if (opt.isEmpty()) {
            return Map.of("ok", false, "message", "未找到");
        }
        QuickAsk qa = opt.get();
        if (label != null && !label.isBlank()) {
            String trimmedLabel = label.trim();
            if (quickAskRepository.countByLabelAndIdNot(trimmedLabel, id) > 0) {
                return Map.of("ok", false, "message", "推荐位文字不能重复");
            }
            qa.setLabel(trimmedLabel);
        }
        Optional.ofNullable(question).ifPresent(v -> qa.setQuestion(v.trim()));
        Optional.ofNullable(sortOrder).ifPresent(qa::setSortOrder);
        Optional.ofNullable(isActive).ifPresent(qa::setIsActive);
        String operator = getCurrentOperator();
        qa.setUpdatedBy(operator);
        quickAskRepository.save(qa);
        writeAuditLog(id, "UPDATE", operator, qa.getLabel(), qa.getQuestion(), qa.getSortOrder(), qa.getIsActive());
        return Map.of("ok", true, "id", id, "message", "已更新");
    }

    /** 删除标签 */
    @Transactional
    public Map<String, Object> deleteQuickAsk(int id) {
        var opt = quickAskRepository.findById(id);
        if (opt.isEmpty()) {
            return Map.of("ok", false, "message", "未找到");
        }
        QuickAsk qa = opt.get();
        String operator = getCurrentOperator();
        writeAuditLog(id, "DELETE", operator, qa.getLabel(), qa.getQuestion(), qa.getSortOrder(), qa.getIsActive());
        quickAskRepository.delete(qa);
        return Map.of("ok", true, "message", "已删除");
    }

    // ==================== 排序管理 ====================

    public Map<String, Object> reorderQuickAsks(List<Map<String, Object>> items) {
        String operator = getCurrentOperator();
        int updated = 0;
        for (Map<String, Object> item : items) {
            int id = ((Number) item.get("id")).intValue();
            int sortOrder = ((Number) item.get("sort_order")).intValue();
            updated += quickAskRepository.updateSortOrder(id, sortOrder, operator);
        }
        return Map.of("ok", updated > 0, "message", "已更新 " + updated + " 条排序");
    }

    private void writeAuditLog(int quickAskId, String action, String operator,
                               String label, String question, Integer sortOrder, Boolean isActive) {
        auditLogRepository.save(QuickAskAuditLog.builder()
                .quickAskId(quickAskId).action(action)
                .label(label).question(question)
                .sortOrder(sortOrder != null ? sortOrder : 0)
                .isActive(isActive != null ? isActive : true)
                .operator(operator)
                .ip(getClientIp())
                .build());
    }

    /** 从当前请求中获取客户端 IP */
    private static String getClientIp() {
        var attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attrs != null) {
            return IpUtils.getClientIp(attrs.getRequest());
        }
        return "unknown";
    }

    public Map<String, Object> listQuickAskAuditLog(int page, int pageSize) {
        var pageable = org.springframework.data.domain.PageRequest.of(page - 1, pageSize);
        var result = auditLogRepository.findAllByOrderByCreatedAtDesc(pageable);
        return Map.of(
                "items", result.getContent(),
                "page", page,
                "page_size", pageSize,
                "total", result.getTotalElements(),
                "total_pages", result.getTotalPages()
        );
    }

    // ==================== Helpers ====================

    /** 从 SecurityContext 获取当前操作员标识 */
    private String getCurrentOperator() {
        var auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null && auth.isAuthenticated() && !"anonymousUser".equals(auth.getPrincipal())) {
            return auth.getName();
        }
        return "anonymous";
    }

    @SuppressWarnings("unchecked")
    private Map<String, Object> castToMap(Object obj) {
        if (obj instanceof Map) {
            return (Map<String, Object>) obj;
        }
        return new HashMap<>();
    }

    // ---- 通用 if-reduction helpers ----

    /** map 中存在 key 且非 null 时执行 setter */
    private void applyIfPresent(Map<String, Object> req, String key, Consumer<Object> setter) {
        Optional.ofNullable(req.get(key)).ifPresent(setter);
    }

    /** map 中存在 key 且为 非空字符串 时执行 setter */
    private void applyStrIfPresent(Map<String, Object> req, String key, Consumer<String> setter) {
        Optional.ofNullable(req.get(key))
                .map(Object::toString)
                .filter(s -> !s.isBlank())
                .ifPresent(setter);
    }

    /** 将 map 中的对象序列化为 JSON，非 null 时执行 setter */
    private void writeJsonIfPresent(Map<String, Object> req, String key, Consumer<String> setter) {
        Optional.ofNullable(req.get(key)).ifPresent(val -> {
            try { setter.accept(mapper.writeValueAsString(val)); } catch (JsonProcessingException ignored) {}
        });
    }

    /** SQL 动态筛选字段 */
    private record SqlFilter(String column, Object value) {}

    private void appendFilters(StringBuilder sql, List<Object> params, SqlFilter... filters) {
        for (var f : filters) {
            if (f.value == null || f.value instanceof String s && s.isBlank()) {
                continue;
            }
            sql.append(" AND ").append(f.column).append(" = ?");
            params.add(f.value);
        }
    }

    private Map<String, Object> drugToMap(Drug d) {
        Map<String, Object> m = new LinkedHashMap<>();
        m.put("drug_id", d.getDrugId());
        m.put("name", d.getName());
        m.put("name_en", d.getNameEn());
        m.put("pinyin", d.getPinyin());
        m.put("category", d.getCategory());
        m.put("subcategory", d.getSubcategory());
        m.put("approval_number", d.getApprovalNumber());
        m.put("sections", d.getSections());
        m.put("source_version", d.getSourceVersion());
        m.put("source_volume", d.getSourceVolume());
        m.put("source_page", d.getSourcePage());
        m.put("is_active", d.getIsActive());
        m.put("created_at", d.getCreatedAt());
        m.put("updated_at", d.getUpdatedAt());
        return m;
    }

    private Map<String, Object> toMap(String k1, Object v1, Object v2) {
        Map<String, Object> m = new LinkedHashMap<>();
        m.put(k1, v1);
        m.put("message", v2);
        return m;
    }
}
