package tech.easyflow.datacenter.execution.service.impl;

import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.Row;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.datacenter.connector.DatacenterConnector;
import tech.easyflow.datacenter.connector.DatacenterConnectorRegistry;
import tech.easyflow.datacenter.entity.DatacenterTable;
import tech.easyflow.datacenter.entity.DatacenterTableField;
import tech.easyflow.datacenter.execution.model.DatacenterQueryRequest;
import tech.easyflow.datacenter.execution.model.DatacenterSchemaResponse;
import tech.easyflow.datacenter.execution.model.DatacenterSqlQueryRequest;
import tech.easyflow.datacenter.execution.model.DatasetRef;
import tech.easyflow.datacenter.execution.service.DatacenterDatasetQueryService;
import tech.easyflow.datacenter.mapper.DatacenterCatalogMapper;
import tech.easyflow.datacenter.mapper.DatacenterDatasetVersionMapper;
import tech.easyflow.datacenter.mapper.DatacenterDerivedTableMapper;
import tech.easyflow.datacenter.mapper.DatacenterTableMapper;
import tech.easyflow.datacenter.meta.entity.DatacenterCatalog;
import tech.easyflow.datacenter.meta.entity.DatacenterDatasetVersion;
import tech.easyflow.datacenter.meta.entity.DatacenterDerivedTable;
import tech.easyflow.datacenter.meta.entity.DatacenterSource;
import tech.easyflow.datacenter.meta.service.DatacenterDatasetRegistryService;
import tech.easyflow.datacenter.utils.SqlSupportUtils;

import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Service
public class DatacenterDatasetQueryServiceImpl implements DatacenterDatasetQueryService {

    @Resource
    private DatacenterDatasetRegistryService registryService;
    @Resource
    private DatacenterConnectorRegistry connectorRegistry;
    @Resource
    private DatacenterTableMapper tableMapper;
    @Resource
    private DatacenterCatalogMapper catalogMapper;
    @Resource
    private DatacenterDatasetVersionMapper datasetVersionMapper;
    @Resource
    private DatacenterDerivedTableMapper derivedTableMapper;

    @Override
    public Page<Row> queryPage(DatacenterQueryRequest request) {
        if (request == null || request.getDatasetRef() == null) {
            throw new BusinessException("datasetRef 不能为空");
        }
        normalizePage(request);
        DatacenterTable table = resolveTable(request.getDatasetRef());
        DatacenterSource source = registryService.getSourceRequired(table.getSourceId());
        DatacenterCatalog catalog = registryService.getCatalogById(table.getCatalogId());
        DatacenterTable queryTable = resolveQueryTable(table, request.getDatasetRef());
        validateRequest(queryTable, request, source);
        request.getDatasetRef().setSourceId(table.getSourceId());
        request.getDatasetRef().setCatalogId(table.getCatalogId());
        request.getDatasetRef().setTableId(table.getId());
        request.getDatasetRef().setTableName(table.getTableName());
        if (catalog != null) {
            request.getDatasetRef().setCatalogName(catalog.getCatalogName());
        }
        DatacenterConnector connector = connectorRegistry.getConnector(source.getSourceType());
        return connector.queryPage(source, queryTable, request);
    }

    @Override
    public List<Row> queryBySql(DatacenterSqlQueryRequest request) {
        if (request == null || request.getDatasetRef() == null) {
            throw new BusinessException("datasetRef 不能为空");
        }
        String sql = trimToNull(request.getSql());
        if (!StringUtils.hasText(sql)) {
            throw new BusinessException("SQL 不能为空");
        }
        DatasetRef datasetRef = request.getDatasetRef();
        if (datasetRef.getSourceId() == null) {
            throw new BusinessException("缺少连接服务配置");
        }
        DatacenterSource source = registryService.getSourceRequired(datasetRef.getSourceId());
        BigInteger catalogId = resolveRequestedCatalogId(datasetRef);
        List<DatacenterTable> managedTables = registryService.listManagedTables(datasetRef.getSourceId(), catalogId);
        if (CollectionUtils.isEmpty(managedTables)) {
            throw new BusinessException("当前连接下没有已接入表");
        }
        SqlSupportUtils.ResolvedSql resolvedSql = SqlSupportUtils.resolve(
                sql,
                managedTables.stream().map(this::toManagedSqlTable).toList()
        );
        DatacenterConnector connector = connectorRegistry.getConnector(source.getSourceType());
        return connector.queryBySql(source, resolvedSql.getExecutableSql());
    }

