@Component
@Configuration
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "nimble")
public class NimbleConfigurationProperties {

    private LanguageID defaultLanguageID = LanguageID.ENGLISH;

    public LanguageID getDefaultLanguageID() {
        return defaultLanguageID;
    }

    public void setDefaultLanguageID(String defaultLanguageID) {
        this.defaultLanguageID = LanguageID.valueOf(defaultLanguageID);
    }

    public enum LanguageID {
        @JsonProperty("en")
        ENGLISH("en"),
        @JsonProperty("es")
        SPANISH("es"),
        @JsonProperty("de")
        GERMAN("de"),
        @JsonProperty("tr")
        TURKISH("tr"),
        @JsonProperty("sv")
        SWEDISH("sv"),
        @JsonProperty("it")
        ITALIAN("it");

        private final String id;

        LanguageID(final String id) {
            this.id = id;
        }

        @Override
        public String toString() {
            return id;
        }

        public static LanguageID fromString(String identifier) {
            return Arrays.stream(LanguageID.values()).filter(l -> l.id.equals(identifier)).findFirst().orElse(null);
        }
    }
}
 @Service
public class AdminService {

    private static final Logger logger = LoggerFactory.getLogger(AdminService.class);

    @Autowired
    private PartyRepository partyRepository;

    @Autowired
    private QualifyingPartyRepository qualifyingPartyRepository;

    @Autowired
    private NegotiationSettingsRepository negotiationSettingsRepository;
    @Autowired
    private UserInvitationRepository userInvitationRepository;

    @Autowired
    private PaymentMeansRepository paymentMeansRepository;

    @Autowired
    private ExecutionContext executionContext;

    @Autowired
    private DeliveryTermsRepository deliveryTermsRepository;

    @Autowired
    private DocumentReferenceRepository documentReferenceRepository;
    @Autowired
    private CertificateRepository certificateRepository;
    private BinaryContentService binaryContentService = new BinaryContentService();
    @Autowired
    private KeycloakAdmin keycloakAdmin;

    @Autowired
    private EmailService emailService;

    @Autowired
    private UaaUserRepository uaaUserRepository;

    @Autowired
    private PersonRepository personRepository;


    @Autowired
    private IdentityService identityService;

    @Autowired
    private IndexingClientController indexingController;

    //    @Cacheable("unverifiedCompanies")
    public List<PartyType> queryCompanies(CompanyState companyState) {
        List<PartyType> resultingCompanies = new ArrayList<>();
        Iterable<PartyType> allParties = partyRepository.findAllByDeletedIsFalse(new Sort(Sort.Direction.ASC, "hjid"));
        for (PartyType company : allParties) {

            // collect roles of company members
            Map<PersonType, List<String>> memberRoles = new HashMap<>();
            for (PersonType companyMember : company.getPerson()) {

                // collect roles
                try {
                    Optional<UaaUser> uaaUser = uaaUserRepository.findByUblPerson(companyMember).stream().findFirst();
                    if (uaaUser.isPresent()) {

                        // avoid invalid usernames (only emails are allowed)
                        if (uaaUser.get().getUsername().contains("@") == false)
                            continue;

                        logger.debug("Fetching roles of user {}", uaaUser.get().getUsername());
                        List<String> roles = new ArrayList<>(identityService.fetchRoles(uaaUser.get()));
                        companyMember.setRole(roles);
                        memberRoles.put(companyMember, roles);
                    }
                } catch (NotFoundException ex) {
                    // ignore
                } catch (Exception ex) {
                    logger.error("Error while fetch roles of user", ex);
                }
            }

            // check if unverified check whether at least on member has proper role
            Set<String> mergedRoles = memberRoles.values().stream().flatMap(Collection::stream).collect(Collectors.toSet());
            if (mergedRoles.isEmpty() == false && mergedRoles.contains(LEGAL_REPRESENTATIVE_ROLE) == false && companyState.equals(CompanyState.UNVERIFIED)) {
                resultingCompanies.add(company);
                continue; // avoid multiple entries in list
            } else if (mergedRoles.contains(LEGAL_REPRESENTATIVE_ROLE) && companyState.equals(CompanyState.VERIFIED)) {
                resultingCompanies.add(company);
                continue; // avoid multiple entries in list
            }
        }

        return resultingCompanies;
    }

    public boolean verifyCompany(Long companyId, String bearer) {
        PartyType company = partyRepository.findByHjid(companyId).stream().findFirst().orElseThrow(ControllerUtils.CompanyNotFoundException::new);

        List<PersonType> companyMembers = company.getPerson();

        for (PersonType companyMember : companyMembers) {
            UaaUser uaaUser = uaaUserRepository.findByUblPerson(companyMember).stream().findFirst().orElseThrow(ControllerUtils.PersonNotFoundException::new);
            List<String> roles = new ArrayList<>(keycloakAdmin.getUserRoles(uaaUser.getExternalID()));
            if (roles.contains(INITIAL_REPRESENTATIVE_ROLE)) {
                keycloakAdmin.addRole(uaaUser.getExternalID(), LEGAL_REPRESENTATIVE_ROLE);

                // send email notification
                String email = companyMember.getContact().getElectronicMail();
                //temp. fix for verifying companies created by efactory federated users
                if(email != null && !email.equals("")){
                    emailService.notifyVerifiedCompany(email, companyMember, company,executionContext.getLanguageId());

                }

                //indexing the verified status of the company
                eu.nimble.service.model.solr.party.PartyType party =  indexingController.getNimbleIndexClient().getParty(company.getHjid().toString(),bearer);
                if(party != null) {
                    party.setVerified(true);
                    List<IndexingClient> indexingClients = indexingController.getClients();
                    for (IndexingClient indexingClient : indexingClients) {
                        indexingClient.setParty(party, bearer);
                    }
                }
                return true;
            }
        }

        return false;
    }

    public List<PartyType> sortCompanies(List<PartyType> partyList, String sortBy, String orderBy) {
        Comparator<PartyType> p;

        if (sortBy.equals(GlobalConstants.PARTY_NAME_STRING)) {
            if(GlobalConstants.DESCENDING_STRING.equals(orderBy)){
                p = (p1, p2) ->
                        p2.getPartyName().get(0).getName().getValue().toLowerCase().compareTo(p1.getPartyName().get(0).getName().getValue().toLowerCase());
            }else {
                p = (p1, p2) ->
                        p1.getPartyName().get(0).getName().getValue().toLowerCase().compareTo(p2.getPartyName().get(0).getName().getValue().toLowerCase());
            }
            partyList.sort(p);
        }
        return partyList;
    }

    public boolean revertCompany(Long companyId, String bearer) throws Exception {

        // query company
        PartyType company = partyRepository.findByHjid(companyId).stream().findFirst().orElseThrow(ControllerUtils.CompanyNotFoundException::new);

        if (identityService.hasAnyRole(bearer, OAuthClient.Role.PLATFORM_MANAGER) == true){
            // revert associated company members
            for (PersonType member : company.getPerson()) {
                Long memberHjid = member.getHjid();
                revertPerson(memberHjid,bearer,true);
            }

            //set deleted flag fr the party
            company.setDeleted(false);

            //update the party
            partyRepository.save(company);
            return true;
        }

        else {
            return false;
        }

    }

