package com.cameleer.server.app.controller;

import com.cameleer.common.model.ApplicationConfig;
import com.cameleer.server.app.dto.AppConfigResponse;
import com.cameleer.server.app.security.JwtAuthenticationFilter;
import com.cameleer.server.app.storage.PostgresApplicationConfigRepository;
import com.cameleer.server.core.admin.SensitiveKeysConfig;
import com.cameleer.server.core.admin.SensitiveKeysMerger;
import com.cameleer.server.core.admin.SensitiveKeysRepository;
import com.cameleer.server.core.agent.AgentInfo;
import com.cameleer.server.core.agent.AgentRegistryService;
import com.cameleer.server.core.security.JwtService.JwtValidationResult;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * Agent-authoritative application config read. Env and application are derived
 * from the agent's JWT + registry entry — not from the URL or query params, so
 * agents cannot spoof env.
 */
@RestController
@RequestMapping("/api/v1/agents")
@Tag(name = "Agent Config", description = "Agent-authoritative config read (AGENT only)")
public class AgentConfigController {

    private final PostgresApplicationConfigRepository configRepository;
    private final AgentRegistryService registryService;
    private final SensitiveKeysRepository sensitiveKeysRepository;
    private final ObjectMapper objectMapper;

    public AgentConfigController(PostgresApplicationConfigRepository configRepository,
                                 AgentRegistryService registryService,
                                 SensitiveKeysRepository sensitiveKeysRepository,
                                 ObjectMapper objectMapper) {
        this.configRepository = configRepository;
        this.registryService = registryService;
        this.sensitiveKeysRepository = sensitiveKeysRepository;
        this.objectMapper = objectMapper;
    }

    @GetMapping("/config")
    @Operation(summary = "Get application config for the calling agent",
            description = "Resolves (application, environment) from the agent's JWT + registry. "
                    + "Prefers the registry entry (heartbeat-authoritative); falls back to the JWT env claim. "
                    + "Returns 404 if neither identifies a valid agent.")
    @ApiResponse(responseCode = "200", description = "Config returned")
    @ApiResponse(responseCode = "404", description = "Calling agent could not be resolved")
    public ResponseEntity<AppConfigResponse> getConfigForAgent(Authentication auth,
                                                                HttpServletRequest request) {
        String instanceId = auth != null ? auth.getName() : null;
        if (instanceId == null || instanceId.isBlank()) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }

        AgentInfo agent = registryService.findById(instanceId);
        String application;
        String environment;
        if (agent != null) {
            application = agent.applicationId();
            environment = agent.environmentId();
        } else {
            // Registry miss — fall back to JWT env claim; application can't be
            // derived from JWT alone, so without a registry entry we 404.
            environment = environmentFromJwt(request);
            application = null;
        }

        if (application == null || application.isBlank() || environment == null || environment.isBlank()) {
            return ResponseEntity.notFound().build();
        }

        ApplicationConfig config = configRepository.findByApplicationAndEnvironment(application, environment)
                .orElse(ApplicationConfigController.defaultConfig(application, environment));

        List<String> globalKeys = sensitiveKeysRepository.find()
                .map(SensitiveKeysConfig::keys)
                .orElse(null);
        List<String> merged = SensitiveKeysMerger.merge(globalKeys, extractSensitiveKeys(config));

        return ResponseEntity.ok(new AppConfigResponse(config, globalKeys, merged));
    }

    private static String environmentFromJwt(HttpServletRequest request) {
        Object attr = request.getAttribute(JwtAuthenticationFilter.JWT_RESULT_ATTR);
        if (attr instanceof JwtValidationResult result) {
            return result.environment();
        }
        return null;
    }

    private List<String> extractSensitiveKeys(ApplicationConfig config) {
        try {
            JsonNode node = objectMapper.valueToTree(config);
            JsonNode keysNode = node.get("sensitiveKeys");
            if (keysNode == null || keysNode.isNull() || !keysNode.isArray()) {
                return null;
            }
            return objectMapper.convertValue(keysNode, new com.fasterxml.jackson.core.type.TypeReference<List<String>>() {});
        } catch (Exception e) {
            return null;
        }
    }
}
