package com.writeoff.module.meeting.controller;

import com.writeoff.common.api.ApiResponse;
import com.writeoff.common.api.PageResult;
import com.writeoff.module.meeting.dto.CreateMeetingRequest;
import com.writeoff.module.meeting.dto.MeetingQueryRequest;
import com.writeoff.module.meeting.dto.CreateMeetingMaterialsExportRequest;
import com.writeoff.module.meeting.dto.GenerateMeetingSummaryRequest;
import com.writeoff.module.meeting.dto.BindMeetingExpertsRequest;
import com.writeoff.module.meeting.dto.MeetingMaterialUploadSignRequest;
import com.writeoff.module.export.dto.CreateExportTaskRequest;
import com.writeoff.module.export.service.ExportTaskService;
import com.writeoff.module.expert.dto.AddBankCardRequest;
import com.writeoff.module.expert.dto.CreateExpertRequest;
import com.writeoff.module.meeting.dto.MeetingInvoiceConfigRequest;
import com.writeoff.module.meeting.dto.MeetingLaborAgreementExtractApplyRequest;
import com.writeoff.module.meeting.dto.MeetingLaborAgreementExtractQueryRequest;
import com.writeoff.module.meeting.dto.MeetingLaborAgreementExtractSubmitRequest;
import com.writeoff.module.meeting.dto.SaveMeetingMaterialRequest;
import com.writeoff.module.meeting.dto.SubmitMeetingRequest;
import com.writeoff.module.meeting.dto.SubmitMeetingMaterialRequest;
import com.writeoff.module.meeting.dto.WithdrawMeetingRequest;
import com.writeoff.module.meeting.model.Meeting;
import com.writeoff.module.meeting.model.MeetingExpertBinding;
import com.writeoff.module.meeting.model.MeetingLaborAgreementExtractResult;
import com.writeoff.module.meeting.model.MeetingMaterial;
import com.writeoff.module.meeting.model.MeetingMaterialHistory;
import com.writeoff.module.expert.model.ExpertBankCardInfo;
import com.writeoff.module.expert.model.ExpertInfo;
import com.writeoff.module.expert.service.PlatformExpertService;
import com.writeoff.module.meeting.service.MeetingExpertBindingService;
import com.writeoff.module.meeting.service.MeetingLaborAgreementExtractService;
import com.writeoff.module.meeting.service.MeetingMaterialService;
import com.writeoff.module.meeting.service.MeetingService;
import com.writeoff.module.template.model.TemplateInfo;
import com.writeoff.module.template.service.TemplateService;
import com.writeoff.module.ocr.dto.DocumentExtractTaskSubmitResponse;
import com.writeoff.security.DataScopeType;
import com.writeoff.security.RequirePermission;
import javax.validation.Valid;
import org.springframework.web.bind.annotation.*;

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

@RestController
@RequestMapping("/api/meetings")
public class MeetingController {
    private final MeetingService meetingService;
    private final MeetingMaterialService meetingMaterialService;
    private final TemplateService templateService;
    private final PlatformExpertService platformExpertService;
    private final MeetingExpertBindingService meetingExpertBindingService;
    private final MeetingLaborAgreementExtractService meetingLaborAgreementExtractService;
    private final ExportTaskService exportTaskService;

    public MeetingController(MeetingService meetingService,
                             MeetingMaterialService meetingMaterialService,
                             TemplateService templateService,
                             PlatformExpertService platformExpertService,
                             MeetingExpertBindingService meetingExpertBindingService,
                             MeetingLaborAgreementExtractService meetingLaborAgreementExtractService,
                             ExportTaskService exportTaskService) {
        this.meetingService = meetingService;
        this.meetingMaterialService = meetingMaterialService;
        this.templateService = templateService;
        this.platformExpertService = platformExpertService;
        this.meetingExpertBindingService = meetingExpertBindingService;
        this.meetingLaborAgreementExtractService = meetingLaborAgreementExtractService;
        this.exportTaskService = exportTaskService;
    }

    @GetMapping
    @RequirePermission(value = "meeting.read", dataScope = DataScopeType.TENANT, auditAction = "MEETING_LIST")
    public ApiResponse<PageResult<Meeting>> list(MeetingQueryRequest query) {
        return ApiResponse.success(meetingService.list(query));
    }

    @GetMapping("/tenant-experts")
    @RequirePermission(value = "meeting.material.read", dataScope = DataScopeType.TENANT, auditAction = "MEETING_TENANT_EXPERT_LIST")
    public ApiResponse<PageResult<ExpertInfo>> tenantExperts(@RequestParam(value = "keyword", required = false) String keyword) {
        return ApiResponse.success(platformExpertService.list(keyword));
    }