    public boolean deleteCompany(Long companyId, String bearer, Long userId) throws Exception {

        // query company
        PartyType company = partyRepository.findByHjid(companyId).stream().findFirst().orElseThrow(ControllerUtils.CompanyNotFoundException::new);

        boolean isUserInCompany = false;

        for (PersonType member : company.getPerson()) {
            Long memberHjid = member.getHjid();
            if(memberHjid.equals(userId)){
                isUserInCompany = true;
                break;
            }
        }

        if (identityService.hasAnyRole(bearer, OAuthClient.Role.PLATFORM_MANAGER) == true){
            isUserInCompany = true;
        }

        if(isUserInCompany){
            // delete associated company members
            for (PersonType member : company.getPerson()) {
                Long memberHjid = member.getHjid();
                deletePerson(memberHjid,bearer,true);
            }

            //set deleted flag fr the party
            company.setDeleted(true);

            //update the party
            partyRepository.save(company);

            //update the index by removing the company
            List<IndexingClient> indexingClients = indexingController.getClients();
            for (IndexingClient indexingClient : indexingClients) {
                indexingClient.deleteParty(company.getHjid().toString(), bearer);
            }
            return true;
        }else {
            return false;
        }

//        // delete negotiation settings
//        negotiationSettingsRepository.deleteByCompany(company);
//
//        // delete trading preferences
//        if (company.getPurchaseTerms() != null) {
//            deliveryTermsRepository.delete(company.getPurchaseTerms().getDeliveryTerms());
//            paymentMeansRepository.delete(company.getPurchaseTerms().getPaymentMeans());
//        }
//        if (company.getSalesTerms() != null) {
//            deliveryTermsRepository.delete(company.getSalesTerms().getDeliveryTerms());
//            paymentMeansRepository.delete(company.getSalesTerms().getPaymentMeans());
//        }
//
//        try {
//            // delete for legacy schema
//            deliveryTermsRepository.deleteByPartyID(companyId);
//            paymentMeansRepository.deleteByPartyID(companyId);
//        } catch (InvalidDataAccessResourceUsageException ex) {
//            // ignored
//        }
//
//        return partyRepository.deleteByHjid(companyId);

    }

    // this method deletes the party represented by the given id
    public void deleteCompanyPermanently(Long companyId) {
        // query company
        PartyType company = partyRepository.findByHjid(companyId).stream().findFirst().orElseThrow(ControllerUtils.CompanyNotFoundException::new);
        // retrieve qualifying party
        QualifyingPartyType qualifyingParty = qualifyingPartyRepository.findByParty(company).stream().findFirst().get();
        // retrieve negotiation settings
        NegotiationSettings negotiationSettings = negotiationSettingsRepository.findOneByCompany(company);
        if(negotiationSettings != null){
            // delete negotiation settings
            negotiationSettingsRepository.delete(negotiationSettings);
        }

        // delete qualifying party
        qualifyingPartyRepository.delete(qualifyingParty);

        // delete trading preferences
        if (company.getPurchaseTerms() != null) {
            deliveryTermsRepository.delete(company.getPurchaseTerms().getDeliveryTerms());
            paymentMeansRepository.delete(company.getPurchaseTerms().getPaymentMeans());
        }
        if (company.getSalesTerms() != null) {
            deliveryTermsRepository.delete(company.getSalesTerms().getDeliveryTerms());
            paymentMeansRepository.delete(company.getSalesTerms().getPaymentMeans());
        }

        // delete document references
        if(company.getDocumentReference() != null){
            for (DocumentReferenceType documentReference : company.getDocumentReference()) {
                if(documentReference.getAttachment() != null && documentReference.getAttachment().getEmbeddedDocumentBinaryObject() != null && documentReference.getAttachment().getEmbeddedDocumentBinaryObject().getUri() != null){
                    String uri = documentReference.getAttachment().getEmbeddedDocumentBinaryObject().getUri();
                    binaryContentService.deleteContentIdentity(uri);
                }

                documentReferenceRepository.delete(documentReference);
            }
        }

        // delete company certificates
        if(company.getCertificate() != null){
            for (CertificateType certificate : company.getCertificate()) {

                if(certificate.getDocumentReference() != null && certificate.getDocumentReference().get(0).getAttachment() != null && certificate.getDocumentReference().get(0).getAttachment().getEmbeddedDocumentBinaryObject() != null && certificate.getDocumentReference().get(0).getAttachment().getEmbeddedDocumentBinaryObject().getUri() != null){
                    String uri = certificate.getDocumentReference().get(0).getAttachment().getEmbeddedDocumentBinaryObject().getUri();
                    binaryContentService.deleteContentIdentity(uri);
                }

                certificateRepository.delete(certificate);
            }

        }

        // delete party
        partyRepository.delete(company);
    }

    public boolean deletePerson(Long personHjid, String bearer , boolean isCompanyDelete) throws Exception {

        OpenIdConnectUserDetails oidUser = identityService.getUserDetails(bearer);
        String keyCloackuid = oidUser.getUserId();

        // query person
        PersonType person = personRepository.findByHjid(personHjid).stream().findFirst().orElseThrow(ControllerUtils.PersonNotFoundException::new);
        List<UaaUser> potentialUser = uaaUserRepository.findByUblPerson(person);
        UaaUser uaaUser = potentialUser.stream().findFirst().orElseThrow(() -> new Exception("Invalid user mapping"));
        String keyCloakId = uaaUser.getExternalID();

        if(keyCloakId.equals(keyCloackuid) || identityService.hasAnyRole(bearer, OAuthClient.Role.PLATFORM_MANAGER) == true
                || isCompanyDelete){
            //set delete flag for the person
            person.setDeleted(true);
            keycloakAdmin.addRole(keyCloakId, KeycloakAdmin.NIMBLE_DELETED_USER);
            //save deleted person
            personRepository.save(person);
            return true;
        }else{
            return false;
        }
    }

    public boolean revertPerson(Long personHjid, String bearer , boolean isCompanyReverted) throws Exception {

        OpenIdConnectUserDetails oidUser = identityService.getUserDetails(bearer);
        String keyCloackuid = oidUser.getUserId();

        // query person
        PersonType person = personRepository.findByHjid(personHjid).stream().findFirst().orElseThrow(ControllerUtils.PersonNotFoundException::new);
        List<UaaUser> potentialUser = uaaUserRepository.findByUblPerson(person);
        UaaUser uaaUser = potentialUser.stream().findFirst().orElseThrow(() -> new Exception("Invalid user mapping"));
        String keyCloakId = uaaUser.getExternalID();

        if(keyCloakId.equals(keyCloackuid) || identityService.hasAnyRole(bearer, OAuthClient.Role.PLATFORM_MANAGER) == true
                || isCompanyReverted){
            //set delete flag for the person
            person.setDeleted(false);
            keycloakAdmin.removeRole(keyCloakId, KeycloakAdmin.NIMBLE_DELETED_USER);
            //save person
            personRepository.save(person);
            return true;
        }else{
            return false;
        }
    }

