package com.cfdeployer.controller;

import com.cfdeployer.model.CfDeployRequest;
import com.cfdeployer.model.CfDeployResponse;
import com.cfdeployer.service.CfCliService;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequestMapping("/api/cf")
@RequiredArgsConstructor
public class CfDeployController {

    private static final Logger log = LoggerFactory.getLogger(CfDeployController.class);

    private final CfCliService cfCliService;
    private final ObjectMapper objectMapper;

    @PostMapping(value = "/deploy", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseEntity<CfDeployResponse> deploy(
            @RequestPart("request") String requestJson,
            @RequestPart("jarFile") MultipartFile jarFile,
            @RequestPart("manifest") MultipartFile manifest) {

        try {
            log.info("Received deployment request");

            CfDeployRequest request = objectMapper.readValue(requestJson, CfDeployRequest.class);
            log.info("Deploying application: {} to org: {} space: {}",
                    request.getAppName(), request.getOrganization(), request.getSpace());

            validateFiles(jarFile, manifest);

            CfDeployResponse response = cfCliService.deployApplication(request, jarFile, manifest);

            if (Boolean.TRUE.equals(response.getSuccess())) {
                return ResponseEntity.ok(response);
            } else {
                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
            }

        } catch (Exception e) {
            log.error("Error processing deployment request", e);
            CfDeployResponse errorResponse = CfDeployResponse.failure(
                    "Failed to process deployment request: " + e.getMessage(),
                    e.toString()
            );
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
        }
    }

    @PostMapping("/apps")
    public ResponseEntity<String> listApps(@RequestBody String requestJson) {
        try {
            log.info("Received request to list apps");
            CfDeployRequest request = objectMapper.readValue(requestJson, CfDeployRequest.class);
            String output = cfCliService.listApps(request);
            return ResponseEntity.ok(output);
        } catch (Exception e) {
            log.error("Error listing apps", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("Failed to list apps: " + e.getMessage());
        }
    }

    @PostMapping("/routes")
    public ResponseEntity<String> listRoutes(@RequestBody String requestJson) {
        try {
            log.info("Received request to list routes");
            CfDeployRequest request = objectMapper.readValue(requestJson, CfDeployRequest.class);
            String output = cfCliService.listRoutes(request);
            return ResponseEntity.ok(output);
        } catch (Exception e) {
            log.error("Error listing routes", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("Failed to list routes: " + e.getMessage());
        }
    }

    @PostMapping("/app/{appName}")
    public ResponseEntity<String> getAppDetails(
            @PathVariable String appName,
            @RequestBody String requestJson) {
        try {
            log.info("Received request to get details for app: {}", appName);
            CfDeployRequest request = objectMapper.readValue(requestJson, CfDeployRequest.class);
            String output = cfCliService.getAppDetails(request, appName);
            return ResponseEntity.ok(output);
        } catch (Exception e) {
            log.error("Error getting app details for: {}", appName, e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("Failed to get app details: " + e.getMessage());
        }
    }

    @PostMapping("/logs/{appName}")
    public ResponseEntity<String> getAppLogs(
            @PathVariable String appName,
            @RequestParam(defaultValue = "true") boolean recent,
            @RequestBody String requestJson) {
        try {
            log.info("Received request to get {} logs for app: {}", recent ? "recent" : "tail", appName);
            CfDeployRequest request = objectMapper.readValue(requestJson, CfDeployRequest.class);
            String output = cfCliService.getAppLogs(request, appName, recent);
            return ResponseEntity.ok(output);
        } catch (Exception e) {
            log.error("Error getting logs for: {}", appName, e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("Failed to get app logs: " + e.getMessage());
        }
    }

    private void validateFiles(MultipartFile jarFile, MultipartFile manifest) {
        if (jarFile.isEmpty()) {
            throw new IllegalArgumentException("JAR file is empty");
        }

        if (manifest.isEmpty()) {
            throw new IllegalArgumentException("Manifest file is empty");
        }

        String jarFileName = jarFile.getOriginalFilename();
        if (jarFileName == null || !jarFileName.toLowerCase().endsWith(".jar")) {
            throw new IllegalArgumentException("Invalid JAR file. File must have .jar extension");
        }

        String manifestFileName = manifest.getOriginalFilename();
        if (manifestFileName == null ||
            (!manifestFileName.toLowerCase().endsWith(".yml") && !manifestFileName.toLowerCase().endsWith(".yaml"))) {
            throw new IllegalArgumentException("Invalid manifest file. File must have .yml or .yaml extension");
        }

        log.debug("File validation successful - JAR: {}, Manifest: {}", jarFileName, manifestFileName);
    }
}