    @PostMapping("/tenant-experts")
    @RequirePermission(value = "meeting.material.save", dataScope = DataScopeType.TENANT, auditAction = "MEETING_TENANT_EXPERT_CREATE")
    public ApiResponse<ExpertInfo> createTenantContextExpert(@RequestBody @Valid CreateExpertRequest request) {
        return ApiResponse.success(platformExpertService.create(request));
    }

    @PostMapping("/tenant-experts/{expertId}/bank-cards")
    @RequirePermission(value = "meeting.material.save", dataScope = DataScopeType.TENANT, auditAction = "MEETING_TENANT_EXPERT_BANK_CARD_CREATE")
    public ApiResponse<ExpertBankCardInfo> addTenantContextExpertBankCard(@PathVariable("expertId") Long expertId,
                                                                        @RequestBody @Valid AddBankCardRequest request) {
        return ApiResponse.success(platformExpertService.addCard(expertId, request));
    }

    @GetMapping("/{id}/experts")
    @RequirePermission(value = "meeting.material.read", dataScope = DataScopeType.MEETING, auditAction = "MEETING_EXPERT_BINDING_LIST")
    public ApiResponse<List<MeetingExpertBinding>> listExperts(@PathVariable("id") Long id) {
        return ApiResponse.success(meetingExpertBindingService.listByMeetingId(id));
    }

    @PostMapping("/{id}/experts/bind")
    @RequirePermission(value = "meeting.material.save", dataScope = DataScopeType.MEETING, auditAction = "MEETING_EXPERT_BINDING_SAVE")
    public ApiResponse<List<MeetingExpertBinding>> bindExperts(@PathVariable("id") Long id,
                                                               @RequestBody @Valid BindMeetingExpertsRequest request) {
        return ApiResponse.success(meetingExpertBindingService.bind(id, request));
    }

    @DeleteMapping("/{id}/experts/{expertId}")
    @RequirePermission(value = "meeting.material.save", dataScope = DataScopeType.MEETING, auditAction = "MEETING_EXPERT_BINDING_DELETE")
    public ApiResponse<List<MeetingExpertBinding>> unbindExpert(@PathVariable("id") Long id,
                                                                @PathVariable("expertId") Long expertId) {
        return ApiResponse.success(meetingExpertBindingService.unbindOne(id, expertId));
    }

    @PostMapping("/{id}/labor-agreement-extract/task")
    @RequirePermission(value = "meeting.material.save", dataScope = DataScopeType.MEETING, auditAction = "MEETING_LABOR_AGREEMENT_EXTRACT_SUBMIT")
    public ApiResponse<DocumentExtractTaskSubmitResponse> submitLaborAgreementExtractTask(@PathVariable("id") Long id,
                                                                                          @RequestBody @Valid MeetingLaborAgreementExtractSubmitRequest request) {
        return ApiResponse.success(meetingLaborAgreementExtractService.submit(id, request));
    }

    @PostMapping("/{id}/labor-agreement-extract/query")
    @RequirePermission(value = "meeting.material.save", dataScope = DataScopeType.MEETING, auditAction = "MEETING_LABOR_AGREEMENT_EXTRACT_QUERY")
    public ApiResponse<MeetingLaborAgreementExtractResult> queryLaborAgreementExtract(@PathVariable("id") Long id,
                                                                                      @RequestBody @Valid MeetingLaborAgreementExtractQueryRequest request) {
        return ApiResponse.success(meetingLaborAgreementExtractService.query(id, request.getTaskId()));
    }

    @PostMapping("/{id}/labor-agreement-extract/apply")
    @RequirePermission(value = "meeting.material.save", dataScope = DataScopeType.MEETING, auditAction = "MEETING_LABOR_AGREEMENT_EXTRACT_APPLY")
    public ApiResponse<Map<String, Object>> applyLaborAgreementExtract(@PathVariable("id") Long id,
                                                                       @RequestBody @Valid MeetingLaborAgreementExtractApplyRequest request) {
        return ApiResponse.success(meetingLaborAgreementExtractService.apply(id, request));
    }

    @PostMapping
    @RequirePermission(value = "meeting.create", dataScope = DataScopeType.PROJECT, auditAction = "MEETING_CREATE")
    public ApiResponse<Meeting> create(@RequestBody @Valid CreateMeetingRequest request) {
        return ApiResponse.success(meetingService.create(request));
    }

    @GetMapping("/{id}")
    @RequirePermission(value = "meeting.read", dataScope = DataScopeType.MEETING, auditAction = "MEETING_DETAIL")
    public ApiResponse<Meeting> detail(@PathVariable("id") Long id) {
        return ApiResponse.success(meetingService.getById(id));
    }