    public enum CompanyState {
        VERIFIED, UNVERIFIED
    }
} public class ControllerUtils {
    @ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "company not found")
    public static class CompanyNotFoundException extends RuntimeException {
    }

    @ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "person not found")
    public static class PersonNotFoundException extends RuntimeException {
    }

    @ResponseStatus(code = HttpStatus.NOT_ACCEPTABLE, reason = "File size exceeds limit")
    public static class FileTooLargeException extends RuntimeException {
    }

    @ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "document not found")
    public static class DocumentNotFoundException extends RuntimeException {
        DocumentNotFoundException() {
        }

        DocumentNotFoundException(String message) {
            super(message);
        }
    }

    @ResponseStatus(code = HttpStatus.FORBIDDEN, reason = "unauthorised access")
    public static class UnauthorisedAccess extends RuntimeException {
    }
} public interface PartyRepository extends PagingAndSortingRepository<PartyType, Long>, JpaSpecificationExecutor<PartyType> {

    @Transactional
    Iterable<PartyType> findAll(Sort sort);

    @Transactional
    Iterable<PartyType> findAllByDeletedIsFalse(Sort sort);

    @Transactional
    Page<PartyType> findAll(Pageable pageable);

    List<PartyType> findByHjid(Long hijd);

    List<PartyType> findByPerson(PersonType person);

    @Modifying
    @Transactional
    long deleteByHjid(Long hijd);

    @Query(value = "select hjid from document_reference_type where document_reference_party_typ_0 = ?1 and document_type = ?2", nativeQuery = true)
    List<BigInteger> findDocumentIds(Long companyId, String documentType);

    @Query(value = "FROM PartyType WHERE stripeAccountId = ?1")
    List<PartyType> findByStripeAccountId(String accountId);

    @Query(value = "FROM PartyType WHERE productPublishSubscription is not null")
    List<PartyType> findAllWithSubscriptions();

    @Query(value = "select pt.hjid from document_reference_type drt,trading_preferences tp,party_type pt where drt.id = ?1 and tp.hjid = drt.document_reference_trading_p_0 and pt.sales_terms_party_type_hjid = tp .hjid",nativeQuery = true)
    List<BigInteger> findByTermsAndConditionsDocumentReferenceId(String id);
} public interface PersonRepository extends PagingAndSortingRepository<PersonType, Long> {
    List<PersonType> findByHjid(Long hijd);
} public interface QualifyingPartyRepository extends PagingAndSortingRepository<QualifyingPartyType, Long> {

    List<QualifyingPartyType> findByHjid(Long hijd);

    List<QualifyingPartyType> findByParty(PartyType party);

    @Modifying
    @Transactional
    long deleteByParty(PartyType party);
} @Service
public class IdentityService {

    private static final Logger logger = LoggerFactory.getLogger(IdentityService.class);

    @Autowired
    private UaaUserRepository uaaUserRepository;

    @Autowired
    private PartyRepository partyRepository;

    @Autowired
    private KeycloakAdmin keycloakAdmin;

    public UaaUser getUserfromBearer(String bearer) throws IOException {
        OpenIdConnectUserDetails userDetails = getUserDetails(bearer);
        return uaaUserRepository.findByExternalID(userDetails.getUserId());
    }

    public OpenIdConnectUserDetails getUserDetails(String bearer) throws IOException {
        return OpenIdConnectUserDetails.fromBearer(bearer);
    }

    /**
     * Checks if the bearer contains at least one of the given roles.
     *
     * @param bearer Token containing roles
     * @param roles  Roles to check
     * @return True if at least one matching role was found.
     * @throws IOException if roles could not be extracted from token
     */
    public boolean hasAnyRole(String bearer, OAuthClient.Role... roles) throws IOException {
        OpenIdConnectUserDetails details = getUserDetails(bearer);
        return Arrays.stream(roles).anyMatch(r -> details.hasRole(r.toString()));
    }

    public Optional<PartyType> getCompanyOfUser(UaaUser uaaUser) {
        if (uaaUser == null)
            return Optional.empty();
        return partyRepository.findByPerson(uaaUser.getUBLPerson()).stream().findFirst();
    }

    @SuppressWarnings("SimplifiableIfStatement")
    public boolean inSameCompany(UaaUser userOne, UaaUser userTwo) {

        if (userOne == null || userTwo == null)
            return false;

        Optional<PartyType> requestingCompany = getCompanyOfUser(userOne);
        Optional<PartyType> targetCompany = getCompanyOfUser(userTwo);
        if ((requestingCompany.isPresent() && targetCompany.isPresent()) == false) // check if companies exist
            return false;

        return requestingCompany.get().getHjid().equals(targetCompany.get().getHjid());
    }

    public Set<String> fetchRoles(PersonType personType) {
        try {
            UaaUser uaaUser = uaaUserRepository.findByUblPerson(personType).stream().findFirst().orElseThrow(NotFoundException::new);
            return fetchRoles(uaaUser);
        } catch (Exception exception) {
            logger.error("Error while fetching roles for person", exception);
        }
        return Collections.emptySet();
    }

    public Set<String> fetchRoles(UaaUser user) {

        Set<String> roles = new HashSet<>();
        try {
            // collect roles
            logger.debug("Fetching roles of user {}", user.getUsername());
            roles = keycloakAdmin.getUserRoles(user.getExternalID(), KeycloakAdmin.NON_NIMBLE_ROLES);
        } catch (Exception ex) {
            logger.error("Error while fetch roles of user", ex);
        }

        return roles;
    }

    public void enrichWithRoles(PersonType person) {
        Set<String> roles = fetchRoles(person);
        person.getRole().clear();
        person.getRole().addAll(roles);
    }

    public void enrichWithRoles(PartyType party) {
        party.getPerson().forEach(this::enrichWithRoles);
    }

    public static Double computeDetailsCompleteness(CompanyDetails companyDetails) {

        List<Double> completenessWeights = new ArrayList<>();
        completenessWeights.add(companyDetails.getLegalName().isEmpty() == false ? 1.0 : 0.0);
        completenessWeights.add(StringUtils.isNotEmpty(companyDetails.getVatNumber()) ? 1.0 : 0.0);
        completenessWeights.add(StringUtils.isNotEmpty(companyDetails.getBusinessType()) ? 1.0 : 0.0);
        completenessWeights.add(companyDetails.getIndustrySectors() != null && companyDetails.getIndustrySectors().size() > 0 ? 1.0 : 0.0);

        Address address = companyDetails.getAddress();
        if (address != null) {
            completenessWeights.add(StringUtils.isNotEmpty(address.getStreetName()) ? 1.0 : 0.0);
            completenessWeights.add(StringUtils.isNotEmpty(address.getCityName()) ? 1.0 : 0.0);
            completenessWeights.add(StringUtils.isNotEmpty(address.getPostalCode()) ? 1.0 : 0.0);
            completenessWeights.add(StringUtils.isNotEmpty(address.getCountry()) ? 1.0 : 0.0);
        }
        return completenessWeights.stream().mapToDouble(d -> d).average().orElse(0.0);
    }

    public static Double computeDescriptionCompleteness(CompanyDescription companyDescription) {
        List<Double> completenessWeights = new ArrayList<>();
        completenessWeights.add(!companyDescription.getCompanyStatement().isEmpty() ? 1.0 : 0.0);
        completenessWeights.add(StringUtils.isNotEmpty(companyDescription.getWebsite()) ? 1.0 : 0.0);
        completenessWeights.add(companyDescription.getLogoImageId() != null ? 1.0 : 0.0);
        completenessWeights.add(companyDescription.getSocialMediaList() != null && companyDescription.getSocialMediaList().size() > 0 ? 1.0 : 0.0);
        return completenessWeights.stream().mapToDouble(d -> d).average().orElse(0.0);
    }

    public static Double computeDeliveryAddressCompleteness(PartyType party) {
        List<Double> completenessWeights = new ArrayList<>();
        TradingPreferences tradingPreferences = party.getPurchaseTerms();
        if (tradingPreferences.getDeliveryTerms().size() == 0 || null == tradingPreferences.getDeliveryTerms().get(0).getDeliveryLocation()) {
            return 0.0;
        } else {
            LocationType locationType = tradingPreferences.getDeliveryTerms().get(0).getDeliveryLocation();
            completenessWeights.add(locationType.getAddress().getStreetName() != null ? 1.0 : 0.0);
            completenessWeights.add(locationType.getAddress().getBuildingNumber() != null ? 1.0 : 0.0);
            completenessWeights.add(locationType.getAddress().getCityName() != null ? 1.0 : 0.0);
            completenessWeights.add(locationType.getAddress().getRegion() != null ? 1.0 : 0.0);
            completenessWeights.add(locationType.getAddress().getPostalZone() != null ? 1.0 : 0.0);
            completenessWeights.add(locationType.getAddress().getCountry() != null ? 1.0 : 0.0);
            return completenessWeights.stream().mapToDouble(d -> d).average().orElse(0.0);
        }
    }

