package com.iqser.red.service.persistence.service.v2.api.external.resource;

import java.util.List;

import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemplateModel;
import com.iqser.red.service.persistence.service.v2.api.external.model.DossierAttributeDefinitionList;
import com.iqser.red.service.persistence.service.v2.api.external.model.DossierStatusDefinitionList;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileAttributeDefinitionList;
import com.iqser.red.service.persistence.service.v2.api.external.model.ReportTemplateList;

import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
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.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.multipart.MultipartFile;

import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentDefinition;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentDefinitionAddRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentDefinitionUpdateRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.DroolsValidationResponse;
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingMetadataModel;
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingSummary;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;

@ApiResponses(value = {@ApiResponse(responseCode = "429", description = "Too many requests.")})
@ResponseStatus(HttpStatus.OK)
public interface DossierTemplateResource {

    String DOSSIER_TEMPLATE_PATH = "/dossier-templates";
    String PATH = ExternalApiConstants.BASE_PATH + DOSSIER_TEMPLATE_PATH;

    String ENTITY_RULES_PATH = "/entity-rules";
    String DATE_FORMATS_PATH = "/date-formats";
    String COMPONENT_RULES_PATH = "/component-rules";
    String COMPONENT_MAPPINGS_PATH = "/component-mappings";
    String DOSSIER_STATUS_DEFINITIONS_PATH = "/dossier-status-definitions";
    String DOSSIER_ATTRIBUTE_DEFINITION_PATH = "/dossier-attribute-definitions";

    String FILE_ATTRIBUTE_DEFINITIONS_PATH = "/file-attribute-definitions";
    String DOSSIER_TEMPLATE_ID_PARAM = "dossierTemplateId";
    String DOSSIER_TEMPLATE_ID_PATH_VARIABLE = "/{" + DOSSIER_TEMPLATE_ID_PARAM + "}";
    String REPORT_TEMPLATES_PATH = "/report-templates";

    String COMPONENT_MAPPING_ID_PARAM = "componentMappingId";
    String COMPONENT_MAPPING_ID_PATH_VARIABLE = "/{" + COMPONENT_MAPPING_ID_PARAM + "}";

    String DRY_RUN_PARAM = "dryRun";
    String ENCODING_PARAM = "encoding";
    String DELIMITER_PARAM = "delimiter";
    String QUOTE_CHAR_PARAM = "quoteChar";
    String MAPPING_NAME_PARAM = "name";

    String INCLUDE_SOFT_DELETED = "includeSoftDeleted";
    String COMPONENT_ID_PARAM = "componentId";
    String COMPONENT_ID_PATH = "/{" + COMPONENT_ID_PARAM + "}";
    String COMPONENT_IDS = "componentIds";
    String COMPONENT_PATH = "/component-definitions";
    String REORDER_PATH = "/reorder";
    String RESTORE_PATH = "/restore";