    @Override
    public DatacenterSchemaResponse getSchema(DatasetRef datasetRef) {
        DatacenterTable table = resolveTable(datasetRef);
        DatacenterSource source = registryService.getSourceRequired(table.getSourceId());
        DatacenterCatalog catalog = registryService.getCatalogById(table.getCatalogId());
        DatacenterSchemaResponse response = new DatacenterSchemaResponse();
        response.setDatasetRef(registryService.resolveDatasetRef(table.getId()));
        response.setSource(source);
        response.setCatalog(catalog);
        response.setTable(table);
        response.setFields(table.getFields());
        response.setVersions(listVersions(table.getId()));
        response.setUpstreamLineage(listUpstream(table.getId()));
        response.setDownstreamLineage(listDownstream(table.getId()));
        return response;
    }

    private DatacenterTable resolveTable(DatasetRef datasetRef) {
        if (datasetRef.getTableId() != null) {
            return registryService.getTableWithFields(datasetRef.getTableId());
        }
        if (datasetRef.getSourceId() == null || !StringUtils.hasText(datasetRef.getTableName())) {
            throw new BusinessException("缺少数据集定位信息");
        }
        QueryWrapper wrapper = QueryWrapper.create();
        wrapper.eq(DatacenterTable::getSourceId, datasetRef.getSourceId());
        wrapper.eq(DatacenterTable::getTableName, datasetRef.getTableName().trim());

        boolean hasCatalogCondition = false;
        if (datasetRef.getCatalogId() != null) {
            wrapper.eq(DatacenterTable::getCatalogId, datasetRef.getCatalogId());
            hasCatalogCondition = true;
        } else if (StringUtils.hasText(datasetRef.getCatalogName())) {
            DatacenterCatalog catalog = resolveCatalog(datasetRef.getSourceId(), datasetRef.getCatalogName().trim());
            wrapper.eq(DatacenterTable::getCatalogId, catalog.getId());
            hasCatalogCondition = true;
        }

        List<DatacenterTable> tables = tableMapper.selectListByQuery(wrapper);
        if (CollectionUtils.isEmpty(tables)) {
            throw new BusinessException("数据集不存在: " + datasetRef.getTableName());
        }
        if (!hasCatalogCondition && tables.size() > 1) {
            throw new BusinessException("数据集存在重名表，请指定库名: " + datasetRef.getTableName());
        }
        return registryService.getTableWithFields(tables.get(0).getId());
    }

    private DatacenterCatalog resolveCatalog(java.math.BigInteger sourceId, String catalogName) {
        QueryWrapper wrapper = QueryWrapper.create();
        wrapper.eq(DatacenterCatalog::getSourceId, sourceId);
        wrapper.eq(DatacenterCatalog::getCatalogName, catalogName);
        List<DatacenterCatalog> catalogs = catalogMapper.selectListByQuery(wrapper);
        if (CollectionUtils.isEmpty(catalogs)) {
            throw new BusinessException("库不存在: " + catalogName);
        }
        if (catalogs.size() > 1) {
            throw new BusinessException("库存在重复配置，请检查: " + catalogName);
        }
        return catalogs.get(0);
    }

    private BigInteger resolveRequestedCatalogId(DatasetRef datasetRef) {
        if (datasetRef == null) {
            return null;
        }
        if (datasetRef.getCatalogId() != null) {
            return datasetRef.getCatalogId();
        }
        if (StringUtils.hasText(datasetRef.getCatalogName())) {
            DatacenterCatalog catalog = resolveCatalog(datasetRef.getSourceId(), datasetRef.getCatalogName().trim());
            return catalog.getId();
        }
        return null;
    }

    private SqlSupportUtils.ManagedTable toManagedSqlTable(DatacenterTable table) {
        DatacenterCatalog catalog = registryService.getCatalogById(table.getCatalogId());
        return new SqlSupportUtils.ManagedTable(
                catalog == null ? null : catalog.getCatalogName(),
                table.getTableName(),
                resolvePhysicalTableName(table)
        );
    }

    private String resolvePhysicalTableName(DatacenterTable table) {
        if (table == null) {
            return null;
        }
        if ("EXTERNAL_TABLE".equals(table.getTableKind()) || "EXTERNAL_VIEW".equals(table.getTableKind())) {
            return trimToNull(table.getActualTable()) == null ? table.getTableName() : table.getActualTable().trim();
        }
        String materializedTable = trimToNull(table.getMaterializedTable());
        if (materializedTable != null) {
            return materializedTable;
        }
        String actualTable = trimToNull(table.getActualTable());
        return actualTable != null ? actualTable : table.getTableName();
    }