    public static Double computeCertificateCompleteness(PartyType party) {
        List<Double> completenessWeights = new ArrayList<>();
        completenessWeights.add(party.getCertificate() != null && party.getCertificate().size() > 0 ? 1.0 : 0.0);
        return completenessWeights.stream().mapToDouble(d -> d).average().orElse(0.0);
    }

    public static Double computeTradeCompleteness(NegotiationSettings negotiationSettings) {
        List<Double> completenessWeights = new ArrayList<>();
        try {
            completenessWeights.add(negotiationSettings.getPaymentMeans() != null && negotiationSettings.getPaymentMeans().size() > 0 ? 1.0 : 0.0);
            completenessWeights.add(negotiationSettings.getPaymentTerms() != null && negotiationSettings.getPaymentTerms().size() > 0 ? 1.0 : 0.0);
            completenessWeights.add(negotiationSettings.getIncoterms() != null && negotiationSettings.getIncoterms().size() > 0 ? 1.0 : 0.0);
        }catch (Exception e){
            logger.error("Exception occurred while computing the tradCompletenessSocre", e);
        }
        return completenessWeights.stream().mapToDouble(d -> d).average().orElse(0.0);
    }

    public static Double computeAdditionalDataCompleteness(PartyType party, CompanyTradeDetails tradeDetails, CompanyDescription companyDescription) {
        List<Double> completenessWeights = new ArrayList<>();
        completenessWeights.add(companyDescription.getEvents() != null && companyDescription.getEvents().size() > 0 ? 1.0 : 0.0);
        completenessWeights.add(companyDescription.getCompanyPhotoList() != null && companyDescription.getCompanyPhotoList().size() > 0 ? 1.0 : 0.0);
        completenessWeights.add(companyDescription.getExternalResources() != null && companyDescription.getExternalResources().size() > 0 ? 1.0 : 0.0);
        return completenessWeights.stream().mapToDouble(d -> d).average().orElse(0.0);
    }
} @Service
public class OAuthClient {

    @Autowired
    private OAuthClientConfig config;

    @Autowired
    private OAuth2ClientContext oauth2Context;

    @Bean
    public TokenStore tokenStore() {
        return new InMemoryTokenStore();
    }

    public OAuth2AccessToken getToken(String username, String password) {

        // build resources
        ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails();
        resourceDetails.setClientId(config.getCliendId());
        resourceDetails.setClientSecret(config.getCliendSecret());
        resourceDetails.setAccessTokenUri(config.getAccessTokenUri());
        resourceDetails.setUsername(username);
        resourceDetails.setPassword(password);

        // build provider
        ResourceOwnerPasswordAccessTokenProvider accessTokenProvider = new ResourceOwnerPasswordAccessTokenProvider();
        AccessTokenRequest accessTokenRequest = oauth2Context.getAccessTokenRequest();

        // fetch token
        OAuth2AccessToken accessToken = accessTokenProvider.obtainAccessToken(resourceDetails, accessTokenRequest);
        return accessToken;
    }

    public OAuth2AccessToken refreshToken(String refreshToken) {

        // build resources
        ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails();
        resourceDetails.setClientId(config.getCliendId());
        resourceDetails.setClientSecret(config.getCliendSecret());
        resourceDetails.setAccessTokenUri(config.getAccessTokenUri());

        // build provider
        AccessTokenRequest accessTokenRequest = oauth2Context.getAccessTokenRequest();
        ResourceOwnerPasswordAccessTokenProvider accessTokenProvider = new ResourceOwnerPasswordAccessTokenProvider();

        // perform refresh
        OAuth2AccessToken newAccessToken = accessTokenProvider.refreshAccessToken(resourceDetails, () -> refreshToken, accessTokenRequest);
        return newAccessToken;
    }

    public enum Role {
        NIMBLE_USER("nimble_user"),
        INITIAL_REPRESENTATIVE("initial_representative"),
        LEGAL_REPRESENTATIVE("legal_representative"),
        PLATFORM_MANAGER("platform_manager"),
        COMPANY_ADMIN("company_admin"),
        PUBLISHER("publisher"),
        NIMBLE_DELETED_USER("nimble_deleted_user"),
        EXTERNAL_REPRESENTATIVE("external_representative"),
        MONITOR("monitor"),
        EFACTORY_USER("eFactoryUser");

        private final String role;

        Role(final String role) {
            this.role = role;
        }

        @Override
        public String toString() {
            return role;
        }
    }
} @SuppressWarnings("WeakerAccess")
public class UblAdapter {

    public static final String VAT_TAX_TYPE_CODE = "VAT";
    public static final String DOCUMENT_TYPE_COMPANY_PHOTO = "CompanyPhoto";
    public static final String DOCUMENT_TYPE_COMPANY_LOGO = "CompanyLogo";
    public static final String DOCUMENT_TYPE_EXTERNAL_RESOURCE = "ExternalResource";

    public static CompanySettings adaptCompanySettings(PartyType party, QualifyingPartyType qualifyingParty) {
        CompanySettings settings = new CompanySettings();

        CompanyDetails companyDetails = adaptCompanyDetails(party, qualifyingParty);
        CompanyDescription companyDescription = adaptCompanyDescription(party, qualifyingParty);

        settings.setCompanyID(UblAdapter.adaptPartyIdentifier(party));
        settings.setDetails(companyDetails);
        settings.setDescription(companyDescription);

        // set payment means
        if (party.getPurchaseTerms() != null) {
            List<PaymentMeans> paymentMeans = party.getPurchaseTerms().getPaymentMeans().stream()
                    .map(UblAdapter::adaptPaymentMeans)
                    .collect(Collectors.toList());
            settings.getTradeDetails().setPaymentMeans(paymentMeans);
        }

        // set delivery terms
        if (party.getPurchaseTerms() != null) {
            List<DeliveryTerms> deliveryTerms = party.getPurchaseTerms().getDeliveryTerms().stream()
                    .map(UblAdapter::adaptDeliveryTerms)
                    .collect(Collectors.toList());
            settings.getTradeDetails().getDeliveryTerms().clear();
            settings.getTradeDetails().getDeliveryTerms().addAll(deliveryTerms);
        }

        if (party.getPpapCompatibilityLevel() != null)
            settings.getTradeDetails().setPpapCompatibilityLevel(party.getPpapCompatibilityLevel().intValue());
        if (party.getStripeAccountId() != null)
            settings.getTradeDetails().setStripeAccountId(party.getStripeAccountId());

        // set certificates
        settings.setCertificates(UblAdapter.adaptCertificates(party.getCertificate()));

        // set preferred product categories
        Set<String> preferredProductCategories = party.getPreferredItemClassificationCode().stream()
                .map(CodeType::getValue)
                .collect(Collectors.toSet());
        settings.setPreferredProductCategories(preferredProductCategories);

        // set product subscriptions
        if(party.getProductPublishSubscription() != null){
            settings.setSubscribedProductCategories(party.getProductPublishSubscription().getCategoryCode());
            Set<String> subscribedCompanyIds = new HashSet<>(party.getProductPublishSubscription().getCompanyID());
            settings.setSubscribedCompanyIds(subscribedCompanyIds);
        }

        // set terms and conditions
        if(party.getSalesTerms() != null){
            settings.setTermsAndConditions(party.getSalesTerms().getDocumentReference());
        }
        // set recently used product categories
        Set<String> recentlyUsedProductCategories = party.getMostRecentItemsClassificationCode().stream()
                .map(CodeType::getValue)
                .collect(Collectors.toSet());
        settings.getRecentlyUsedProductCategories().clear();
        settings.getRecentlyUsedProductCategories().addAll(recentlyUsedProductCategories);

        return settings;
    }

