package com.cameleer.server.app.controller;

import com.cameleer.server.core.runtime.Environment;
import com.cameleer.server.core.runtime.EnvironmentColor;
import com.cameleer.server.core.runtime.EnvironmentService;
import com.cameleer.server.core.runtime.RuntimeType;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api/v1/admin/environments")
@Tag(name = "Environment Admin", description = "Environment management (ADMIN only)")
@PreAuthorize("hasRole('ADMIN')")
public class EnvironmentAdminController {

    private final EnvironmentService environmentService;

    public EnvironmentAdminController(EnvironmentService environmentService) {
        this.environmentService = environmentService;
    }

    @GetMapping
    @Operation(summary = "List all environments")
    @PreAuthorize("isAuthenticated()")
    public ResponseEntity<List<Environment>> listEnvironments() {
        return ResponseEntity.ok(environmentService.listAll());
    }

    @GetMapping("/{envSlug}")
    @Operation(summary = "Get environment by slug")
    @ApiResponse(responseCode = "200", description = "Environment found")
    @ApiResponse(responseCode = "404", description = "Environment not found")
    public ResponseEntity<Environment> getEnvironment(@PathVariable String envSlug) {
        try {
            return ResponseEntity.ok(environmentService.getBySlug(envSlug));
        } catch (IllegalArgumentException e) {
            return ResponseEntity.notFound().build();
        }
    }

    @PostMapping
    @Operation(summary = "Create a new environment")
    @ApiResponse(responseCode = "201", description = "Environment created")
    @ApiResponse(responseCode = "400", description = "Invalid slug or slug already exists")
    public ResponseEntity<?> createEnvironment(@RequestBody CreateEnvironmentRequest request) {
        try {
            environmentService.create(request.slug(), request.displayName(), request.production());
            return ResponseEntity.status(201).body(environmentService.getBySlug(request.slug()));
        } catch (IllegalArgumentException e) {
            return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
        }
    }

    @PutMapping("/{envSlug}")
    @Operation(summary = "Update an environment's mutable fields (displayName, production, enabled, color)",
            description = "Slug is immutable after creation and cannot be changed. "
                    + "Any slug field in the request body is ignored. "
                    + "If color is null or absent, the existing color is preserved.")
    @ApiResponse(responseCode = "200", description = "Environment updated")
    @ApiResponse(responseCode = "400", description = "Unknown color value")
    @ApiResponse(responseCode = "404", description = "Environment not found")
    public ResponseEntity<?> updateEnvironment(@PathVariable String envSlug,
                                                @RequestBody UpdateEnvironmentRequest request) {
        try {
            Environment current = environmentService.getBySlug(envSlug);
            String nextColor = request.color() == null ? current.color() : request.color();
            if (!EnvironmentColor.isValid(nextColor)) {
                return ResponseEntity.badRequest().body(Map.of("error", "unknown environment color: " + request.color()));
            }
            environmentService.update(current.id(), request.displayName(), request.production(), request.enabled(), nextColor);
            return ResponseEntity.ok(environmentService.getBySlug(envSlug));
        } catch (IllegalArgumentException e) {
            if (e.getMessage().contains("not found")) {
                return ResponseEntity.notFound().build();
            }
            return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
        }
    }

    @DeleteMapping("/{envSlug}")
    @Operation(summary = "Delete an environment")
    @ApiResponse(responseCode = "204", description = "Environment deleted")
    @ApiResponse(responseCode = "400", description = "Cannot delete default environment")
    @ApiResponse(responseCode = "404", description = "Environment not found")
    public ResponseEntity<?> deleteEnvironment(@PathVariable String envSlug) {
        try {
            Environment current = environmentService.getBySlug(envSlug);
            environmentService.delete(current.id());
            return ResponseEntity.noContent().build();
        } catch (IllegalArgumentException e) {
            if (e.getMessage().contains("not found")) {
                return ResponseEntity.notFound().build();
            }
            return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
        }
    }

    private static final java.util.regex.Pattern CUSTOM_ARGS_PATTERN =
            java.util.regex.Pattern.compile("^[-a-zA-Z0-9_.=:/\\s+\"']*$");

    private void validateContainerConfig(Map<String, Object> config) {
        Object customArgs = config.get("customArgs");
        if (customArgs instanceof String s && !s.isBlank() && !CUSTOM_ARGS_PATTERN.matcher(s).matches()) {
            throw new IllegalArgumentException("customArgs contains invalid characters. Only JVM-style arguments are allowed.");
        }
        Object runtimeType = config.get("runtimeType");
        if (runtimeType instanceof String s && !s.isBlank() && RuntimeType.fromString(s) == null) {
            throw new IllegalArgumentException("Invalid runtimeType: " + s +
                    ". Must be one of: auto, spring-boot, quarkus, plain-java, native");
        }
    }

    @PutMapping("/{envSlug}/default-container-config")
    @Operation(summary = "Update default container config for an environment")
    @ApiResponse(responseCode = "200", description = "Default container config updated")
    @ApiResponse(responseCode = "400", description = "Invalid configuration")
    @ApiResponse(responseCode = "404", description = "Environment not found")
    public ResponseEntity<?> updateDefaultContainerConfig(@PathVariable String envSlug,
                                                           @RequestBody Map<String, Object> defaultContainerConfig) {
        try {
            validateContainerConfig(defaultContainerConfig);
            Environment current = environmentService.getBySlug(envSlug);
            environmentService.updateDefaultContainerConfig(current.id(), defaultContainerConfig);
            return ResponseEntity.ok(environmentService.getBySlug(envSlug));
        } catch (IllegalArgumentException e) {
            if (e.getMessage().contains("not found")) {
                return ResponseEntity.notFound().build();
            }
            return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
        }
    }

    @PutMapping("/{envSlug}/jar-retention")
    @Operation(summary = "Update JAR retention policy for an environment")
    @ApiResponse(responseCode = "200", description = "Retention policy updated")
    @ApiResponse(responseCode = "404", description = "Environment not found")
    public ResponseEntity<?> updateJarRetention(@PathVariable String envSlug,
                                                 @RequestBody JarRetentionRequest request) {
        try {
            Environment current = environmentService.getBySlug(envSlug);
            environmentService.updateJarRetentionCount(current.id(), request.jarRetentionCount());
            return ResponseEntity.ok(environmentService.getBySlug(envSlug));
        } catch (IllegalArgumentException e) {
            if (e.getMessage().contains("not found")) {
                return ResponseEntity.notFound().build();
            }
            return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
        }
    }

    public record CreateEnvironmentRequest(String slug, String displayName, boolean production) {}
    public record UpdateEnvironmentRequest(String displayName, boolean production, boolean enabled, String color) {}
    public record JarRetentionRequest(Integer jarRetentionCount) {}
}