    @GetMapping("/{id}/change-logs")
    @RequirePermission(value = "meeting.change-log.read", dataScope = DataScopeType.MEETING, auditAction = "MEETING_CHANGE_LOG_LIST")
    public ApiResponse<List<Map<String, Object>>> changeLogs(@PathVariable("id") Long id) {
        return ApiResponse.success(meetingService.listChangeLogs(id));
    }

    @PutMapping("/{id}")
    @RequirePermission(value = "meeting.create", dataScope = DataScopeType.MEETING, auditAction = "MEETING_UPDATE")
    public ApiResponse<Meeting> update(@PathVariable("id") Long id,
                                       @RequestBody @Valid CreateMeetingRequest request) {
        return ApiResponse.success(meetingService.update(id, request));
    }

    @PutMapping("/{id}/invoice-config")
    @RequirePermission(value = "meeting.invoice.config", dataScope = DataScopeType.MEETING, auditAction = "MEETING_INVOICE_CONFIG_UPDATE")
    public ApiResponse<Meeting> updateInvoiceConfig(@PathVariable("id") Long id,
                                                    @RequestBody @Valid MeetingInvoiceConfigRequest request) {
        return ApiResponse.success(meetingService.updateInvoiceConfig(id, request));
    }

    @PostMapping("/{id}/submit")
    @RequirePermission(value = "meeting.submit", dataScope = DataScopeType.MEETING, auditAction = "MEETING_SUBMIT")
    public ApiResponse<Map<String, Object>> submit(@PathVariable("id") Long id,
                                                   @RequestBody @Valid SubmitMeetingRequest request) {
        return ApiResponse.success(meetingService.submit(id, request));
    }

    @PostMapping("/{id}/withdraw")
    @RequirePermission(value = "meeting.withdraw", dataScope = DataScopeType.MEETING, auditAction = "MEETING_WITHDRAW")
    public ApiResponse<Map<String, Object>> withdraw(@PathVariable("id") Long id,
                                                     @RequestBody @Valid WithdrawMeetingRequest request) {
        return ApiResponse.success(meetingService.withdraw(id, request));
    }

    @PostMapping("/{id}/delete")
    @RequirePermission(value = "meeting.delete", dataScope = DataScopeType.MEETING, auditAction = "MEETING_DELETE")
    public ApiResponse<String> deleteDraft(@PathVariable("id") Long id) {
        meetingService.deleteDraft(id);
        return ApiResponse.success("ok");
    }

    @PostMapping("/{id}/cancel")
    @RequirePermission(value = "meeting.cancel", dataScope = DataScopeType.MEETING, auditAction = "MEETING_CANCEL")
    public ApiResponse<Map<String, Object>> cancel(@PathVariable("id") Long id,
                                                    @RequestBody Map<String, String> body) {
        String reason = body == null ? null : body.get("reason");
        return ApiResponse.success(meetingService.cancel(id, reason));
    }

    @GetMapping("/{id}/materials")
    @RequirePermission(value = "meeting.material.read", dataScope = DataScopeType.MEETING_MODULE, auditAction = "MEETING_MATERIAL_LIST")
    public ApiResponse<PageResult<MeetingMaterial>> materials(@PathVariable("id") Long id) {
        return ApiResponse.success(meetingMaterialService.list(id));
    }

    @PostMapping("/{id}/materials/{moduleCode}/save")
    @RequirePermission(value = "meeting.material.save", dataScope = DataScopeType.MEETING_MODULE, auditAction = "MEETING_MATERIAL_SAVE")
    public ApiResponse<MeetingMaterial> saveMaterial(@PathVariable("id") Long id,
                                                     @PathVariable("moduleCode") String moduleCode,
                                                     @RequestBody @Valid SaveMeetingMaterialRequest request) {
        return ApiResponse.success(meetingMaterialService.save(id, moduleCode, request));
    }

    @PostMapping("/{id}/materials/{moduleCode}/submit")
    @RequirePermission(value = "meeting.material.submit", dataScope = DataScopeType.MEETING_MODULE, auditAction = "MEETING_MATERIAL_SUBMIT")
    public ApiResponse<MeetingMaterial> submitMaterial(@PathVariable("id") Long id,
                                                       @PathVariable("moduleCode") String moduleCode,
                                                       @RequestBody @Valid SubmitMeetingMaterialRequest request) {
        return ApiResponse.success(meetingMaterialService.submit(id, moduleCode, request));
    }

    @PostMapping("/{id}/materials/{moduleCode}/upload-sign")
    @RequirePermission(value = "meeting.material.save", dataScope = DataScopeType.MEETING_MODULE, auditAction = "MEETING_MATERIAL_UPLOAD_SIGN")
    public ApiResponse<Map<String, Object>> uploadMaterialSign(@PathVariable("id") Long id,
                                                               @PathVariable("moduleCode") String moduleCode,
                                                               @RequestBody @Valid MeetingMaterialUploadSignRequest request) {
        return ApiResponse.success(meetingMaterialService.presignMaterialUpload(id, moduleCode, request.getFileName(), request.getContentType()));
    }