    public static Address adaptAddress(AddressType ublAddress) {

        if (ublAddress == null)
            return new Address();

        Address dtoAddress = new Address();
        dtoAddress.setStreetName(ublAddress.getStreetName());
        dtoAddress.setBuildingNumber(ublAddress.getBuildingNumber());
        dtoAddress.setCityName(ublAddress.getCityName());
        dtoAddress.setPostalCode(ublAddress.getPostalZone());
        dtoAddress.setDistrict(ublAddress.getDistrict());
        dtoAddress.setRegion(ublAddress.getRegion());
        if (ublAddress.getCoordinate() != null){
            dtoAddress.setLocationLatitude(ublAddress.getCoordinate().getLatitude());
            dtoAddress.setLocationLongitude(ublAddress.getCoordinate().getLongitude());
        }
        if (ublAddress.getCountry() != null && ublAddress.getCountry().getIdentificationCode() != null)
            dtoAddress.setCountry(ublAddress.getCountry().getIdentificationCode().getValue());
        return dtoAddress;
    }

    public static AddressType adaptAddress(Address dtoAddress) {

        if (dtoAddress == null)
            return new AddressType();

        AddressType ublAddress = new AddressType();
        ublAddress.setStreetName(dtoAddress.getStreetName());
        ublAddress.setBuildingNumber(dtoAddress.getBuildingNumber());
        ublAddress.setCityName(dtoAddress.getCityName());
        ublAddress.setPostalZone(dtoAddress.getPostalCode());
        ublAddress.setDistrict(dtoAddress.getDistrict());
        ublAddress.setRegion(dtoAddress.getRegion());

        CoordinateType coordinateType = new CoordinateType();
        coordinateType.setLatitude(dtoAddress.getLocationLatitude());
        coordinateType.setLongitude(dtoAddress.getLocationLongitude());
        ublAddress.setCoordinate(coordinateType);

        CountryType country = new CountryType();
        CodeType codeType = new CodeType();
        codeType.setValue(dtoAddress.getCountry());
        country.setIdentificationCode(codeType);
        ublAddress.setCountry(country);

        return ublAddress;
    }

    public static CompanyDetails adaptCompanyDetails(PartyType party, QualifyingPartyType qualifyingParty) {

        CompanyDetails companyDetails = new CompanyDetails();

        companyDetails.setBrandName(UblAdapter.adaptTextType(party.getBrandName()));
        companyDetails.setLegalName(UblAdapter.adaptPartyNames(party.getPartyName()));
        companyDetails.setVatNumber(party.getPartyTaxScheme()
                .stream()
                .filter(scheme -> scheme != null && scheme.getTaxScheme() != null && scheme.getTaxScheme().getTaxTypeCode() != null)
                .filter(scheme -> VAT_TAX_TYPE_CODE.equals(scheme.getTaxScheme().getTaxTypeCode().getName()))
                .filter(scheme -> scheme.getTaxScheme().getTaxTypeCode().getValue() != null)
                .map(scheme -> scheme.getTaxScheme().getTaxTypeCode().getValue())
                .findFirst().orElse(null));
        companyDetails.setAddress(adaptAddress(party.getPostalAddress()));
        companyDetails.setProcessIds(new ArrayList<>(party.getProcessID()));
        Map< NimbleConfigurationProperties.LanguageID, String> industrySectors = party.getIndustrySector().stream()
                .collect(Collectors.toMap(t -> NimbleConfigurationProperties.LanguageID.fromString(t.getLanguageID()), t -> t.getValue()));
        companyDetails.setIndustrySectors(industrySectors);
        if (party.getIndustryClassificationCode() != null)
            companyDetails.setBusinessType(party.getIndustryClassificationCode().getValue());

        if (qualifyingParty != null) {
            companyDetails.setVerificationInformation(qualifyingParty.getBusinessIdentityEvidenceID());
            companyDetails.setBusinessKeywords(adaptTextType(qualifyingParty.getBusinessClassificationScheme().getDescription()));
            if (qualifyingParty.getOperatingYearsQuantity() != null && qualifyingParty.getOperatingYearsQuantity().getValue() != null)
                companyDetails.setYearOfCompanyRegistration(qualifyingParty.getOperatingYearsQuantity().getValue().intValue());
        }

        companyDetails.setPerson(party.getPerson());
        return companyDetails;
    }

    public static CompanyDescription adaptCompanyDescription(PartyType party, QualifyingPartyType qualifyingPartyType) {

        CompanyDescription companyDescription = new CompanyDescription();
        companyDescription.setWebsite(party.getWebsiteURI());
        if (party.getContact() != null && party.getContact().getOtherCommunication() != null) {
            companyDescription.setSocialMediaList(party.getContact().getOtherCommunication()
                    .stream()
                    .map( c -> c.getValue().getValue())
                    .collect(Collectors.toList()));
        }

        // photos
        if (party.getDocumentReference() != null) {
            List<String> companyPhotoIds = party.getDocumentReference().stream()
                    .filter(dr -> DOCUMENT_TYPE_COMPANY_PHOTO.equals(dr.getDocumentType()))
                    .map(dr -> dr.getHjid().toString())
                    .collect(Collectors.toList());
            companyDescription.setCompanyPhotoList(companyPhotoIds);

            // company logo
            String logoImageId = party.getDocumentReference().stream()
                    .filter(dr -> DOCUMENT_TYPE_COMPANY_LOGO.equals(dr.getDocumentType()))
                    .map(dr -> dr.getHjid().toString())
                    .findFirst()
                    .orElse(null);
            companyDescription.setLogoImageId(logoImageId);

            // external resources
            List<String> externalResources = UblAdapter.adaptExternalResourcesType(party.getDocumentReference());
            companyDescription.setExternalResources(externalResources);
        }

        Map<NimbleConfigurationProperties.LanguageID, String> companyDescriptionMap = new HashMap<>();

        if (qualifyingPartyType != null) {
            if (qualifyingPartyType.getEconomicOperatorRole() != null && qualifyingPartyType.getEconomicOperatorRole().getRoleDescription().isEmpty() == false){
                qualifyingPartyType.getEconomicOperatorRole().getRoleDescription().forEach(desc -> {
                    companyDescriptionMap.put(NimbleConfigurationProperties.LanguageID.fromString(desc.getLanguageID()), desc.getValue());
                });
                companyDescription.setCompanyStatement(companyDescriptionMap);
            }

            // adapt events
            if (qualifyingPartyType.getEvent() != null) {
                List<CompanyEvent> events = qualifyingPartyType.getEvent().stream()
                        .map(UblAdapter::adaptEvent)
                        .collect(Collectors.toList());
                companyDescription.getEvents().clear();
                companyDescription.getEvents().addAll(events);
            }
        }

        return companyDescription;
    }

    public static DeliveryTerms adaptDeliveryTerms(DeliveryTermsType ublDeliveryTerms) {

        if (ublDeliveryTerms == null)
            return new DeliveryTerms();

        DeliveryTerms dtoDeliveryTerms = new DeliveryTerms();
        dtoDeliveryTerms.setSpecialTerms(UblAdapter.adaptTextType(ublDeliveryTerms.getSpecialTerms()));

        // adapt address
        if (ublDeliveryTerms.getDeliveryLocation() != null)
            dtoDeliveryTerms.setDeliveryAddress(adaptAddress(ublDeliveryTerms.getDeliveryLocation().getAddress()));

        // adapt delivery period
        if (ublDeliveryTerms.getEstimatedDeliveryPeriod() != null)
            if (ublDeliveryTerms.getEstimatedDeliveryPeriod().getDurationMeasure() != null)
                dtoDeliveryTerms.setEstimatedDeliveryTime(ublDeliveryTerms.getEstimatedDeliveryPeriod().getDurationMeasure().getValue().intValue());

        return dtoDeliveryTerms;
    }

