package io.cameleer.server.app.controller;

import io.cameleer.server.app.license.LicenseRepository;
import io.cameleer.server.app.license.LicenseService;
import io.cameleer.server.core.license.LicenseGate;
import io.cameleer.license.LicenseInfo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * License management for ADMIN users. All mutation goes through {@link LicenseService} so that
 * install / replace flows are uniformly audited, persisted, and published to listeners (retention
 * policy, license metrics, etc.).
 *
 * <p>GET returns {@code {state, invalidReason, envelope, lastValidatedAt?}}. The raw JWT-style
 * token is deliberately omitted from the response — only the parsed {@link LicenseInfo} is
 * exposed.</p>
 */
@RestController
@RequestMapping("/api/v1/admin/license")
@PreAuthorize("hasRole('ADMIN')")
@Tag(name = "License Admin", description = "License management")
public class LicenseAdminController {

    private final LicenseService licenseService;
    private final LicenseGate gate;
    private final LicenseRepository repo;

    public LicenseAdminController(LicenseService svc, LicenseGate gate, LicenseRepository repo) {
        this.licenseService = svc;
        this.gate = gate;
        this.repo = repo;
    }

    @GetMapping
    @Operation(summary = "Get current license state, invalid reason, and parsed envelope")
    public ResponseEntity<Map<String, Object>> getCurrent() {
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("state", gate.getState().name());
        body.put("invalidReason", gate.getInvalidReason());
        body.put("envelope", gate.getCurrent()); // null when ABSENT/INVALID; raw token deliberately omitted
        repo.findByTenantId(licenseService.getTenantId()).ifPresent(rec ->
                body.put("lastValidatedAt", rec.lastValidatedAt().toString()));
        return ResponseEntity.ok(body);
    }

    public record UpdateLicenseRequest(String token) {}

    @PostMapping
    @Operation(summary = "Install or replace the license token at runtime")
    public ResponseEntity<?> update(@RequestBody UpdateLicenseRequest request, Authentication auth) {
        String userId = auth == null ? "system" : auth.getName().replaceFirst("^user:", "");
        try {
            LicenseInfo info = licenseService.install(request.token(), userId, "api");
            return ResponseEntity.ok(Map.of(
                    "state", gate.getState().name(),
                    "envelope", info));
        } catch (Exception e) {
            return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
        }
    }
}