    @GetMapping("/{id}/materials/{moduleCode}/current")
    @RequirePermission(value = "meeting.material.read", dataScope = DataScopeType.MEETING_MODULE, auditAction = "MEETING_MATERIAL_CURRENT")
    public ApiResponse<MeetingMaterial> currentMaterial(@PathVariable("id") Long id,
                                                        @PathVariable("moduleCode") String moduleCode) {
        return ApiResponse.success(meetingMaterialService.current(id, moduleCode));
    }

    @GetMapping("/{id}/materials/{moduleCode}/history")
    @RequirePermission(value = "meeting.material.history.read", dataScope = DataScopeType.MEETING_MODULE, auditAction = "MEETING_MATERIAL_HISTORY")
    public ApiResponse<PageResult<MeetingMaterialHistory>> materialHistory(@PathVariable("id") Long id,
                                                                           @PathVariable("moduleCode") String moduleCode) {
        return ApiResponse.success(meetingMaterialService.history(id, moduleCode));
    }

    @GetMapping("/{id}/matched-templates")
    @RequirePermission(value = "meeting.material.read", dataScope = DataScopeType.MEETING, auditAction = "MEETING_MATCHED_TEMPLATES")
    public ApiResponse<List<TemplateInfo>> matchedTemplates(@PathVariable("id") Long id) {
        Meeting meeting = meetingService.getById(id);
        return ApiResponse.success(templateService.listMatchedForMeeting(id, meeting.getProjectId()));
    }

    @PostMapping("/{id}/materials/export")
    @RequirePermission(value = "meeting.material.export", dataScope = DataScopeType.MEETING, auditAction = "MEETING_MATERIAL_EXPORT")
    public ApiResponse<Map<String, Object>> exportMaterials(@PathVariable("id") Long id,
                                                            @RequestBody @Valid CreateMeetingMaterialsExportRequest request) {
        return ApiResponse.success(meetingMaterialService.createMaterialsExportTask(id, request.getIdempotencyKey(), request.getFileName()));
    }

    @PostMapping("/{id}/summary/generate")
    @RequirePermission(value = "meeting.material.export", dataScope = DataScopeType.MEETING, auditAction = "MEETING_SUMMARY_GENERATE")
    public ApiResponse<Map<String, Object>> generateSummary(@PathVariable("id") Long id,
                                                            @RequestBody @Valid GenerateMeetingSummaryRequest request) {
        return ApiResponse.success(meetingMaterialService.generateSummaryTask(id, request.getIdempotencyKey(), request.getFileName()));
    }

    @GetMapping("/{id}/summary/task-status")
    @RequirePermission(value = "meeting.material.export", dataScope = DataScopeType.MEETING, auditAction = "MEETING_SUMMARY_TASK_STATUS")
    public ApiResponse<Map<String, Object>> summaryTaskStatus(@PathVariable("id") Long id,
                                                              @RequestParam(value = "taskId", required = false) Long taskId) {
        return ApiResponse.success(meetingMaterialService.getSummaryTaskStatus(id, taskId));
    }

    @PostMapping("/{id}/summary/refresh-token")
    @RequirePermission(value = "meeting.material.export", dataScope = DataScopeType.MEETING, auditAction = "MEETING_SUMMARY_REFRESH_TOKEN")
    public ApiResponse<Map<String, Object>> refreshSummaryToken(@PathVariable("id") Long id,
                                                                @RequestParam("taskId") Long taskId) {
        return ApiResponse.success(meetingMaterialService.refreshSummaryToken(id, taskId));
    }

    @GetMapping("/{id}/summary/download")
    @RequirePermission(value = "meeting.material.export", dataScope = DataScopeType.MEETING, auditAction = "MEETING_SUMMARY_DOWNLOAD")
    public ApiResponse<Map<String, Object>> downloadSummary(@PathVariable("id") Long id,
                                                            @RequestParam("taskId") Long taskId,
                                                            @RequestParam("token") String token) {
        return ApiResponse.success(meetingMaterialService.downloadSummary(id, taskId, token));
    }

    @PostMapping("/export")
    @RequirePermission(value = "meeting.read", dataScope = DataScopeType.TENANT, auditAction = "MEETING_EXPORT")
    public ApiResponse<Map<String, Object>> exportMeetings(@RequestBody @Valid CreateExportTaskRequest request) {
        request.setTaskCode("MEETING_EXPORT");
        request.setBizType("MEETING");
        return ApiResponse.success(exportTaskService.create(request));
    }
}
