package eu.eudat.controllers;

import eu.eudat.data.entities.UserDatasetProfile;
import eu.eudat.data.entities.UserInfo;
import eu.eudat.data.query.items.table.datasetprofile.DatasetProfileTableRequestItem;
import eu.eudat.exceptions.datasetprofile.DatasetProfileNewVersionException;
import eu.eudat.exceptions.datasetprofile.DatasetProfileWithDatasetsExeption;
import eu.eudat.logic.managers.AdminManager;
import eu.eudat.logic.managers.DatasetProfileManager;
import eu.eudat.logic.managers.MetricsManager;
import eu.eudat.logic.managers.UserManager;
import eu.eudat.logic.proxy.config.configloaders.ConfigLoader;
import eu.eudat.logic.security.claims.ClaimedAuthorities;
import eu.eudat.logic.services.ApiContext;
import eu.eudat.models.data.admin.composite.DatasetProfile;
import eu.eudat.models.data.datasetprofile.DatasetProfileListingModel;
import eu.eudat.models.data.helpers.common.DataTableData;
import eu.eudat.models.data.helpers.responses.ResponseItem;
import eu.eudat.models.data.listingmodels.UserInfoListingModel;
import eu.eudat.models.data.security.Principal;
import eu.eudat.models.data.user.composite.PagedDatasetProfile;
import eu.eudat.types.ApiMessageCode;
import eu.eudat.types.MetricNames;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.transaction.Transactional;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

import static eu.eudat.types.Authorities.ADMIN;
import static eu.eudat.types.Authorities.DATASET_PROFILE_MANAGER;

@RestController
@CrossOrigin
@RequestMapping(value = {"/api/admin/"})
public class Admin extends BaseController {

	private DatasetProfileManager datasetProfileManager;
	private UserManager userManager;
	private ConfigLoader configLoader;
	private final MetricsManager metricsManager;

	@Autowired
	public Admin(ApiContext apiContext, DatasetProfileManager datasetProfileManager, UserManager userManager/*, Logger logger*/, ConfigLoader configLoader, MetricsManager metricsManager) {
		super(apiContext);
		this.datasetProfileManager = datasetProfileManager;
		this.userManager = userManager;
		this.configLoader = configLoader;
		this.metricsManager = metricsManager;
	}

	@Transactional
	@RequestMapping(method = RequestMethod.POST, value = {"/addDmp"}, consumes = "application/json", produces = "application/json")
	public ResponseEntity<Object> addDmp(@Valid @RequestBody DatasetProfile profile, @ClaimedAuthorities(claims = {ADMIN ,DATASET_PROFILE_MANAGER}) Principal principal) {
		//this.getLoggerService().info(principal, "Admin Added Dataset Profile");
		DatasetProfile shortenProfile = profile.toShort();
		eu.eudat.data.entities.DatasetProfile modelDefinition = AdminManager.generateViewStyleDefinition(shortenProfile, getApiContext());
		modelDefinition.setGroupId(UUID.randomUUID());
		modelDefinition.setVersion((short) 0);

		eu.eudat.data.entities.DatasetProfile datasetProfile = this.getApiContext().getOperationsContext().getDatabaseRepository().getDatasetProfileDao().createOrUpdate(modelDefinition);
		UserDatasetProfile userDatasetProfile = new UserDatasetProfile();
		userDatasetProfile.setDatasetProfile(datasetProfile);
		UserInfo userInfo = getApiContext().getOperationsContext().getDatabaseRepository().getUserInfoDao().find(principal.getId());
		userDatasetProfile.setUser(userInfo);
		userDatasetProfile.setRole(0);
		getApiContext().getOperationsContext().getDatabaseRepository().getUserDatasetProfileDao().createOrUpdate(userDatasetProfile);
		datasetProfileManager.storeDatasetProfileUsers(datasetProfile, profile);

		metricsManager.increaseValue(MetricNames.DATASET_TEMPLATE, 1, MetricsManager.datasetTemplateStatus.get(datasetProfile.getStatus()) );
		return ResponseEntity.status(HttpStatus.OK).body(modelDefinition.getId());
	}

