package com.ruoyi.web.controller.front;

import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.generator.domain.front.FrontProject;
import com.ruoyi.generator.domain.front.dto.DatabaseDesignResponse;
import com.ruoyi.generator.domain.front.dto.AiGenerationTaskCreateRequest;
import com.ruoyi.generator.domain.front.dto.FrontProjectCreateRequest;
import com.ruoyi.generator.domain.front.dto.FrontProjectUpdateRequest;
import com.ruoyi.generator.domain.front.dto.GenerateAppBlueprintRequest;
import com.ruoyi.generator.domain.front.dto.GenerateBusinessBlueprintRequest;
import com.ruoyi.generator.domain.front.dto.GenerateDatabaseRequest;
import com.ruoyi.generator.domain.front.dto.FrontendPageDesignRequest;
import com.ruoyi.generator.domain.front.dto.PreviewFileRequest;
import com.ruoyi.generator.service.front.IAiGenerateService;
import com.ruoyi.generator.service.front.IAiGenerationTaskService;
import com.ruoyi.generator.service.front.BusinessBlockRegistryService;
import com.ruoyi.generator.service.front.FrontendPageDesignService;
import com.ruoyi.generator.service.front.IFrontProjectPreviewService;
import com.ruoyi.generator.service.front.IFrontProjectRunPreviewService;
import com.ruoyi.generator.service.front.IFrontProjectService;

@RestController
@RequestMapping("/front/project")
public class FrontProjectController extends BaseController
{
    @Autowired
    private IFrontProjectService frontProjectService;
    @Autowired
    private IAiGenerateService aiGenerateService;
    @Autowired
    private IAiGenerationTaskService aiGenerationTaskService;
    @Autowired
    private FrontendPageDesignService frontendPageDesignService;
    @Autowired
    private BusinessBlockRegistryService businessBlockRegistryService;
    @Autowired
    private IFrontProjectPreviewService frontProjectPreviewService;
    @Autowired
    private IFrontProjectRunPreviewService frontProjectRunPreviewService;

    @PostMapping("/create")
    public AjaxResult create(@RequestBody FrontProjectCreateRequest request)
    {
        Long userId = SecurityUtils.getUserId();
        return AjaxResult.success(frontProjectService.createProject(userId, request));
    }

    @GetMapping("/list")
    public AjaxResult list()
    {
        Long userId = SecurityUtils.getUserId();
        return AjaxResult.success(frontProjectService.listProjects(userId));
    }

    @GetMapping("/options/modules")
    public AjaxResult modules()
    {
        return AjaxResult.success(frontProjectService.listAvailableModules());
    }

    @GetMapping("/{projectId}")
    public AjaxResult get(@PathVariable Long projectId)
    {
        Long userId = SecurityUtils.getUserId();
        return AjaxResult.success(frontProjectService.getProject(userId, projectId));
    }

    @PutMapping("/{projectId}")
    public AjaxResult update(@PathVariable Long projectId, @RequestBody FrontProjectUpdateRequest request)
    {
        Long userId = SecurityUtils.getUserId();
        return toAjax(frontProjectService.updateProject(userId, projectId, request));
    }

    @DeleteMapping("/{projectId}")
    public AjaxResult delete(@PathVariable Long projectId)
    {
        Long userId = SecurityUtils.getUserId();
        return toAjax(frontProjectService.deleteProject(userId, projectId));
    }

    @GetMapping("/{projectId}/database")
    public AjaxResult database(@PathVariable Long projectId)
    {
        Long userId = SecurityUtils.getUserId();
        return AjaxResult.success(frontProjectService.getDatabase(userId, projectId));
    }

    @PutMapping("/{projectId}/database")
    public AjaxResult saveDatabase(@PathVariable Long projectId, @RequestBody DatabaseDesignResponse request)
    {
        Long userId = SecurityUtils.getUserId();
        return AjaxResult.success(frontProjectService.saveDatabase(userId, projectId, request));
    }

    @GetMapping("/{projectId}/page-designs")
    public AjaxResult pageDesigns(@PathVariable Long projectId,
            @RequestParam(defaultValue = "frontend") String scope)
    {
        Long userId = SecurityUtils.getUserId();
        return AjaxResult.success(frontendPageDesignService.listPageDesigns(userId, projectId, scope));
    }

