package top.tqx.demo_1.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import top.tqx.demo_1.config.AiApiConfig;
import top.tqx.demo_1.constant.CommonConstant;
import top.tqx.demo_1.entity.DeptAdmin;
import top.tqx.demo_1.entity.Department;
import top.tqx.demo_1.entity.KnowledgeBasePath;
import top.tqx.demo_1.entity.User;
import top.tqx.demo_1.mapper.DeptAdminMapper;
import top.tqx.demo_1.mapper.DepartmentMapper;
import top.tqx.demo_1.mapper.KnowledgeBasePathMapper;
import top.tqx.demo_1.mapper.UserMapper;
import top.tqx.demo_1.service.KnowledgeBaseService;
import top.tqx.demo_1.vo.CollectionVO;
import top.tqx.demo_1.vo.ChunkVO;
import top.tqx.demo_1.vo.DocumentVO;

import java.time.LocalDateTime;
import java.util.*;

@Slf4j
@Service
public class KnowledgeBaseServiceImpl extends ServiceImpl<KnowledgeBasePathMapper, KnowledgeBasePath>
        implements KnowledgeBaseService {

    @Autowired
    private KnowledgeBasePathMapper knowledgeBasePathMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private DeptAdminMapper deptAdminMapper;

    @Autowired
    private DepartmentMapper departmentMapper;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private AiApiConfig aiApiConfig;

    @Override
    public List<CollectionVO> getCollections(Long userId, Long deptId, String deptCode) {
        List<KnowledgeBasePath> allKnowledgeBases = list();

        List<CollectionVO> collections = new ArrayList<>();
        for (KnowledgeBasePath kb : allKnowledgeBases) {
            if (canAccess(userId, deptCode, kb)) {
                CollectionVO vo = new CollectionVO();
                vo.setName(kb.getName());
                vo.setDisplayName(kb.getDisplayName() != null ? kb.getDisplayName() : kb.getName());
                vo.setDepartment(kb.getDepartment());
                vo.setDescription(kb.getDescription());
                vo.setDocumentCount(0);
                collections.add(vo);
            }
        }

        log.info("获取用户可访问向量库列表: userId={}, deptId={}, deptCode={}, 数量={}",
                userId, deptId, deptCode, collections.size());
        return collections;
    }

    private boolean canAccess(Long userId, String deptCode, KnowledgeBasePath kb) {
        if (kb.getOwnerType() == null) {
            return false;
        }

        if ("personal".equals(kb.getOwnerType())) {
            return kb.getUploader() != null && kb.getUploader().equals(userId.toString());
        }

        if ("department".equals(kb.getOwnerType())) {
            if ("public".equals(kb.getVisibility())) {
                return true;
            }
            if ("private".equals(kb.getVisibility()) && kb.getDepartment() != null) {
                return kb.getDepartment().equals(deptCode);
            }
        }

        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CollectionVO createCollection(String name, String displayName, String department, String description, Long userId) {
        LambdaQueryWrapper<KnowledgeBasePath> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(KnowledgeBasePath::getName, name);
        KnowledgeBasePath existing = this.getOne(wrapper);

        if (existing != null) {
            throw new RuntimeException("向量库已存在: " + name);
        }

        String departmentName = getDepartmentNameById(department);

        KnowledgeBasePath knowledgeBase = new KnowledgeBasePath();
        knowledgeBase.setName(name);
        knowledgeBase.setDisplayName(displayName);
        knowledgeBase.setPath("/knowledge/" + name);
        knowledgeBase.setDepartment(departmentName);
        knowledgeBase.setDescription(description);
        knowledgeBase.setUploader(userId != null ? userId.toString() : null);
        knowledgeBase.setOwnerType(departmentName != null ? "department" : "personal");
        knowledgeBase.setVisibility("public");
        knowledgeBase.setCreateTime(LocalDateTime.now());
        knowledgeBase.setUpdateTime(LocalDateTime.now());

        boolean saved = this.save(knowledgeBase);
        if (!saved) {
            throw new RuntimeException("创建向量库失败");
        }

        log.info("创建向量库成功: name={}, displayName={}, department={}, userId={}",
                name, displayName, departmentName, userId);

        CollectionVO vo = new CollectionVO();
        vo.setName(knowledgeBase.getName());
        vo.setDisplayName(knowledgeBase.getDisplayName());
        vo.setDepartment(knowledgeBase.getDepartment());
        vo.setDescription(knowledgeBase.getDescription());
        vo.setDocumentCount(0);
        vo.setCreateTime(knowledgeBase.getCreateTime());
        return vo;
    }

    private String getDepartmentNameById(String departmentId) {
        if (departmentId == null || departmentId.isEmpty()) {
            return null;
        }
        try {
            Long deptId = Long.parseLong(departmentId);
            Department dept = departmentMapper.selectById(deptId);
            if (dept != null && dept.getDeptName() != null) {
                return dept.getDeptName();
            }
        } catch (NumberFormatException e) {
            log.warn("部门ID格式不正确: {}", departmentId);
        } catch (Exception e) {
            log.warn("查询部门名称失败: departmentId={}", departmentId, e);
        }
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CollectionVO updateCollection(String name, String displayName, String department, String description) {
        LambdaQueryWrapper<KnowledgeBasePath> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(KnowledgeBasePath::getName, name);
        KnowledgeBasePath knowledgeBase = this.getOne(wrapper);

        if (knowledgeBase == null) {
            throw new RuntimeException("向量库不存在: " + name);
        }

        if (displayName != null) {
            knowledgeBase.setDisplayName(displayName);
        }
        if (department != null) {
            String departmentName = getDepartmentNameById(department);
            knowledgeBase.setDepartment(departmentName);
        }
        if (description != null) {
            knowledgeBase.setDescription(description);
        }
        knowledgeBase.setUpdateTime(LocalDateTime.now());

        boolean updated = this.updateById(knowledgeBase);
        if (!updated) {
            throw new RuntimeException("更新向量库失败");
        }

        log.info("更新向量库成功: name={}, displayName={}, department={}", name, displayName, knowledgeBase.getDepartment());

        CollectionVO vo = new CollectionVO();
        vo.setName(knowledgeBase.getName());
        vo.setDisplayName(knowledgeBase.getDisplayName());
        vo.setDepartment(knowledgeBase.getDepartment());
        vo.setDescription(knowledgeBase.getDescription());
        vo.setUpdateTime(knowledgeBase.getUpdateTime());
        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteCollection(String name) {
        LambdaQueryWrapper<KnowledgeBasePath> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(KnowledgeBasePath::getName, name);
        KnowledgeBasePath knowledgeBase = this.getOne(wrapper);

        if (knowledgeBase == null) {
            throw new RuntimeException("向量库不存在: " + name);
        }

        boolean removed = this.removeById(knowledgeBase.getId());
        if (!removed) {
            throw new RuntimeException("删除向量库失败");
        }

        log.info("删除向量库成功: name={}", name);
    }

    @Override
    public List<ChunkVO> getCollectionChunks(String kbName, String documentId, Integer limit, Integer offset) {
        log.info("获取向量库切片列表: kbName={}, documentId={}, limit={}, offset={}",
                kbName, documentId, limit, offset);

        return new ArrayList<>();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> deprecateDocument(String kbName, String filename, String reason, Long userId) {
        log.info("废止文档: kbName={}, filename={}, reason={}, userId={}",
                kbName, filename, reason, userId);

        Map<String, Object> result = new HashMap<>();
        result.put("success", true);
        result.put("deprecated_chunks", 0);
        result.put("document_id", filename);
        result.put("collection", kbName);
        result.put("deprecated_date", LocalDateTime.now().toString());

        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> restoreDocument(String kbName, String filename) {
        log.info("恢复文档: kbName={}, filename={}", kbName, filename);

        Map<String, Object> result = new HashMap<>();
        result.put("success", true);
        result.put("restored_chunks", 0);
        result.put("document_id", filename);
        result.put("collection", kbName);

        return result;
    }

    @Override
    public List<Map<String, Object>> getDocumentVersions(String kbName, String filename, Integer limit) {
        log.info("获取文档版本历史: kbName={}, filename={}, limit={}", kbName, filename, limit);

        List<Map<String, Object>> versions = new ArrayList<>();

        Map<String, Object> version = new HashMap<>();
        version.put("document_id", filename);
        version.put("collection", kbName);
        version.put("version", "v1");
        version.put("status", "active");
        version.put("effective_date", LocalDateTime.now().minusMonths(1).toString());
        version.put("deprecated_date", null);
        version.put("deprecated_reason", null);
        version.put("change_summary", "初始版本");
        version.put("supersedes", null);
        version.put("created_at", LocalDateTime.now().minusMonths(1).toString());
        version.put("created_by", "system");
        version.put("chunk_count", 0);
        versions.add(version);

        return versions;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DocumentVO uploadDocument(String collection, String filename, byte[] content, Long userId) {
        log.info("上传文档到向量库: collection={}, filename={}, size={}, userId={}",
                collection, filename, content != null ? content.length : 0, userId);

        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }

        validateUploadPermission(user, collection);

        LambdaQueryWrapper<KnowledgeBasePath> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(KnowledgeBasePath::getName, collection);
        KnowledgeBasePath existingKb = this.getOne(wrapper);

        if (existingKb == null) {
            if (isAdmin(user)) {
                createCollectionInRAG(collection, collection, null, "管理员创建的向量库", userId);
            } else {
                throw new RuntimeException("向量库不存在且无权限创建: " + collection);
            }
        }

        DocumentVO doc = uploadToRAG(collection, filename, content);

        if (existingKb != null) {
            existingKb.setUpdateTime(LocalDateTime.now());
            if (content != null) {
                existingKb.setFileSize((long) content.length);
            }
            this.updateById(existingKb);
        }

        return doc;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<DocumentVO> batchUploadDocuments(String collection, List<String> filenames, List<byte[]> contents, Long userId) {
        log.info("批量上传文档到向量库: collection={}, count={}, userId={}",
                collection, filenames != null ? filenames.size() : 0, userId);

        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }

        validateUploadPermission(user, collection);

        LambdaQueryWrapper<KnowledgeBasePath> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(KnowledgeBasePath::getName, collection);
        KnowledgeBasePath existingKb = this.getOne(wrapper);

        if (existingKb == null) {
            if (isAdmin(user)) {
                createCollectionInRAG(collection, collection, null, "管理员创建的向量库", userId);
            } else {
                throw new RuntimeException("向量库不存在且无权限创建: " + collection);
            }
        }

        List<DocumentVO> documents = new ArrayList<>();
        if (filenames != null && contents != null) {
            for (int i = 0; i < filenames.size(); i++) {
                byte[] content = i < contents.size() ? contents.get(i) : null;
                DocumentVO doc = uploadToRAG(collection, filenames.get(i), content);
                documents.add(doc);
            }
        }

        if (existingKb != null) {
            existingKb.setUpdateTime(LocalDateTime.now());
            this.updateById(existingKb);
        }

        return documents;
    }

    private void validateUploadPermission(User user, String collection) {
        if (isAdmin(user)) {
            return;
        }

        if (isDeptAdmin(user)) {
            List<String> allowedCollections = getDeptAdminAllowedCollections(user.getId());
            if (!allowedCollections.contains(collection)) {
                throw new RuntimeException("部门管理员只能上传到本部门所属的向量库");
            }
            return;
        }

        String publicCollection = "user_" + user.getId() + "_public";
        String privateCollection = "user_" + user.getId() + "_private";
        if (!collection.equals(publicCollection) && !collection.equals(privateCollection)) {
            throw new RuntimeException("个人用户只能上传到自己的公开或私密向量库");
        }
    }

    private boolean isAdmin(User user) {
        return CommonConstant.RoleType.SUPER_ADMIN.equals(user.getUserType());
    }

    private boolean isDeptAdmin(User user) {
        LambdaQueryWrapper<DeptAdmin> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(DeptAdmin::getUserId, user.getId())
                .eq(DeptAdmin::getStatus, 1);
        return deptAdminMapper.selectCount(wrapper) > 0;
    }

    private List<String> getDeptAdminAllowedCollections(Long userId) {
        List<String> collections = new ArrayList<>();

        LambdaQueryWrapper<DeptAdmin> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(DeptAdmin::getUserId, userId)
                .eq(DeptAdmin::getStatus, 1);
        List<DeptAdmin> deptAdminList = deptAdminMapper.selectList(wrapper);

        for (DeptAdmin deptAdmin : deptAdminList) {
            Department dept = departmentMapper.selectById(deptAdmin.getDeptId());
            if (dept != null) {
                LambdaQueryWrapper<KnowledgeBasePath> kbWrapper = new LambdaQueryWrapper<>();
                kbWrapper.eq(KnowledgeBasePath::getDepartment, dept.getDeptName());
                List<KnowledgeBasePath> deptKbs = knowledgeBasePathMapper.selectList(kbWrapper);
                for (KnowledgeBasePath kb : deptKbs) {
                    collections.add(kb.getName());
                }
            }
        }

        return collections;
    }

    private void createCollectionInRAG(String name, String displayName, String department, String description, Long userId) {
        try {
            String url = aiApiConfig.getFullUrl(aiApiConfig.getCollection().getCreate());
            log.info("调用RAG服务创建向量库: {}", url);

            String departmentName = getDepartmentNameById(department);

            Map<String, Object> requestBody = new HashMap<>();
            requestBody.put("name", name);
            requestBody.put("display_name", displayName);
            if (departmentName != null) {
                requestBody.put("department", departmentName);
            }
            if (description != null) {
                requestBody.put("description", description);
            }

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);

            ResponseEntity<String> response = restTemplate.postForEntity(url, entity, String.class);

            if (response.getStatusCode().is2xxSuccessful()) {
                log.info("RAG向量库创建成功: {}", name);

                KnowledgeBasePath knowledgeBase = new KnowledgeBasePath();
                knowledgeBase.setName(name);
                knowledgeBase.setDisplayName(displayName);
                knowledgeBase.setPath("/knowledge/" + name);
                knowledgeBase.setDepartment(departmentName);
                knowledgeBase.setDescription(description);
                knowledgeBase.setUploader(userId != null ? userId.toString() : null);
                knowledgeBase.setOwnerType(departmentName != null ? "department" : "personal");
                knowledgeBase.setVisibility("public");
                knowledgeBase.setCreateTime(LocalDateTime.now());
                knowledgeBase.setUpdateTime(LocalDateTime.now());
                this.save(knowledgeBase);
            } else {
                log.warn("RAG向量库创建失败: status={}, body={}", response.getStatusCode(), response.getBody());
                throw new RuntimeException("创建向量库失败: " + response.getBody());
            }
        } catch (Exception e) {
            log.error("调用RAG服务创建向量库失败", e);
            throw new RuntimeException("创建向量库失败: " + e.getMessage());
        }
    }

    private DocumentVO uploadToRAG(String collection, String filename, byte[] content) {
        try {
            String url = aiApiConfig.getFullUrl(aiApiConfig.getDocument().getUpload());
            log.info("调用RAG服务上传文档: {}", url);

            MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
            body.add("file", new ByteArrayResource(content) {
                @Override
                public String getFilename() {
                    return filename;
                }
            });
            body.add("collection", collection);

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.MULTIPART_FORM_DATA);
            HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(body, headers);

            ResponseEntity<Map> response = restTemplate.postForEntity(url, entity, Map.class);

            if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
                Map bodyMap = response.getBody();
                Boolean success = (Boolean) bodyMap.get("success");
                if (success != null && success) {
                    log.info("RAG文档上传成功: {}", filename);

                    DocumentVO doc = new DocumentVO();
                    doc.setFilename(filename);
                    doc.setCollection(collection);
                    doc.setPath(collection + "/" + filename);
                    doc.setSize(content != null ? (long) content.length : 0L);
                    doc.setStatus("active");
                    doc.setChunkCount(0);
                    doc.setCreateTime(LocalDateTime.now());

                    Map fileInfo = (Map) bodyMap.get("file");
                    if (fileInfo != null) {
                        if (fileInfo.containsKey("path")) {
                            doc.setPath((String) fileInfo.get("path"));
                        }
                        if (fileInfo.containsKey("size")) {
                            doc.setSize(((Number) fileInfo.get("size")).longValue());
                        }
                    }

                    Map vectorization = (Map) bodyMap.get("vectorization");
                    if (vectorization != null && vectorization.containsKey("chunk_count")) {
                        doc.setChunkCount(((Number) vectorization.get("chunk_count")).intValue());
                    }

                    return doc;
                } else {
                    String message = (String) bodyMap.get("message");
                    log.warn("RAG文档上传失败: {}", message);
                    throw new RuntimeException("文档上传失败: " + message);
                }
            } else {
                log.warn("RAG文档上传失败: status={}", response.getStatusCode());
                throw new RuntimeException("文档上传失败");
            }
        } catch (Exception e) {
            log.error("调用RAG服务上传文档失败", e);
            throw new RuntimeException("文档上传失败: " + e.getMessage());
        }
    }

    @Override
    public List<DocumentVO> getDocumentList(String collection, Long userId) {
        log.info("获取向量库文档列表: collection={}, userId={}", collection, userId);

        return new ArrayList<>();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteDocument(String path) {
        log.info("删除文档: path={}", path);

        if (path == null || path.isEmpty()) {
            throw new RuntimeException("文档路径不能为空");
        }
    }

    @Override
    public List<ChunkVO> getDocumentChunks(String path) {
        log.info("获取文档切片: path={}", path);

        return new ArrayList<>();
    }
}