package com.agent.management.service.impl;

import com.agent.management.common.exception.BusinessException;
import com.agent.management.model.entity.GraphSource;
import com.agent.management.rag.graph.GraphSchemaSnapshot;
import com.agent.management.repository.GraphSourceRepository;
import com.agent.management.service.CredentialManager;
import com.agent.management.service.CypherGuardService;
import com.agent.management.service.GraphSourceService;
import com.agent.management.service.Neo4jExecutorService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

/**
 * 知识图谱源 Service：CRUD + Jasypt 加密 + 测试连接 + Cypher 执行
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class GraphSourceServiceImpl implements GraphSourceService {

    private final GraphSourceRepository repository;
    private final Neo4jExecutorService neo4jExecutor;
    private final CredentialManager credentialManager;
    private final CypherGuardService cypherGuard;

    @Override
    public GraphSource create(GraphSource gs) {
        validateRequired(gs);
        if (repository.existsByName(gs.getName())) {
            throw new BusinessException("图谱源名称已存在: " + gs.getName());
        }
        if (gs.getPasswordPlain() == null || gs.getPasswordPlain().isEmpty()) {
            throw new BusinessException("密码不能为空");
        }
        gs.setPasswordEnc(credentialManager.encrypt(gs.getPasswordPlain()));
        gs.setPasswordPlain(null);
        GraphSource saved = repository.save(gs);
        log.info("已创建图谱源 id={}, name={}", saved.getId(), saved.getName());
        return sanitize(saved);
    }

    @Override
    public GraphSource update(Long id, GraphSource incoming) {
        GraphSource existing = repository.findById(id)
                .orElseThrow(() -> new BusinessException(404, "图谱源不存在"));

        if (incoming.getName() != null && !incoming.getName().equals(existing.getName())) {
            if (repository.existsByName(incoming.getName())) {
                throw new BusinessException("图谱源名称已存在: " + incoming.getName());
            }
            existing.setName(incoming.getName());
        }
        if (incoming.getUri() != null) existing.setUri(incoming.getUri());
        if (incoming.getUsername() != null) existing.setUsername(incoming.getUsername());
        if (incoming.getDatabase() != null) existing.setDatabase(incoming.getDatabase());
        if (incoming.getPasswordPlain() != null && !incoming.getPasswordPlain().isEmpty()) {
            existing.setPasswordEnc(credentialManager.encrypt(incoming.getPasswordPlain()));
        }

        GraphSource saved = repository.save(existing);
        neo4jExecutor.invalidate(id);
        log.info("已更新图谱源 id={}", id);
        return sanitize(saved);
    }

    @Override
    public void delete(Long id) {
        GraphSource gs = repository.findById(id)
                .orElseThrow(() -> new BusinessException(404, "图谱源不存在"));
        neo4jExecutor.invalidate(id);
        repository.delete(gs);
        log.info("已删除图谱源 id={}", id);
    }

    @Override
    public TestResult testConnection(GraphSource gs) {
        validateRequired(gs);
        String plain = gs.getPasswordPlain();
        if (plain == null || plain.isEmpty()) {
            if (gs.getPasswordEnc() != null) {
                plain = credentialManager.decrypt(gs.getPasswordEnc());
            } else {
                return new TestResult(false, "密码未提供");
            }
        }
        try {
            neo4jExecutor.testConnection(gs.getUri(), gs.getUsername(), plain, gs.getDatabase());
            return new TestResult(true, "连接成功");
        } catch (Exception e) {
            return new TestResult(false, e.getMessage());
        }
    }

    @Override
    public TestResult testExisting(Long id) {
        GraphSource gs = getDecrypted(id);
        try {
            neo4jExecutor.testConnection(gs.getUri(), gs.getUsername(),
                    gs.getDecryptedPassword(), gs.getDatabase());
            gs.setTestStatus("SUCCESS");
            gs.setTestMessage("连接成功");
            repository.save(gs);
            return new TestResult(true, "连接成功");
        } catch (Exception e) {
            gs.setTestStatus("FAILED");
            gs.setTestMessage(e.getMessage());
            repository.save(gs);
            return new TestResult(false, e.getMessage());
        }
    }

    @Override
    public GraphSource get(Long id) {
        return sanitize(repository.findById(id)
                .orElseThrow(() -> new BusinessException(404, "图谱源不存在")));
    }

    @Override
    public GraphSource getDecrypted(Long id) {
        GraphSource gs = repository.findById(id)
                .orElseThrow(() -> new BusinessException(404, "图谱源不存在"));
        credentialManager.decryptToTransient(gs);
        return gs;
    }

    @Override
    public List<GraphSource> listAll() {
        return repository.findAllByOrderByIdDesc().stream().map(this::sanitize).toList();
    }

    @Override
    public Map<String, Object> executeQuery(Long id, String cypher) {
        cypherGuard.validate(cypher);
        GraphSource gs = getDecrypted(id);
        return neo4jExecutor.executeQuery(gs, cypher);
    }

    @Override
    public void explainQuery(Long id, String cypher) {
        cypherGuard.validate(cypher);
        neo4jExecutor.executeQuery(getDecrypted(id), "EXPLAIN " + cypher);
    }

    @Override
    public List<String> listLabels(Long id) {
        GraphSource gs = getDecrypted(id);
        return neo4jExecutor.listLabels(gs);
    }

    @Override
    public GraphSchemaSnapshot discoverSchema(Long id) {
        return neo4jExecutor.discoverSchema(getDecrypted(id));
    }

    @Override
    public Map<String,Object> discoverMetadata(Long id) { return neo4jExecutor.discoverMetadata(getDecrypted(id)); }

    // =========================== 内部 ===========================

    private void validateRequired(GraphSource gs) {
        if (gs.getName() == null || gs.getName().trim().isEmpty())
            throw new BusinessException("图谱源名称不能为空");
        if (gs.getUri() == null || gs.getUri().trim().isEmpty())
            throw new BusinessException("Neo4j URI 不能为空");
        if (gs.getUsername() == null || gs.getUsername().trim().isEmpty())
            throw new BusinessException("用户名不能为空");
    }

    private GraphSource sanitize(GraphSource gs) {
        GraphSource safe = new GraphSource();
        safe.setId(gs.getId());
        safe.setName(gs.getName());
        safe.setUri(gs.getUri());
        safe.setUsername(gs.getUsername());
        safe.setDatabase(gs.getDatabase());
        safe.setTestStatus(gs.getTestStatus());
        safe.setTestMessage(gs.getTestMessage());
        safe.setCreatedAt(gs.getCreatedAt());
        safe.setUpdatedAt(gs.getUpdatedAt());
        return safe;
    }
}