    @GetMapping("/{projectId}/business-blocks")
    public AjaxResult businessBlocks(@PathVariable Long projectId,
            @RequestParam(defaultValue = "frontend") String scope)
    {
        Long userId = SecurityUtils.getUserId();
        frontProjectService.getProject(userId, projectId);
        return AjaxResult.success(businessBlockRegistryService.listDefinitions(scope));
    }

    @PostMapping("/{projectId}/page-designs/init")
    public AjaxResult initPageDesigns(@PathVariable Long projectId,
            @RequestParam(defaultValue = "frontend") String scope)
    {
        Long userId = SecurityUtils.getUserId();
        return AjaxResult.success(frontendPageDesignService.initializePageDesigns(userId, projectId, scope));
    }

    @PostMapping("/{projectId}/page-designs")
    public AjaxResult createPageDesign(@PathVariable Long projectId,
            @RequestParam(defaultValue = "frontend") String scope,
            @RequestBody FrontendPageDesignRequest request)
    {
        Long userId = SecurityUtils.getUserId();
        return AjaxResult.success(frontendPageDesignService.createPageDesign(userId, projectId, request, scope));
    }

    @GetMapping("/{projectId}/page-designs/{designId}")
    public AjaxResult pageDesign(@PathVariable Long projectId, @PathVariable Long designId,
            @RequestParam(defaultValue = "frontend") String scope)
    {
        Long userId = SecurityUtils.getUserId();
        return AjaxResult.success(frontendPageDesignService.getPageDesign(userId, projectId, designId, scope));
    }

    @PutMapping("/{projectId}/page-designs/{designId}")
    public AjaxResult savePageDesign(@PathVariable Long projectId, @PathVariable Long designId,
            @RequestParam(defaultValue = "frontend") String scope,
            @RequestBody FrontendPageDesignRequest request)
    {
        Long userId = SecurityUtils.getUserId();
        return AjaxResult.success(frontendPageDesignService.updatePageDesign(userId, projectId, designId, request, scope));
    }

    @DeleteMapping("/{projectId}/page-designs/{designId}")
    public AjaxResult deletePageDesign(@PathVariable Long projectId, @PathVariable Long designId,
            @RequestParam(defaultValue = "frontend") String scope)
    {
        Long userId = SecurityUtils.getUserId();
        frontendPageDesignService.deletePageDesign(userId, projectId, designId, scope);
        return AjaxResult.success();
    }

    @PostMapping("/{projectId}/generate-database")
    public AjaxResult generateDatabase(@PathVariable Long projectId, @RequestBody GenerateDatabaseRequest request)
    {
        Long userId = SecurityUtils.getUserId();
        return success(aiGenerateService.generateDatabase(userId, projectId, request));
    }

    @PostMapping("/{projectId}/generate-app-blueprint")
    public AjaxResult generateAppBlueprint(@PathVariable Long projectId, @RequestBody GenerateAppBlueprintRequest request)
    {
        Long userId = SecurityUtils.getUserId();
        return success(aiGenerateService.generateAppBlueprint(userId, projectId, request));
    }

    @PostMapping("/{projectId}/generate-business-blueprint")
    public AjaxResult generateBusinessBlueprint(@PathVariable Long projectId, @RequestBody GenerateBusinessBlueprintRequest request)
    {
        Long userId = SecurityUtils.getUserId();
        return success(aiGenerateService.generateBusinessBlueprint(userId, projectId, request));
    }

    @PostMapping("/{projectId}/ai-tasks")
    public AjaxResult createAiTask(@PathVariable Long projectId, @RequestBody AiGenerationTaskCreateRequest request)
    {
        Long userId = SecurityUtils.getUserId();
        return success(aiGenerationTaskService.createTask(userId, projectId, request));
    }

    @GetMapping("/{projectId}/ai-tasks/{taskId}")
    public AjaxResult getAiTask(@PathVariable Long projectId, @PathVariable Long taskId)
    {
        Long userId = SecurityUtils.getUserId();
        return success(aiGenerationTaskService.getTask(userId, projectId, taskId));
    }

    @PostMapping("/{projectId}/ai-tasks/{taskId}/retry")
    public AjaxResult retryAiTask(@PathVariable Long projectId, @PathVariable Long taskId)
    {
        Long userId = SecurityUtils.getUserId();
        return success(aiGenerationTaskService.retryTask(userId, projectId, taskId));
    }

