package de.avatic.lcc.controller.configuration;

import de.avatic.lcc.dto.generic.PropertyDTO;
import de.avatic.lcc.dto.generic.ValidityPeriodDTO;
import de.avatic.lcc.dto.configuration.properties.SetPropertyDTO;
import de.avatic.lcc.model.db.country.IsoCode;
import de.avatic.lcc.service.access.CountryService;
import de.avatic.lcc.service.access.PropertyService;
import de.avatic.lcc.service.configuration.PropertyApprovalService;
import de.avatic.lcc.util.exception.badrequest.NotFoundException;
import jakarta.validation.constraints.Min;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * REST controller for managing application properties.
 * Provides endpoints for listing, modifying, and approving staged properties.
 */
@RestController
@RequestMapping("/api/properties")
@Validated
public class PropertyController {

    private final PropertyService propertyService;
    private final PropertyApprovalService propertyApprovalService;
    private final CountryService countryService;

    /**
     * Constructs a new PropertyController with the provided services.
     *
     * @param propertyService         Service for managing properties.
     * @param propertyApprovalService Service for managing staged changes.
     */
    public PropertyController(PropertyService propertyService, PropertyApprovalService propertyApprovalService, CountryService countryService) {
        this.propertyService = propertyService;
        this.propertyApprovalService = propertyApprovalService;
        this.countryService = countryService;
    }

    /**
     * Retrieves the list of all application properties.
     *
     * @return ResponseEntity containing the list of PropertyDTO objects.
     */
    @GetMapping({"/", ""})
    @PreAuthorize("hasRole('SUPER')")
    public ResponseEntity<List<PropertyDTO>> listProperties(@RequestParam(name = "property_set", defaultValue = "0", required = false) @Min(0) Integer propertySetId) {
        var props = propertyService.listProperties(propertySetId);

        if (props.isEmpty()) {
            throw new NotFoundException(NotFoundException.NotFoundType.PROPERTY_SET, "id", propertySetId.toString());
        }

        return ResponseEntity.ok(props);

    }


    /**
     * Retrieves all validity periods of the application properties.
     *
     * @return ResponseEntity containing the list of ValidityPeriodDTO objects.
     */
    @GetMapping({"/periods", "/periods/"})
    @PreAuthorize("hasRole('SUPER')")
    public ResponseEntity<List<ValidityPeriodDTO>> listPeriods() {
        return ResponseEntity.ok(propertyService.listPropertySets());
    }

    /**
     * Invalidates a specific validity period by its ID.
     *
     * @param id The ID of the validity period to invalidate.
     * @return ResponseEntity indicating the operation status.
     */
    @DeleteMapping({"/periods/{id}", "/periods/{id}/"})
    @PreAuthorize("hasRole('SUPER')")
    public ResponseEntity<Void> invalidatePeriod(@PathVariable @Min(1) Integer id) {
        propertyService.invalidate(id);
        return ResponseEntity.ok().build();

    }

    /**
     * Sets a country-specific property by ISO code and external mapping ID.
     *
     * @param isoCode   The ISO code of the country.
     * @param mappingId The external mapping ID for the property.
     * @param dto     The value to set for the property.
     * @return ResponseEntity indicating the operation status.
     */
    @PutMapping({"/country/{iso}/{external_mapping_id}", "/country/{iso}/{external_mapping_id}/"})
    @PreAuthorize("hasRole('SUPER')")
    public ResponseEntity<Void> setCountryProperty(@PathVariable("iso") IsoCode isoCode, @PathVariable(name = "external_mapping_id") String mappingId, @RequestBody SetPropertyDTO dto) {
        countryService.setProperties(isoCode, mappingId, dto.getValue());
        return ResponseEntity.ok().build();
    }

    /**
     * Sets a system-wide property by external mapping ID.
     *
     * @param externalMappingId The external mapping ID for the property.
     * @param dto             The value to set for the property.
     * @return ResponseEntity indicating the operation status.
     */
    @PutMapping({"/system/{external_mapping_id}", "/system/{external_mapping_id}/"})
    @PreAuthorize("hasRole('SUPER')")
    public ResponseEntity<Void> setProperties(@PathVariable(name = "external_mapping_id") String externalMappingId, @RequestBody SetPropertyDTO dto) {
        propertyService.setProperties(externalMappingId, dto.getValue());
        return ResponseEntity.ok().build();
    }

    /**
     * Checks if there are any staged changes for properties.
     *
     * @return ResponseEntity containing true if drafts are present, false otherwise.
     */
    @GetMapping({"/staged_changes", "/staged_changes/"})
    @PreAuthorize("hasRole('SUPER')")
    public ResponseEntity<Boolean> checkPropertiesDrafts() {
        return ResponseEntity.ok(propertyApprovalService.hasPropertiesDraft());
    }

    /**
     * Approves all staged changes for properties.
     *
     * @return ResponseEntity indicating the operation status.
     */
    @PutMapping({"/staged_changes", "/staged_changes/"})
    @PreAuthorize("hasRole('SUPER')")
    public ResponseEntity<Void> approvePropertiesDrafts() {
        propertyApprovalService.applyDraft();
        return ResponseEntity.ok().build();
    }

}