    public static DeliveryTermsType adaptDeliveryTerms(DeliveryTerms dtoDeliveryTerms) {

        if (dtoDeliveryTerms == null)
            return new DeliveryTermsType();

        DeliveryTermsType ublDeliveryTerms = new DeliveryTermsType();
        ublDeliveryTerms.setSpecialTerms(adaptTextType(dtoDeliveryTerms.getSpecialTerms()));

        // adapt address
        AddressType deliveryAddress = adaptAddress(dtoDeliveryTerms.getDeliveryAddress());
        LocationType deliveryLocation = new LocationType();
        deliveryLocation.setAddress(deliveryAddress);
        ublDeliveryTerms.setDeliveryLocation(deliveryLocation);

        // adapt delivery time
        if (dtoDeliveryTerms.getEstimatedDeliveryTime() != null) {
            QuantityType deliveryTimeQuantity = new QuantityType();
            deliveryTimeQuantity.setValue(new BigDecimal(dtoDeliveryTerms.getEstimatedDeliveryTime()));
            deliveryTimeQuantity.setUnitCode("Days");
            PeriodType deliveryPeriod = new PeriodType();
            deliveryPeriod.setDurationMeasure(deliveryTimeQuantity);
            ublDeliveryTerms.setEstimatedDeliveryPeriod(deliveryPeriod);
        }

        return ublDeliveryTerms;
    }

    public static PaymentMeansType adaptPaymentMeans(PaymentMeans dtoPaymentMeans) {

        if (dtoPaymentMeans == null)
            return new PaymentMeansType();

        PaymentMeansType ublPaymentMeans = new PaymentMeansType();
        ublPaymentMeans.setInstructionNote(UblAdapter.adaptTextType(dtoPaymentMeans.getInstructionNote()));
        return ublPaymentMeans;
    }

    public static PaymentMeans adaptPaymentMeans(PaymentMeansType ublPaymentMeans) {

        if (ublPaymentMeans == null)
            return new PaymentMeans();

        PaymentMeans dtoPaymentMeans = new PaymentMeans();
        dtoPaymentMeans.setInstructionNote(UblAdapter.adaptTextType(ublPaymentMeans.getInstructionNote()));
        return dtoPaymentMeans;
    }

    public static PartyIdentificationType adaptPartyIdentifier(String identifier) {
        PartyIdentificationType partyIdentificationType = new PartyIdentificationType();
        partyIdentificationType.setID(identifier);
        return partyIdentificationType;
    }

    public static String adaptPartyIdentifier(PartyIdentificationType partyIdentification) {
        return partyIdentification.getID();
    }

    public static String adaptPartyIdentifier(PartyType partyType) {
        return adaptPartyIdentifier(Objects.requireNonNull(partyType.getPartyIdentification().stream().findFirst().orElse(null)));
    }

    public static FrontEndUser adaptUser(UaaUser uaaUser, List<PartyType> companies) {
        FrontEndUser frontEndUser = new FrontEndUser();
        frontEndUser.setUsername(uaaUser.getUsername());
        PersonType ublPerson = uaaUser.getUBLPerson();
        frontEndUser.setFirstname(ublPerson.getFirstName());
        frontEndUser.setLastname(ublPerson.getFamilyName());
        frontEndUser.setShowWelcomeInfo(uaaUser.getShowWelcomeInfo());
        if (ublPerson.getContact() != null)
            frontEndUser.setEmail(ublPerson.getContact().getElectronicMail());
        frontEndUser.setUserID(ublPerson.getHjid());

        // set company ids
        if (companies != null && companies.isEmpty() == false) {
            PartyType company = companies.get(0);
            frontEndUser.setCompanyID(company.getHjid().toString());
            frontEndUser.setCompanyName(adaptPartyNames(company.getPartyName()));
        }

        return frontEndUser;
    }

    public static PersonType adaptPerson(FrontEndUser frontEndUser) {
        PersonType person = new PersonType();
        person.setFirstName(frontEndUser.getFirstname());
        person.setFamilyName(frontEndUser.getLastname());
        person.setBirthplaceName(frontEndUser.getPlaceOBirth());
        ContactType contact = new ContactType();
        contact.setElectronicMail(frontEndUser.getEmail());
        contact.setTelephone(frontEndUser.getPhoneNumber());
        person.setContact(contact);
        return person;
    }

    public static PartyType adaptCompanyRegistration(CompanyRegistration registration, PersonType representative) {
        return adaptCompanySettings(registration.getSettings(), representative, null);
    }

    public static PartyType adaptCompanySettings(CompanySettings settings, PersonType representative, PartyType companyToChange) {

        if (companyToChange == null)
            companyToChange = new PartyType();

        // brand name
        List<TextType> brandNames = UblAdapter.adaptTextType(settings.getDetails().getBrandName());
        companyToChange.getBrandName().clear();
        companyToChange.getBrandName().addAll(brandNames);

        // legal name
        List<PartyNameType> legalNames = UblAdapter.adaptPartyNames(settings.getDetails().getLegalName());
        companyToChange.getPartyName().clear();
        companyToChange.getPartyName().addAll(legalNames);

        // process ids
        List<String> processIds = new ArrayList<>(settings.getDetails().getProcessIds());
        companyToChange.getProcessID().clear();
        companyToChange.getProcessID().addAll(processIds);

        // VAT number
        if (settings.getDetails().getVatNumber() != null) {
            companyToChange.getPartyTaxScheme().clear();
            companyToChange.getPartyTaxScheme().add(adaptTaxSchema(settings.getDetails().getVatNumber()));
        }

        // postal address
        companyToChange.setPostalAddress(adaptAddress(settings.getDetails().getAddress()));

        // classification code
        companyToChange.setIndustryClassificationCode(adaptCodeType("IndustryClassificationCode", settings.getDetails().getBusinessType()));

        // website URL
        if (settings.getDescription() != null) {
            companyToChange.setWebsiteURI(settings.getDescription().getWebsite());

            // social media list
            ContactType socialMediaContact = new ContactType();
            socialMediaContact.setOtherCommunication(adaptSocialMediaList(settings.getDescription().getSocialMediaList()));
            companyToChange.setContact(socialMediaContact);

            // external resources
            List<DocumentReferenceType> existingExternalResources = companyToChange.getDocumentReference().stream()
                    .filter(d -> DOCUMENT_TYPE_EXTERNAL_RESOURCE.equals(d.getDocumentType()))
                    .collect(Collectors.toList());
            List<DocumentReferenceType> newExternalResources = UblAdapter.adaptExternalResources(settings.getDescription().getExternalResources());
            companyToChange.getDocumentReference().removeAll(existingExternalResources);
            companyToChange.getDocumentReference().addAll(newExternalResources);
        }

        // industry sectors
        List<TextType> industrySectors = UblAdapter.adaptLanguageMapToTextType(settings.getDetails().getIndustrySectors());
        companyToChange.getIndustrySector().clear();
        companyToChange.getIndustrySector().addAll(industrySectors);

        // check if representative is not already in list
        if (representative != null) {
            boolean alreadyInList = companyToChange.getPerson()
                    .stream()
                    .anyMatch(personType -> personType.getContact().getElectronicMail().equals(representative.getContact().getElectronicMail()));
            if (alreadyInList == false)
                companyToChange.getPerson().add(representative);
        }


        if (settings.getTradeDetails() != null) {

            if (companyToChange.getPurchaseTerms() == null)
                companyToChange.setPurchaseTerms(new TradingPreferences());

            // delivery terms
            List<DeliveryTermsType> deliveryTerms = settings.getTradeDetails().getDeliveryTerms().stream()
                    .map(UblAdapter::adaptDeliveryTerms)
                    .collect(Collectors.toList());
            companyToChange.getPurchaseTerms().getDeliveryTerms().clear();
            companyToChange.getPurchaseTerms().getDeliveryTerms().addAll(deliveryTerms);

            // payment means
            List<PaymentMeansType> paymentMeans = settings.getTradeDetails().getPaymentMeans().stream()
                    .map(UblAdapter::adaptPaymentMeans)
                    .collect(Collectors.toList());
            companyToChange.getPurchaseTerms().getPaymentMeans().clear();
            companyToChange.getPurchaseTerms().getPaymentMeans().addAll(paymentMeans);

            // PPAP
            int ppapLevel = settings.getTradeDetails().getPpapCompatibilityLevel() != null ? settings.getTradeDetails().getPpapCompatibilityLevel() : 0;
            companyToChange.setPpapCompatibilityLevel(BigDecimal.valueOf(ppapLevel));

            // stripe account it
            companyToChange.setStripeAccountId(settings.getTradeDetails().getStripeAccountId());
        }

        return companyToChange;
    }

