package com.cloudhandson.vpdbackoffice.web;

import com.cloudhandson.vpdbackoffice.domain.probe.ProbeCommand;
import com.cloudhandson.vpdbackoffice.domain.protectedobject.ProtectedObject;
import com.cloudhandson.vpdbackoffice.domain.vector.VectorQueryEmbedding;
import com.cloudhandson.vpdbackoffice.service.AppException;
import com.cloudhandson.vpdbackoffice.service.BearerTokenService;
import com.cloudhandson.vpdbackoffice.service.OrdsProbeService;
import com.cloudhandson.vpdbackoffice.service.ProtectedObjectService;
import com.cloudhandson.vpdbackoffice.service.VectorKnowledgeService;
import com.cloudhandson.vpdbackoffice.service.VpdPolicyService;
import com.cloudhandson.vpdbackoffice.mapper.UserMapper;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class ProbeController {

  private final OrdsProbeService probeService;
  private final ProtectedObjectService protectedObjectService;
  private final BearerTokenService tokenService;
  private final VpdPolicyService vpdPolicyService;
  private final UserMapper userMapper;
  private final VectorKnowledgeService vectorKnowledgeService;

  public ProbeController(
      OrdsProbeService probeService,
      ProtectedObjectService protectedObjectService,
      BearerTokenService tokenService,
      VpdPolicyService vpdPolicyService,
      UserMapper userMapper,
      VectorKnowledgeService vectorKnowledgeService
  ) {
    this.probeService = probeService;
    this.protectedObjectService = protectedObjectService;
    this.tokenService = tokenService;
    this.vpdPolicyService = vpdPolicyService;
    this.userMapper = userMapper;
    this.vectorKnowledgeService = vectorKnowledgeService;
  }

  @GetMapping("/probe")
  public String probe(Model model) {
    Set<String> defaultObjectKeys = defaultPermissionObjectKeys();
    List<ProtectedObject> objects =
        protectedObjectService.findEnabled().stream()
            .sorted(Comparator
                .comparing((ProtectedObject object) ->
                    !defaultObjectKeys.contains(object.displayName()))
                .thenComparing(ProtectedObject::displayName))
            .toList();
    model.addAttribute("objects", objects);
    model.addAttribute("defaultObjectKeys", defaultObjectKeys);
    model.addAttribute("users", userMapper.findAll());
    model.addAttribute("aiEmbeddingConfigured", vectorKnowledgeService.aiEmbeddingConfigured());
    return "probe";
  }

  @PostMapping("/probe")
  public String run(
      @RequestParam long objectId,
      @RequestParam(defaultValue = "") String bearerToken,
      @RequestParam(required = false) Long tempUserId,
      @RequestParam(defaultValue = "50") int limit,
      @RequestParam(required = false) String requestBody,
      @RequestParam(defaultValue = "DEMO") String embeddingMode,
      Model model
  ) {
    String normalizedToken = bearerToken == null ? "" : bearerToken.trim();
    ProtectedObject selectedObject = protectedObjectService.findEnabled().stream()
        .filter(object -> object.objectId() == objectId)
        .findFirst()
        .orElse(null);
    boolean vectorSearch = selectedObject != null
        && VectorKnowledgeService.VECTOR_OBJECT.equalsIgnoreCase(selectedObject.objectName());
    model.addAttribute("vectorSearch", vectorSearch);
    Long temporaryKeyId = null;
    if (tempUserId != null) {
      var issued = tokenService.issueTemporaryToken(tempUserId, "ORDS 검증 임시 실행");
      normalizedToken = issued.plainToken();
      temporaryKeyId = issued.keyId();
    }
    try {
      if (vectorSearch) {
        VectorQueryEmbedding vectorQuery = vectorKnowledgeService.vectorizeQuery(requestBody, embeddingMode);
        requestBody = vectorQuery.requestBody();
        model.addAttribute("vectorQuery", vectorQuery.query());
        model.addAttribute("vectorEmbeddingMode", vectorQuery.embeddingMode());
        model.addAttribute("vectorEmbeddingModel", vectorQuery.embeddingModel());
      }
      model.addAttribute("result", probeService.runProbe(
          new ProbeCommand(temporaryKeyId, objectId, normalizedToken, limit, requestBody)));
      model.addAttribute("tokenContext", tokenService.findTokenContextByPlainToken(normalizedToken));
    } catch (AppException exception) {
      model.addAttribute("errorMessage", exception.getMessage());
    } finally {
      if (temporaryKeyId != null) {
        tokenService.revokeToken(temporaryKeyId, "temporary probe completed");
      }
    }
    model.addAttribute("selectedObject", selectedObject);
    return "fragments/probe-result :: result";
  }

  private Set<String> defaultPermissionObjectKeys() {
    Set<String> result = new LinkedHashSet<>();
    vpdPolicyService.findPolicies().stream()
        .filter(policy -> policy.permissionSystemDefault() && "YES".equalsIgnoreCase(policy.enabled()))
        .map(policy -> policy.objectDisplayName().toUpperCase())
        .forEach(result::add);
    return result;
  }
}