    @GetMapping(value = PATH, produces = MediaType.APPLICATION_JSON_VALUE)
    @Operation(summary = "Lists all existing DossierTemplates.", description = "None")
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "List of all existing DossierTemplates.")})
    List<DossierTemplateModel> getAllDossierTemplates();


    @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
    @Operation(summary = "Get a specific DossierTemplate by its identifier.", description = "None")
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Get a specific DossierTemplate by its identifier."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")})
    DossierTemplateModel getDossierTemplate(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId);


    @ResponseStatus(value = HttpStatus.NO_CONTENT)
    @PostMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + ENTITY_RULES_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @Operation(summary = "Upload a component or entity rules file in drools format for a specific DossierTemplate.")
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Rules upload successful."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found."), @ApiResponse(responseCode = "400", description = "Uploaded rules could not be verified."), @ApiResponse(responseCode = "422", description = "Uploaded rules could not be compiled.")})
    ResponseEntity<DroolsValidationResponse> uploadEntityRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
                                                               @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file,
                                                               @Parameter(name = DRY_RUN_PARAM, description = "If true rules will be only validated not stored.") @RequestParam(value = DRY_RUN_PARAM, required = false, defaultValue = "false") boolean dryRun);


    @ResponseBody
    @ResponseStatus(value = HttpStatus.OK)
    @Operation(summary = "Returns file containing the currently used entity rules.")
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")})
    @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + ENTITY_RULES_PATH)
    ResponseEntity<?> downloadEntityRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId);


    @ResponseStatus(value = HttpStatus.NO_CONTENT)
    @PostMapping(value = PATH
                         + DOSSIER_TEMPLATE_ID_PATH_VARIABLE
                         + COMPONENT_RULES_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @Operation(summary = "Upload a component or entity rules file in drools format for a specific DossierTemplate.")
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Rules upload successful."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found."), @ApiResponse(responseCode = "400", description = "Uploaded rules could not be verified."), @ApiResponse(responseCode = "422", description = "Uploaded rules could not be compiled.")})
    ResponseEntity<DroolsValidationResponse> uploadComponentRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
                                                                  @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file,
                                                                  @Parameter(name = DRY_RUN_PARAM, description = "If true rules will be only validated not stored.") @RequestParam(value = DRY_RUN_PARAM, required = false, defaultValue = "false") boolean dryRun);


    @ResponseBody
    @ResponseStatus(value = HttpStatus.OK)
    @Operation(summary = "Returns file containing the currently used component rules.")
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")})
    @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_RULES_PATH)
    ResponseEntity<?> downloadComponentRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId);


    @ResponseStatus(value = HttpStatus.NO_CONTENT)
    @PostMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + DATE_FORMATS_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @Operation(summary = "Upload a date formats file for a specific DossierTemplate.")
    @ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Date formats upload successful."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found."), @ApiResponse(responseCode = "400", description = "Uploaded date formats could not be verified."), @ApiResponse(responseCode = "422", description = "Uploaded date formats file could not be parsed.")})
    ResponseEntity<?> uploadDateFormats(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
                                        @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file);


    @ResponseBody
    @ResponseStatus(value = HttpStatus.NO_CONTENT)
    @Operation(summary = "Returns file containing the currently used date formats.")
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")})
    @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + DATE_FORMATS_PATH)
    ResponseEntity<?> downloadDateFormats(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId);


    @Operation(summary = "Get the file attribute definitions of a DossierTemplate.", description = "None")
    @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + FILE_ATTRIBUTE_DEFINITIONS_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "File attribute definitions returned successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")})
    FileAttributeDefinitionList getFileAttributeDefinitions(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId);


    @Operation(summary = "Get the component mapping summaries of a DossierTemplate.", description = "None")
    @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping views returned successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")})
    ComponentMappingSummary getComponentMappingSummaries(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId);


    @Operation(summary = "Upload a new component mapping to a DossierTemplate.", description = "None")
    @PostMapping(value = PATH
                         + DOSSIER_TEMPLATE_ID_PATH_VARIABLE
                         + COMPONENT_MAPPINGS_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping uploaded successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")})
    ComponentMappingMetadataModel uploadMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
                                                @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file,
                                                @Parameter(name = MAPPING_NAME_PARAM, description = "String of what the mapping should be accessible under. If left empty, the name of the file without the ending will be used as name.") @RequestParam(value = MAPPING_NAME_PARAM, required = false, defaultValue = "") String name,
                                                @Parameter(name = ENCODING_PARAM, description = "The encoding of the file. Default is UTF-8.") @RequestParam(value = ENCODING_PARAM, required = false, defaultValue = "UTF-8") String encoding,
                                                @Parameter(name = DELIMITER_PARAM, description = "The delimiter used in the file. Default is ','") @RequestParam(value = DELIMITER_PARAM, required = false, defaultValue = ",") String delimiter,
                                                @Parameter(name = QUOTE_CHAR_PARAM, description = "The quote char used in the file. Default is '\"'") @RequestParam(value = QUOTE_CHAR_PARAM, required = false, defaultValue = "\"") String quoteChar);


    @Operation(summary = "Update an existing component mapping of a DossierTemplate.", description = "None")
    @PutMapping(value = PATH
                        + DOSSIER_TEMPLATE_ID_PATH_VARIABLE
                        + COMPONENT_MAPPINGS_PATH
                        + COMPONENT_MAPPING_ID_PATH_VARIABLE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping updated successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")})
    ComponentMappingMetadataModel updateMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
                                                @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId,
                                                @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file,
                                                @Parameter(name = MAPPING_NAME_PARAM, description = "String of what the mapping should be accessible under. If left empty, the name of the file without the ending will be used as name.") @RequestParam(value = MAPPING_NAME_PARAM, required = false, defaultValue = "") String name,
                                                @Parameter(name = ENCODING_PARAM, description = "The encoding of the file. Default is UTF-8.") @RequestParam(value = ENCODING_PARAM, required = false, defaultValue = "UTF-8") String encoding,
                                                @Parameter(name = DELIMITER_PARAM, description = "The delimiter used in the file. Default is ','") @RequestParam(value = DELIMITER_PARAM, required = false, defaultValue = ",") String delimiter,
                                                @Parameter(name = QUOTE_CHAR_PARAM, description = "The quote char used in the file. Default is '\"'") @RequestParam(value = QUOTE_CHAR_PARAM, required = false, defaultValue = "\"") String quoteChar);


    @ResponseBody
    @ResponseStatus(value = HttpStatus.OK)
    @Operation(summary = "Returns file containing the specified mapping as a file.")
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")})
    @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH + COMPONENT_MAPPING_ID_PATH_VARIABLE)
    ResponseEntity<?> downloadMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId);


    @ResponseBody
    @ResponseStatus(value = HttpStatus.OK)
    @Operation(summary = "Deletes a specified mapping.")
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")})
    @DeleteMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH + COMPONENT_MAPPING_ID_PATH_VARIABLE)
    ResponseEntity<?> deleteMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId);


    @Operation(summary = "Returns the list of all existing dossier status definitions.", description = "None")
    @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + DOSSIER_STATUS_DEFINITIONS_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully returned the dossier status definitions for the specified dossier template."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")})
    DossierStatusDefinitionList getDossierStatusDefinitions(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId);


    @Operation(summary = "Returns the list of all existing dossier attribute definitions.", description = "None")
    @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + DOSSIER_ATTRIBUTE_DEFINITION_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully returned the dossier attribute definitions for the specified dossier template."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")})
    DossierAttributeDefinitionList getDossierAttributeDefinitions(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId);


    @ResponseBody
    @ResponseStatus(HttpStatus.CREATED)
    @Operation(summary = "Create new components", description = "Create new components for a given dossier template.")
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Created")})
    @PostMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    List<ComponentDefinition> createComponents(@Valid @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
                                               @RequestBody List<ComponentDefinitionAddRequest> componentDefinitionUpdateRequests);


    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @Operation(summary = "Get all components", description = "Get all components for a given dossier template.")
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
    @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
    List<ComponentDefinition> getComponents(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
                                            @Parameter(name = INCLUDE_SOFT_DELETED, description = "Include files which are soft deleted. Default is false.") @RequestParam(value = INCLUDE_SOFT_DELETED, defaultValue = "false") boolean includeSoftDeleted);


    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @Operation(summary = "Get a component by ID", description = "Get specific details for a component by ID.")
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified component is not found.")})
    @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_PATH + COMPONENT_ID_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
    ComponentDefinition getComponent(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @PathVariable(COMPONENT_ID_PARAM) String componentId);


    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @Operation(summary = "Update components", description = "Update existing components.")
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
    @PutMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    List<ComponentDefinition> updateComponents(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
                                               @Valid @RequestBody List<ComponentDefinitionUpdateRequest> componentDefinitionUpdateRequests);


    @ResponseBody
    @ResponseStatus(HttpStatus.NO_CONTENT)
    @Operation(summary = "Delete components", description = "Delete existing components by their IDs.")
    @ApiResponses(value = {@ApiResponse(responseCode = "204", description = "No Content")})
    @DeleteMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_PATH)
    void deleteComponents(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @RequestParam(name = COMPONENT_IDS) List<String> componentIds);


    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @Operation(summary = "Restore components", description = "Restore  previously soft deleted components based on their IDs.")
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
    @PutMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_PATH + RESTORE_PATH)
    List<ComponentDefinition> restoreComponents(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @RequestParam(name = COMPONENT_IDS) List<String> componentIds);


    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @Operation(summary = "Reorder components", description = "Reorder components based on their rank.")
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
    @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_PATH + REORDER_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
    List<ComponentDefinition> reorderComponents(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @RequestParam(name = COMPONENT_IDS) List<String> componentIds);


    @GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + REPORT_TEMPLATES_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
    @Operation(summary = "Returns the list of all available report templates", description = "None")
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully returned the report templates for the specified dossier template."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")})
    ReportTemplateList getReportTemplates(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId);

}