    public static QualifyingPartyType adaptQualifyingParty(CompanySettings settings, PartyType company) {
        return adaptQualifyingParty(settings, company, null);
    }

    public static QualifyingPartyType adaptQualifyingParty(CompanySettings settings, PartyType company, QualifyingPartyType existingQualifyingParty) {

        QualifyingPartyType qualifyingParty = existingQualifyingParty == null ? new QualifyingPartyType() : existingQualifyingParty;

        // set verification info
        qualifyingParty.setBusinessIdentityEvidenceID(settings.getDetails().getVerificationInformation());

        // business keywords
        ClassificationSchemeType classificationScheme = new ClassificationSchemeType();
        classificationScheme.setDescription(adaptTextType(settings.getDetails().getBusinessKeywords()));
        qualifyingParty.setBusinessClassificationScheme(classificationScheme);

        // year of company registration
        if (settings.getDetails() != null && settings.getDetails().getYearOfCompanyRegistration() != null) {
            QuantityType years = new QuantityType();
            years.setValue(new BigDecimal(settings.getDetails().getYearOfCompanyRegistration()));
            qualifyingParty.setOperatingYearsQuantity(years);
        }

        // company events
        if (settings.getDescription() != null) {
            List<EventType> events = new ArrayList<>();
            settings.getDescription().getEvents().stream()
                    .map(UblAdapter::adaptEvent)
                    .collect(Collectors.toCollection(() -> events));
            qualifyingParty.getEvent().clear();
            qualifyingParty.getEvent().addAll(events);

            // company statement
            EconomicOperatorRoleType economicOperatorRole = new EconomicOperatorRoleType();
            economicOperatorRole.setRoleDescription(adaptLanguageMapToTextType(settings.getDescription().getCompanyStatement()));
            qualifyingParty.setEconomicOperatorRole(economicOperatorRole);
        }

        qualifyingParty.setParty(company);

        return qualifyingParty;
    }

    public static List<DocumentReferenceType> adaptExternalResources(List<String> externalResources) {
        return externalResources.stream()
                .map(er -> {
                    ExternalReferenceType externalReference = new ExternalReferenceType();
                    externalReference.setURI(er);
                    AttachmentType attachment = new AttachmentType();
                    attachment.setExternalReference(externalReference);

                    DocumentReferenceType documentReference = new DocumentReferenceType();
                    documentReference.setDocumentType(DOCUMENT_TYPE_EXTERNAL_RESOURCE);
                    documentReference.setAttachment(attachment);
                    return documentReference;
                })
                .collect(Collectors.toList());
    }


    public static List<String> adaptExternalResourcesType(List<DocumentReferenceType> externalResources) {
        return externalResources.stream()
                .filter(er -> DOCUMENT_TYPE_EXTERNAL_RESOURCE.equals(er.getDocumentType()))
                .filter(er -> er.getAttachment() != null && er.getAttachment().getExternalReference() != null)
                .map(er -> er.getAttachment().getExternalReference().getURI())
                .collect(Collectors.toList());
    }

    public static EventType adaptEvent(CompanyEvent event) {
        EventType ublEvent = new EventType();

        // identifier
        ublEvent.setIdentificationID(event.getName());

//        // completion indicator
//        Boolean completionIndicator = new Date().after(event.getDateTo());
//        ublEvent.setCompletionIndicator(completionIndicator);

        // address
        LocationType location = new LocationType();
        location.setAddress(adaptAddress(event.getPlace()));
        ublEvent.setOccurenceLocation(location);

        // start/end data
        PeriodType durationPeriod = new PeriodType();
        durationPeriod.setStartDateItem(event.getDateFrom());
        durationPeriod.setEndDateItem(event.getDateTo());
        ublEvent.setDurationPeriod(durationPeriod);

        // description
        ublEvent.setDescription(adaptTextTypeSingleLang(event.getDescription()));

        return ublEvent;
    }

    public static CompanyEvent adaptEvent(EventType ublEvent) {
        CompanyEvent companyEvent = new CompanyEvent();
        companyEvent.setDescription(ublEvent.getDescription().getValue());
        companyEvent.setName(ublEvent.getIdentificationID());
        if (ublEvent.getOccurenceLocation() != null)
            companyEvent.setPlace(adaptAddress(ublEvent.getOccurenceLocation().getAddress()));
        if (ublEvent.getDurationPeriod() != null) {
            companyEvent.setDateFrom(ublEvent.getDurationPeriod().getStartDateItem());
            companyEvent.setDateTo(ublEvent.getDurationPeriod().getEndDateItem());
        }
        return companyEvent;
    }

    public static PartyTaxSchemeType adaptTaxSchema(String vatNumber) {
        CodeType codeType = adaptCodeType(VAT_TAX_TYPE_CODE, vatNumber);
        TaxSchemeType taxScheme = new TaxSchemeType();
        taxScheme.setTaxTypeCode(codeType);
        PartyTaxSchemeType partyTaxSchemeType = new PartyTaxSchemeType();
        partyTaxSchemeType.setTaxScheme(taxScheme);
        return partyTaxSchemeType;
    }

    public static List<CommunicationType> adaptSocialMediaList(List<String> socialMediaList) {

        if (socialMediaList == null)
            return Collections.emptyList();

        return socialMediaList.stream().map(sm -> {
            CommunicationType communicationType = new CommunicationType();
            communicationType.setValue(UblAdapter.adaptTextTypeSingleLang(sm));
            return communicationType;
        }).collect(Collectors.toList());
    }

    public static CodeType adaptCodeType(String name, String value) {
        CodeType codeType = new CodeType();
        codeType.setName(name);
        codeType.setValue(value);
        return codeType;
    }

    public static CertificateType adaptCertificate(BinaryObjectType certificateBinary, String name, String type, String description) {

        CodeType codeType = adaptCodeType(name, null);

        AttachmentType attachmentType = new AttachmentType();
        attachmentType.setEmbeddedDocumentBinaryObject(certificateBinary);
        DocumentReferenceType documentReferenceType = new DocumentReferenceType();
        documentReferenceType.setAttachment(attachmentType);

        CertificateType certificateType = new CertificateType();
        certificateType.setRemarks(description);
        certificateType.setCertificateType(type);
        certificateType.setCertificateTypeCode(codeType);
        certificateType.getDocumentReference().add(documentReferenceType);

        return certificateType;
    }

