package com.agent.management.service.impl;

import com.agent.management.common.exception.BusinessException;
import com.agent.management.model.entity.DataSource;
import com.agent.management.repository.DataSourceRepository;
import com.agent.management.service.CredentialManager;
import com.agent.management.service.DataSourceService;
import com.agent.management.service.DynamicJdbcService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 数据源 Service：负责 CRUD + 密码 Jasypt 加解密 + 连接测试
 *
 * <p>关键设计：从 Repository 拿到 DataSource 后，必须调用
 * {@link CredentialManager#decryptToTransient} 把 passwordEnc 解密后回填到
 * decryptedPassword 字段（{@code @Transient} + {@code @JsonIgnore}），
 * 这样下游 DynamicJdbcService 可以透明使用，且 passwordEnc 字段始终保持密文语义。</p>
 *
 * <p>Controller 永远不返回 password 相关字段，由 {@link CredentialManager#sanitize}
 * 在 Service 出口统一清空。</p>
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DataSourceServiceImpl implements DataSourceService {

    private final DataSourceRepository repository;
    private final DynamicJdbcService jdbcService;
    private final CredentialManager credentialManager;

    @Override
    public DataSource create(DataSource ds) {
        validateRequired(ds);
        if (repository.existsByName(ds.getName())) {
            throw new BusinessException("数据源名称已存在: " + ds.getName());
        }
        // 密码加密入库
        if (ds.getPasswordPlain() == null || ds.getPasswordPlain().isEmpty()) {
            throw new BusinessException("密码不能为空");
        }
        ds.setPasswordEnc(credentialManager.encrypt(ds.getPasswordPlain()));
        ds.setPasswordPlain(null);  // 清空明文
        DataSource saved = repository.save(ds);
        log.info("已创建数据源 id={}, name={}", saved.getId(), saved.getName());
        return sanitize(saved);
    }

    @Override
    public DataSource update(Long id, DataSource incoming) {
        DataSource 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.getType() != null) existing.setType(incoming.getType());
        if (incoming.getJdbcUrl() != null) existing.setJdbcUrl(incoming.getJdbcUrl());
        if (incoming.getUsername() != null) existing.setUsername(incoming.getUsername());
        if (incoming.getSchemaName() != null) existing.setSchemaName(incoming.getSchemaName());
        // 密码：如果传了新明文密码则重新加密；否则保留原密文
        if (incoming.getPasswordPlain() != null && !incoming.getPasswordPlain().isEmpty()) {
            existing.setPasswordEnc(credentialManager.encrypt(incoming.getPasswordPlain()));
        }

        DataSource saved = repository.save(existing);
        // 密码或 URL 变更，旧的连接池需要失效
        jdbcService.invalidate(id);
        log.info("已更新数据源 id={}", id);
        return sanitize(saved);
    }

    @Override
    public void delete(Long id) {
        DataSource ds = repository.findById(id)
                .orElseThrow(() -> new BusinessException(404, "数据源不存在"));
        jdbcService.invalidate(id);
        repository.delete(ds);
        log.info("已删除数据源 id={}", id);
    }

    @Override
    public TestResult testConnection(DataSource ds) {
        validateRequired(ds);
        String plain = ds.getPasswordPlain();
        if (plain == null || plain.isEmpty()) {
            // 可能是用现有数据源测试（passwordPlain 为空），需要解密 passwordEnc
            if (ds.getPasswordEnc() != null) {
                plain = credentialManager.decrypt(ds.getPasswordEnc());
            } else {
                return new TestResult(false, "密码未提供");
            }
        }
        try {
            jdbcService.testConnection(ds, plain);
            return new TestResult(true, "连接成功");
        } catch (Exception e) {
            return new TestResult(false, e.getMessage());
        }
    }

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

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

    @Override
    public DataSource getDecrypted(Long id) {
        DataSource ds = repository.findById(id)
                .orElseThrow(() -> new BusinessException(404, "数据源不存在"));
        credentialManager.decryptToTransient(ds);
        return ds;
    }

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

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

    private void validateRequired(DataSource ds) {
        if (ds.getName() == null || ds.getName().trim().isEmpty())
            throw new BusinessException("数据源名称不能为空");
        if (ds.getType() == null || ds.getType().trim().isEmpty())
            throw new BusinessException("数据源类型不能为空");
        if (ds.getJdbcUrl() == null || ds.getJdbcUrl().trim().isEmpty())
            throw new BusinessException("JDBC URL 不能为空");
        if (ds.getUsername() == null || ds.getUsername().trim().isEmpty())
            throw new BusinessException("用户名不能为空");
    }

    private DataSource sanitize(DataSource ds) {
        DataSource safe = new DataSource();
        safe.setId(ds.getId());
        safe.setName(ds.getName());
        safe.setType(ds.getType());
        safe.setJdbcUrl(ds.getJdbcUrl());
        safe.setUsername(ds.getUsername());
        safe.setSchemaName(ds.getSchemaName());
        safe.setTestStatus(ds.getTestStatus());
        safe.setTestMessage(ds.getTestMessage());
        safe.setCreatedAt(ds.getCreatedAt());
        safe.setUpdatedAt(ds.getUpdatedAt());
        return safe;
    }
}
