package tech.easyflow.admin.controller.ai;

import cn.dev33.satoken.annotation.SaCheckPermission;
import com.mybatisflex.core.BaseMapper;
import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import tech.easyflow.ai.dto.ModelInvokeConfigDtos;
import tech.easyflow.ai.entity.Model;
import tech.easyflow.ai.entity.table.ModelTableDef;
import tech.easyflow.ai.mapper.ModelMapper;
import tech.easyflow.ai.service.AgentResourceReferenceService;
import tech.easyflow.ai.service.ModelService;
import tech.easyflow.admin.model.ai.ModelGatewayConfigView;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.system.entity.SysOption;
import tech.easyflow.system.service.SysOptionService;
import tech.easyflow.ai.service.capability.ModelCapabilityResolution;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.tree.Tree;
import tech.easyflow.common.web.controller.BaseCurdController;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.common.web.jsonbody.JsonBody;

import javax.annotation.Resource;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 控制层。
 *
 * @author michael
 * @since 2024-08-23
 */
@RestController
@RequestMapping("/api/v1/model")
public class ModelController extends BaseCurdController<ModelService, Model> {

    private static final String CHAT_PUBLISH_BASE_URL = "chat_publish_base_url";

    @Autowired
    private SysOptionService sysOptionService;

    public ModelController(ModelService service) {
        super(service);
    }

    @Autowired
    ModelService modelService;

    /**
     * 查询模型统一网关页面所需的安全配置。
     *
     * @return 仅包含模型发布基础地址的配置
     */
    @GetMapping("/gatewayConfig")
    @SaCheckPermission("/api/v1/model/query")
    public Result<ModelGatewayConfigView> gatewayConfig() {
        SysOption option = sysOptionService.getByOptionKey(
                CHAT_PUBLISH_BASE_URL,
                SaTokenUtil.getLoginAccount().getTenantId()
        );
        return Result.ok(new ModelGatewayConfigView(option == null ? null : option.getValue()));
    }

    @Resource
    ModelMapper modelMapper;
    @Resource
    AgentResourceReferenceService agentResourceReferenceService;

    @GetMapping("list")
    @SaCheckPermission("/api/v1/model/query")
    public Result<List<Model>> list(Model entity, Boolean asTree, String sortKey, String sortType) {
        return Result.ok(service.listSelectableModels(entity, asTree, sortKey, sortType));
    }

    @GetMapping("getList")
    @SaCheckPermission("/api/v1/model/query")
    public Result<Map<String, Map<String, List<Model>>>> getList(Model entity) {
        return Result.ok(modelService.getList(entity));
    }

    @GetMapping("invokeList")
    @SaCheckPermission("/api/v1/model/query")
    public Result<List<Model>> invokeList() {
        return Result.ok(modelService.listInvokeModels());
    }

    @PostMapping("/addAiLlm")
    @SaCheckPermission("/api/v1/model/save")
    public Result<Boolean> addAiLlm(Model entity) {
        LoginAccount account = SaTokenUtil.getLoginAccount();
        commonFiled(entity, account.getId(), account.getTenantId(), account.getDeptId());
        return Result.ok(modelService.addAiLlm(entity));
    }


    @GetMapping("verifyLlmConfig")
    @SaCheckPermission("/api/v1/model/save")
    public Result<?> verifyLlmConfig(@RequestParam BigInteger id) {
        Model model = service.getModelInstance(id);
        return Result.ok(modelService.verifyModelConfig(model));
    }

    /**
     * 根据模型 ID 返回自动识别的类型和能力。
     *
     * @param providerId 供应商 ID
     * @param modelName 模型 ID
     * @return 模型能力识别结果
     */
    @GetMapping("capabilities")
    @SaCheckPermission("/api/v1/model/query")
    public Result<ModelCapabilityResolution> resolveCapabilities(
            @RequestParam(required = false) BigInteger providerId,
            @RequestParam String modelName) {
        return Result.ok(modelService.resolveModelCapabilities(providerId, modelName));
    }

    @PostMapping("/removeByEntity")
    @SaCheckPermission("/api/v1/model/remove")
    @Transactional(rollbackFor = Exception.class)
    public Result<?> removeByEntity(@RequestBody Model entity) {
        LoginAccount account = requireAccount();
        List<Model> models = service.list(QueryWrapper.create()
                .select(Model::getId)
                .eq(Model::getProviderId, entity.getProviderId())
                .eq(Model::getGroupName, entity.getGroupName())
                .eq(Model::getTenantId, account.getTenantId())
                .orderBy(Model::getId, true)
                .forUpdate());
        if (models.isEmpty()) {
            throw new BusinessException("模型不存在或无权删除");
        }
        agentResourceReferenceService.assertModelsUnused(
                models.stream().map(Model::getId).toList());
        entity.setTenantId(account.getTenantId());
        modelService.removeByEntity(entity);
        return Result.ok();
    }