    public static DocumentReferenceType adaptCompanyPhoto(BinaryObjectType photoBinary, Boolean isLogo) {

        AttachmentType attachment = new AttachmentType();
        attachment.setEmbeddedDocumentBinaryObject(photoBinary);

        DocumentReferenceType document = new DocumentReferenceType();
        String documentType = isLogo ? DOCUMENT_TYPE_COMPANY_LOGO : DOCUMENT_TYPE_COMPANY_PHOTO;
        document.setDocumentType(documentType);
        document.setAttachment(attachment);
        return document;
    }

    public static List<CompanyCertificate> adaptCertificates(List<CertificateType> certificateTypes) {
        return certificateTypes.stream()
                .map(certificateType ->
                        new CompanyCertificate(certificateType.getCertificateTypeCode().getName(),
                                certificateType.getCertificateType(),
                                certificateType.getHjid().toString(),
                                certificateType.getRemarks(),
                                certificateType.getDocumentReference().size() > 0 &&
                                        !Strings.isNullOrEmpty(certificateType.getDocumentReference().get(0).getAttachment().getEmbeddedDocumentBinaryObject().getFileName()) &&
                                        !Strings.isNullOrEmpty(certificateType.getDocumentReference().get(0).getAttachment().getEmbeddedDocumentBinaryObject().getMimeCode())))
                .collect(Collectors.toList());
    }

    public static QualityIndicatorType adaptQualityIndicator(QualityIndicatorParameter parameterName, Double value) {
        QualityIndicatorType qualityIndicator = new QualityIndicatorType();
        QuantityType quantity = new QuantityType();
        quantity.setValue(new BigDecimal(value));
        qualityIndicator.setQuantity(quantity);
        qualityIndicator.setQualityParameter(parameterName.name());
        return qualityIndicator;
    }

    public static List<CodeType> adaptProductCategories(Set<String> categoryCodes) {
        return categoryCodes.stream()
                .map(category -> {
                    CodeType code = new CodeType();
                    code.setValue(category);
                    return code;
                }).collect(Collectors.toList());
    }

    public static List<TextType> adaptLanguageMapToTextType(Map<NimbleConfigurationProperties.LanguageID, String> languageMap) {
        return languageMap.entrySet().stream()
                .map(sector -> {
                    TextType textType = new TextType();
                    textType.setLanguageID(sector.getKey().toString());
                    textType.setValue(sector.getValue());
                    return textType;
                }).collect(Collectors.toList());
    }

    public static TextType adaptTextType(String value, NimbleConfigurationProperties.LanguageID languageID) {
        TextType textType = new TextType();
        if (languageID != null)
            textType.setLanguageID(languageID.toString());
        textType.setValue(value);
        return textType;
    }

    @SuppressWarnings("ConstantConditions")
    public static TextType adaptTextTypeSingleLang(String value) {
        return adaptTextType(value, null);
    }

    public static List<TextType> adaptTextType(Map<NimbleConfigurationProperties.LanguageID, String> texts) {
        if (texts == null)
            return Collections.emptyList();
        return texts.entrySet().stream()
                .map( t -> adaptTextType(t.getValue(), t.getKey()))
                .collect(Collectors.toList());
    }

    public static Map<NimbleConfigurationProperties.LanguageID, String> adaptTextType(List<TextType> textTypes) {
        if(textTypes != null) {
            textTypes = textTypes.stream().filter(t -> StringUtils.isNotEmpty(t.getLanguageID()) && StringUtils.isNotEmpty(t.getValue())).collect(Collectors.toList());
            return textTypes.stream()
                    .collect(Collectors.toMap(t -> NimbleConfigurationProperties.LanguageID.fromString(t.getLanguageID()), TextType::getValue, (k1, k2) -> k1));
        } else {
            return Collections.emptyMap();
        }
    }

    public static List<String> adaptTextTypeList(List<TextType> textTypes) {
        return textTypes.stream().map(TextType::getValue).collect(Collectors.toList());
    }

    public static List<PartyNameType> adaptPartyNames(Map<NimbleConfigurationProperties.LanguageID, String> names) {

        if (names == null)
            return Collections.emptyList();

        List<TextType> textTypes = adaptTextType(names);
        return textTypes.stream().map(textType -> {
            PartyNameType partyName = new PartyNameType();
            partyName.setName(textType);
            return partyName;
        }).collect(Collectors.toList());
    }

    public static Map<NimbleConfigurationProperties.LanguageID, String> adaptPartyNames(List<PartyNameType> partyNameTypes) {
        return adaptTextType(partyNameTypes.stream().map(PartyNameType::getName).collect(Collectors.toList()));
    }

    public static String adaptPartyCreationDate(PartyType party) {

        if (party == null || party.getMetadata() == null)
            return null;

        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");

        return dateFormat.format(party.getMetadata().getCreationDateItem());
    }
}
 public enum Role {
    NIMBLE_USER("nimble_user"),
    INITIAL_REPRESENTATIVE("initial_representative"),
    LEGAL_REPRESENTATIVE("legal_representative"),
    PLATFORM_MANAGER("platform_manager"),
    COMPANY_ADMIN("company_admin"),
    PUBLISHER("publisher"),
    NIMBLE_DELETED_USER("nimble_deleted_user"),
    EXTERNAL_REPRESENTATIVE("external_representative"),
    MONITOR("monitor"),
    EFACTORY_USER("eFactoryUser");

    private final String role;

    Role(final String role) {
        this.role = role;
    }

    @Override
    public String toString() {
        return role;
    }
} public static String adaptPartyCreationDate(PartyType party) {

    if (party == null || party.getMetadata() == null)
        return null;

    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");

    return dateFormat.format(party.getMetadata().getCreationDateItem());
}

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import eu.nimble.core.infrastructure.identity.config.NimbleConfigurationProperties;
import eu.nimble.core.infrastructure.identity.service.AdminService;
import eu.nimble.core.infrastructure.identity.system.ControllerUtils;
import eu.nimble.core.infrastructure.identity.repository.PartyRepository;
import eu.nimble.core.infrastructure.identity.repository.PersonRepository;
import eu.nimble.core.infrastructure.identity.repository.QualifyingPartyRepository;
import eu.nimble.core.infrastructure.identity.service.IdentityService;
import eu.nimble.core.infrastructure.identity.uaa.OAuthClient;
import eu.nimble.core.infrastructure.identity.utils.UblAdapter;
import eu.nimble.service.model.ubl.commonaggregatecomponents.PartyType;
import eu.nimble.service.model.ubl.commonaggregatecomponents.PersonType;
import eu.nimble.service.model.ubl.commonaggregatecomponents.QualifyingPartyType;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.namespace.QName;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import static eu.nimble.core.infrastructure.identity.uaa.OAuthClient.Role.*;
import static eu.nimble.core.infrastructure.identity.utils.UblAdapter.adaptPartyCreationDate;
package eu.nimble.core.infrastructure.identity.system.ubl;




/**
 * Created by Johannes Innerbichler on 26/04/17.
 * Controller for retrieving party data.
 */
@Controller
@Api(value = "party", description = "API for handling parties on the platform.")
public class PartyController {

    private static final Logger logger = LoggerFactory.getLogger(PartyController.class);

    @Autowired
    private PartyRepository partyRepository;

    @Autowired
    private PersonRepository personRepository;

    @Autowired
    private QualifyingPartyRepository qualifyingPartyRepository;

    @Autowired
    private IdentityService identityService;