    @PostMapping("/{projectId}/ai-tasks/{taskId}/cancel")
    public AjaxResult cancelAiTask(@PathVariable Long projectId, @PathVariable Long taskId)
    {
        Long userId = SecurityUtils.getUserId();
        return success(aiGenerationTaskService.cancelTask(userId, projectId, taskId));
    }

    @GetMapping("/{projectId}/generations")
    public AjaxResult generations(@PathVariable Long projectId)
    {
        Long userId = SecurityUtils.getUserId();
        return success(aiGenerationTaskService.listGenerations(userId, projectId));
    }

    @GetMapping("/ai-quota")
    public AjaxResult aiQuota()
    {
        Long userId = SecurityUtils.getUserId();
        return success(aiGenerationTaskService.getQuota(userId));
    }

    @PostMapping("/{projectId}/preview")
    public AjaxResult preview(@PathVariable Long projectId)
    {
        Long userId = SecurityUtils.getUserId();
        FrontProject project = frontProjectService.getProject(userId, projectId);
        frontProjectPreviewService.getStructure(userId, projectId, "backend");
        if (!"0".equals(project.getFrontendEnabled()))
        {
            frontProjectPreviewService.getStructure(userId, projectId, "frontend");
        }
        frontProjectPreviewService.getStructure(userId, projectId, "admin_frontend");
        frontProjectPreviewService.getStructure(userId, projectId, "sql");
        return toAjax(frontProjectPreviewService.markPreviewReady(userId, projectId));
    }

    @PostMapping("/{projectId}/run-preview")
    public AjaxResult startRunPreview(@PathVariable Long projectId)
    {
        Long userId = SecurityUtils.getUserId();
        return success(frontProjectRunPreviewService.start(userId, projectId));
    }

    @GetMapping("/{projectId}/run-preview")
    public AjaxResult runPreviewStatus(@PathVariable Long projectId)
    {
        Long userId = SecurityUtils.getUserId();
        return success(frontProjectRunPreviewService.status(userId, projectId));
    }

    @PostMapping("/{projectId}/run-preview/stop")
    public AjaxResult stopRunPreview(@PathVariable Long projectId)
    {
        Long userId = SecurityUtils.getUserId();
        return success(frontProjectRunPreviewService.stop(userId, projectId));
    }

    @GetMapping("/{projectId}/structure")
    public AjaxResult structure(@PathVariable Long projectId, @RequestParam String templateType)
    {
        Long userId = SecurityUtils.getUserId();
        return success(frontProjectPreviewService.getStructure(userId, projectId, templateType));
    }

    @GetMapping("/{projectId}/file-content")
    public AjaxResult fileContent(@PathVariable Long projectId, PreviewFileRequest request)
    {
        Long userId = SecurityUtils.getUserId();
        return success(frontProjectPreviewService.getFileContent(userId, projectId, request));
    }

    @GetMapping("/{projectId}/download")
    public void download(@PathVariable Long projectId, @RequestParam(required = false) String templateType, HttpServletResponse response) throws IOException
    {
        Long userId = SecurityUtils.getUserId();
        FrontProject project = frontProjectService.getProject(userId, projectId);
        byte[] data = StringUtils.isNotEmpty(templateType)
                ? frontProjectPreviewService.download(userId, projectId, templateType)
                : frontProjectPreviewService.downloadAll(userId, projectId);
        response.reset();
        FileUtils.setAttachmentResponseHeader(response, sourceZipName(project));
        response.addHeader("Content-Length", "" + data.length);
        response.setContentType("application/octet-stream; charset=UTF-8");
        IOUtils.write(data, response.getOutputStream());
    }

    private String sourceZipName(FrontProject project)
    {
        String fallback = project != null && project.getProjectId() != null
                ? "project-" + project.getProjectId()
                : "easycode-project";
        String rawName = project == null ? null : project.getProjectName();
        String safeName = StringUtils.isEmpty(rawName) ? fallback : rawName.trim().replaceAll("[\\\\/:*?\"<>|]", "_");
        if (StringUtils.isEmpty(safeName))
        {
            safeName = fallback;
        }
        return safeName + ".zip";
    }
}