    @PostMapping("/updateInvokeConfig")
    @SaCheckPermission("/api/v1/model/save")
    public Result<Model> updateInvokeConfig(@RequestBody ModelInvokeConfigDtos.UpdateRequest request) {
        return Result.ok(modelService.updateInvokeConfig(
                request.getId(),
                request.getInvokeCode(),
                request.getPublishEnabled()
        ));
    }

    @PostMapping("/batchUpdateInvokePublishStatus")
    @SaCheckPermission("/api/v1/model/save")
    public Result<List<Model>> batchUpdateInvokePublishStatus(@RequestBody ModelInvokeConfigDtos.BatchPublishRequest request) {
        return Result.ok(modelService.batchUpdateInvokePublishStatus(
                request.getIds(),
                request.getPublishEnabled()
        ));
    }

    @Override
    protected Result<?> onSaveOrUpdateBefore(Model entity, boolean isSave) {
        modelService.validateForSaveOrUpdate(entity, isSave);
        return super.onSaveOrUpdateBefore(entity, isSave);
    }

    @GetMapping("/selectLlmByProviderCategory")
    @SaCheckPermission("/api/v1/model/query")
    public Result<Map<String, List<Model>>> selectLlmByProviderCategory(Model entity, String sortKey, String sortType) {
        QueryWrapper queryWrapper = QueryWrapper.create(entity, buildOperators(entity));
        queryWrapper.orderBy(buildOrderBy(sortKey, sortType, getDefaultOrderBy()));
        BaseMapper<Model> mapper = service.getMapper();
        List<Model> totalList = mapper.selectListWithRelationsByQuery(queryWrapper);
        Map<String, List<Model>> groupList = totalList.stream().collect(Collectors.groupingBy(Model::getGroupName));
        return Result.ok(groupList);
    }

    @GetMapping("/selectLlmByProviderAndModelType")
    @SaCheckPermission("/api/v1/model/query")
    public Result<Map<String, List<Model>>> selectLlmByProviderAndModelType(
            @RequestParam String modelType,
            @RequestParam BigInteger providerId,
            @RequestParam(required = false) String selectText
    ) {
        QueryWrapper queryWrapper = QueryWrapper.create();
        queryWrapper.eq(Model::getProviderId, providerId);
        queryWrapper.eq(Model::getModelType, modelType);
        String keyword = normalizeSearchKeyword(selectText);
        if (StringUtils.hasText(keyword)) {
            queryWrapper.and(buildLiteralContainsCondition(
                    keyword, ModelTableDef.MODEL.TITLE, ModelTableDef.MODEL.MODEL_NAME));
        }
        List<Model> totalList = service.getMapper().selectListWithRelationsByQuery(queryWrapper);
        Map<String, List<Model>> groupList = totalList.stream().collect(Collectors.groupingBy(Model::getGroupName));
        return Result.ok(groupList);
    }


    @GetMapping("/selectLlmList")
    @SaCheckPermission("/api/v1/model/query")
    public Result<List<Model>> selectLlmList(Model entity, Boolean asTree, String sortKey, String sortType) {
        QueryWrapper queryWrapper = QueryWrapper.create(entity, buildOperators(entity));
        queryWrapper.orderBy(buildOrderBy(sortKey, sortType, getDefaultOrderBy()));
        List<Model> totalList = Tree.tryToTree(modelMapper.selectListWithRelationsByQuery(queryWrapper), asTree);
        totalList.forEach(aiLlm -> {
            aiLlm.setTitle(aiLlm.getModelProvider().getProviderName() + "/" + aiLlm.getTitle());
        });
        return Result.ok(totalList);
    }

    @PostMapping("removeLlmByIds")
    @SaCheckPermission("/api/v1/model/remove")
    @Transactional(rollbackFor = Exception.class)
    public Result<?> removeLlm(@JsonBody(value = "id", required = true) Serializable id) {
        LoginAccount account = requireAccount();
        List<Serializable> ids = Collections.singletonList(id);
        QueryWrapper queryWrapper = QueryWrapper.create()
                .in(Model::getId, ids)
                .eq(Model::getTenantId, account.getTenantId())
                .orderBy(Model::getId, true)
                .forUpdate();
        List<Model> models = service.list(queryWrapper);
        if (models.isEmpty()) {
            throw new BusinessException("模型不存在或无权删除");
        }
        agentResourceReferenceService.assertModelsUnused(
                models.stream().map(Model::getId).toList());
        service.remove(QueryWrapper.create()
                .in(Model::getId, ids)
                .eq(Model::getTenantId, account.getTenantId()));
        return Result.ok();
    }

    /**
     * 获取带租户信息的当前登录账号。
     *
     * @return 当前登录账号
     * @throws BusinessException 登录状态无效时抛出
     */
    private LoginAccount requireAccount() {
        LoginAccount account = SaTokenUtil.getLoginAccount();
        if (account == null || account.getTenantId() == null) {
            throw new BusinessException("当前登录状态失效，请重新登录后再试");
        }
        return account;
    }
}