	@Transactional
	@RequestMapping(method = RequestMethod.POST, value = {"/addDmp/{id}"}, consumes = "application/json", produces = "application/json")
	public ResponseEntity<ResponseItem<UUID>> updateDmp(@PathVariable String id, @RequestBody DatasetProfile profile, @ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) {
		DatasetProfile shortenProfile = profile.toShort();
		eu.eudat.data.entities.DatasetProfile modelDefinition = AdminManager.generateViewStyleDefinition(shortenProfile, getApiContext());
		eu.eudat.data.entities.DatasetProfile datasetprofile = this.getApiContext().getOperationsContext().getDatabaseRepository().getDatasetProfileDao().find(UUID.fromString(id));
		datasetprofile.setDefinition(modelDefinition.getDefinition());
		Short oldStatus = datasetprofile.getStatus();
		datasetprofile.setStatus(modelDefinition.getStatus());
		datasetprofile.setLabel(modelDefinition.getLabel());
		datasetprofile.setDescription(modelDefinition.getDescription());
		datasetprofile.setLanguage(modelDefinition.getLanguage());
		eu.eudat.data.entities.DatasetProfile datasetProfile = this.getApiContext().getOperationsContext().getDatabaseRepository().getDatasetProfileDao().createOrUpdate(datasetprofile);
		datasetProfileManager.storeDatasetProfileUsers(datasetProfile, profile);
		if (datasetProfile.getStatus() == 1 && oldStatus == 0) {
			metricsManager.increaseValue(MetricNames.DATASET_TEMPLATE, 1, MetricNames.ACTIVE);
		}
		return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<UUID>().status(ApiMessageCode.NO_MESSAGE));
	}

	@Transactional
	@RequestMapping(method = RequestMethod.POST, value = {"/newVersion/{id}"}, produces = "application/json")
	public ResponseEntity newVersionDatasetProfile(@PathVariable String id, @RequestBody DatasetProfile profile, @ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) throws Exception {
		try {
			eu.eudat.data.entities.DatasetProfile modelDefinition = this.datasetProfileManager.createNewVersionDatasetProfile(id, profile);
			return ResponseEntity.status(HttpStatus.OK).body(modelDefinition.getId());
		} catch (DatasetProfileNewVersionException exception) {
			return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem<eu.eudat.models.data.admin.composite.DatasetProfile>().status(ApiMessageCode.ERROR_MESSAGE).message(exception.getMessage()));
		}
	}

	@RequestMapping(method = RequestMethod.GET, value = {"/get/{id}"}, produces = "application/json")
	@Transactional
	public ResponseEntity<ResponseItem<DatasetProfile>> get(@PathVariable String id, @ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) {
		eu.eudat.models.data.admin.composite.DatasetProfile datasetprofile = this.datasetProfileManager.getDatasetProfile(id);
		return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<DatasetProfile>().status(ApiMessageCode.NO_MESSAGE).payload(datasetprofile));
	}

	@RequestMapping(method = RequestMethod.POST, value = {"/datasetprofiles/getPaged"}, produces = "application/json")
	public @ResponseBody
	ResponseEntity<ResponseItem<DataTableData<DatasetProfileListingModel>>> getPaged(@RequestBody DatasetProfileTableRequestItem datasetProfileTableRequestItem, @ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) throws Exception {
		DataTableData<DatasetProfileListingModel> datasetProfileTableData = this.datasetProfileManager.getPaged(datasetProfileTableRequestItem, principal);
		return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<DataTableData<DatasetProfileListingModel>>().status(ApiMessageCode.NO_MESSAGE).payload(datasetProfileTableData));
	}

	@RequestMapping(method = RequestMethod.POST, value = {"/preview"}, consumes = "application/json", produces = "application/json")
	public ResponseEntity<ResponseItem<PagedDatasetProfile>> getPreview(@RequestBody DatasetProfile profile, @ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) {
		eu.eudat.data.entities.DatasetProfile modelDefinition = AdminManager.generateViewStyleDefinition(profile, getApiContext());
		eu.eudat.models.data.user.composite.DatasetProfile datasetProfile = userManager.generateDatasetProfileModel(modelDefinition);
		PagedDatasetProfile pagedDatasetProfile = new PagedDatasetProfile();
		pagedDatasetProfile.buildPagedDatasetProfile(datasetProfile);
		return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<PagedDatasetProfile>().status(ApiMessageCode.NO_MESSAGE).payload(pagedDatasetProfile));
	}

	@Transactional
	@RequestMapping(method = RequestMethod.POST, value = {"/datasetprofile/clone/{id}"}, consumes = "application/json", produces = "application/json")
	public ResponseEntity<ResponseItem<eu.eudat.models.data.admin.composite.DatasetProfile>> clone(@PathVariable String id, @ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) {
		eu.eudat.models.data.admin.composite.DatasetProfile datasetprofile = this.datasetProfileManager.getDatasetProfile(id);
		datasetprofile.setLabel(datasetprofile.getLabel() + " new ");
		return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<eu.eudat.models.data.admin.composite.DatasetProfile>().payload(datasetprofile));
	}

	@Transactional
	@RequestMapping(method = RequestMethod.DELETE, value = {"{id}"}, consumes = "application/json", produces = "application/json")
	public @ResponseBody
	ResponseEntity<ResponseItem<DatasetProfile>> inactivate(@PathVariable String id, @ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) {
		try {
			eu.eudat.data.entities.DatasetProfile ret = AdminManager.inactivate(this.getApiContext().getOperationsContext().getDatabaseRepository().getDatasetProfileDao(), this.getApiContext().getOperationsContext().getDatabaseRepository().getDatasetDao(), id);
			return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<eu.eudat.models.data.admin.composite.DatasetProfile>().status(ApiMessageCode.SUCCESS_MESSAGE));
		} catch (DatasetProfileWithDatasetsExeption exception) {
			return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem<eu.eudat.models.data.admin.composite.DatasetProfile>().status(ApiMessageCode.UNSUCCESS_DELETE).message(exception.getMessage()));
		}
	}

	@Transactional
	@RequestMapping(method = RequestMethod.GET, value = {"/getXml/{id}"}, produces = "application/json")
	public ResponseEntity getDatasetProfileXml(@PathVariable String id, @RequestHeader("Content-Type") String contentType, @ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) throws IllegalAccessException, IOException, InstantiationException {
		if (contentType.equals("application/xml")) {
			eu.eudat.data.entities.DatasetProfile profile = this.getApiContext().getOperationsContext().getDatabaseRepository().getDatasetProfileDao().find(UUID.fromString(id));
			eu.eudat.models.data.user.composite.DatasetProfile datasetProfile = userManager.generateDatasetProfileModel(profile);
			datasetProfile.setStatus(profile.getStatus());
			datasetProfile.setDescription(profile.getDescription());
			datasetProfile.setLanguage(profile.getLanguage());
			return this.datasetProfileManager.getDocument(datasetProfile, profile.getLabel());
		} else {
			return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem<eu.eudat.models.data.admin.composite.DatasetProfile>().status(ApiMessageCode.ERROR_MESSAGE).message("NOT AUTHORIZE"));
		}
	}

	@RequestMapping(method = RequestMethod.POST, value = {"/upload", "/upload/{id}"})
	public ResponseEntity<Object> setDatasetProfileXml(@RequestParam("file") MultipartFile file,
													   @PathVariable(value = "id", required = false) String id,
													   @ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) throws Exception {
		eu.eudat.logic.utilities.documents.xml.datasetProfileXml.datasetProfileModel.DatasetProfile datasetProfileModel = this.datasetProfileManager.createDatasetProfileFromXml(file);
		eu.eudat.models.data.admin.composite.DatasetProfile datasetProfileEntity = datasetProfileModel.toAdminCompositeModel(file.getOriginalFilename());
		eu.eudat.data.entities.DatasetProfile modelDefinition;
		if (id == null) {
			modelDefinition = AdminManager.generateViewStyleDefinition(datasetProfileEntity, getApiContext());
			eu.eudat.data.entities.DatasetProfile datasetProfile = this.getApiContext().getOperationsContext().getDatabaseRepository().getDatasetProfileDao().createOrUpdate(modelDefinition);
			UserDatasetProfile userDatasetProfile = new UserDatasetProfile();
			userDatasetProfile.setDatasetProfile(datasetProfile);
			UserInfo userInfo = getApiContext().getOperationsContext().getDatabaseRepository().getUserInfoDao().find(principal.getId());
			userDatasetProfile.setUser(userInfo);
			userDatasetProfile.setRole(0);
			getApiContext().getOperationsContext().getDatabaseRepository().getUserDatasetProfileDao().createOrUpdate(userDatasetProfile);
		} else {
			modelDefinition = datasetProfileManager.createNewVersionDatasetProfile(id, datasetProfileEntity);
		}
		return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<List<eu.eudat.data.entities.DatasetProfile>>()
				.status(ApiMessageCode.SUCCESS_MESSAGE).message(""));
	}

	@RequestMapping(method = RequestMethod.GET, value = {"/getRDACommonStandards"}, produces = "application/json")
	public ResponseEntity getRDACommonStandards(@ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) {
		return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<List<String>>().status(ApiMessageCode.SUCCESS_MESSAGE).payload(configLoader.getRdaProperties()));
	}
}