    private void normalizePage(DatacenterQueryRequest request) {
        if (request.getPageNumber() == null || request.getPageNumber() < 1L) {
            request.setPageNumber(1L);
        }
        if (request.getPageSize() == null || request.getPageSize() < 1L) {
            throw new BusinessException("pageSize 必须大于 0");
        }
    }

    private DatacenterTable resolveQueryTable(DatacenterTable table, DatasetRef datasetRef) {
        if (datasetRef == null || datasetRef.getVersionId() == null) {
            return table;
        }
        DatacenterDatasetVersion version = datasetVersionMapper.selectOneById(datasetRef.getVersionId());
        if (version == null || !table.getId().equals(version.getTableId())) {
            throw new BusinessException("数据集版本不存在: " + datasetRef.getVersionId());
        }
        DatacenterTable queryTable = new DatacenterTable();
        queryTable.setId(table.getId());
        queryTable.setSourceId(table.getSourceId());
        queryTable.setCatalogId(table.getCatalogId());
        queryTable.setTableName(table.getTableName());
        queryTable.setTableDesc(table.getTableDesc());
        queryTable.setActualTable(table.getActualTable());
        queryTable.setMaterializedTable(version.getMaterializedTable());
        queryTable.setAccessMode(table.getAccessMode());
        queryTable.setTableKind(table.getTableKind());
        queryTable.setVersioningEnabled(table.getVersioningEnabled());
        queryTable.setCapabilitiesJson(table.getCapabilitiesJson());
        queryTable.setFields(table.getFields());
        return queryTable;
    }

    private void validateRequest(DatacenterTable table, DatacenterQueryRequest request, DatacenterSource source) {
        Map<String, DatacenterTableField> fieldMap = new LinkedHashMap<>();
        for (DatacenterTableField field : table.getFields()) {
            fieldMap.put(field.getFieldName(), field);
        }
        if (!CollectionUtils.isEmpty(request.getSelectedColumns())) {
            for (String column : request.getSelectedColumns()) {
                DatacenterTableField field = fieldMap.get(column);
                if (field == null || !isEnabled(field.getQueryable())) {
                    throw new BusinessException("字段不可查询: " + column);
                }
            }
        } else {
            request.setSelectedColumns(
                    table.getFields().stream()
                            .filter(field -> isEnabled(field.getQueryable()))
                            .map(DatacenterTableField::getFieldName)
                            .toList()
            );
        }
        if (!CollectionUtils.isEmpty(request.getFilters())) {
            request.getFilters().forEach(filter -> {
                DatacenterTableField field = fieldMap.get(filter.getColumn());
                if (field == null || !isEnabled(field.getQueryable())) {
                    throw new BusinessException("字段不可过滤: " + filter.getColumn());
                }
            });
        }
        if (!CollectionUtils.isEmpty(request.getSorts())) {
            request.getSorts().forEach(sort -> {
                DatacenterTableField field = fieldMap.get(sort.getColumn());
                if (field == null || !isEnabled(field.getSortable())) {
                    throw new BusinessException("字段不可排序: " + sort.getColumn());
                }
            });
        }
        if (request.getWhere() != null && !request.getWhere().isBlank()) {
            boolean allowLegacyWhere = "PROJECT_MYSQL".equals(source.getSourceType())
                    || "MYSQL".equals(source.getSourceType())
                    || "POSTGRESQL".equals(source.getSourceType());
            if (!allowLegacyWhere) {
                throw new BusinessException("当前数据源仅支持结构化 DSL 查询");
            }
        }
    }

    private boolean isEnabled(Integer value) {
        return value == null || value == 1;
    }

    private String trimToNull(String value) {
        if (!StringUtils.hasText(value)) {
            return null;
        }
        return value.trim();
    }

    private List<DatacenterDatasetVersion> listVersions(java.math.BigInteger tableId) {
        QueryWrapper wrapper = QueryWrapper.create();
        wrapper.eq(DatacenterDatasetVersion::getTableId, tableId);
        wrapper.orderBy("version_no desc");
        return datasetVersionMapper.selectListByQuery(wrapper);
    }

    private List<DatacenterDerivedTable> listUpstream(java.math.BigInteger tableId) {
        QueryWrapper wrapper = QueryWrapper.create();
        wrapper.eq(DatacenterDerivedTable::getDerivedTableId, tableId);
        wrapper.orderBy("created desc");
        return derivedTableMapper.selectListByQuery(wrapper);
    }

    private List<DatacenterDerivedTable> listDownstream(java.math.BigInteger tableId) {
        QueryWrapper wrapper = QueryWrapper.create();
        wrapper.eq(DatacenterDerivedTable::getSourceTableId, tableId);
        wrapper.orderBy("created desc");
        return derivedTableMapper.selectListByQuery(wrapper);
    }
}
