@FeignClient(name = "indexing-service", fallback = IndexingClientFallback.class)
public interface IndexingClient {
    @RequestMapping(method = RequestMethod.PUT, value = "/party", consumes = "application/json")
    @Headers("Content-Type: application/json")
    Response setParty(@RequestBody PartyType party,
                      @RequestHeader(value = "Authorization", required = true) String bearerToken);

    @RequestMapping(method = RequestMethod.GET, value = "/party")
    PartyType getParty(@RequestParam(value = "uri") String uri,
    @RequestHeader(value = "Authorization", required = true) String bearerToken);

    @RequestMapping(method = RequestMethod.DELETE, value = "/party")
    Boolean deleteParty(@RequestParam(value = "uri") String uri,
    @RequestHeader(value = "Authorization", required = true) String bearerToken);

    @RequestMapping(method = RequestMethod.POST, value = "/item/search", consumes = "application/json")
    SearchResult searchItem(@RequestBody Search search,
            @RequestHeader(value = "Authorization", required = true) String bearerToken);

    @RequestMapping(method = RequestMethod.DELETE, value = "/item")
    Boolean removeItem(@RequestParam(value = "uri") String uri,
            @RequestHeader(value = "Authorization", required = true) String bearerToken);

    @RequestMapping(method = RequestMethod.DELETE, value = "/catalogue")
    Boolean deleteCatalogue(@RequestParam(value = "uri") String uri,
            @RequestHeader(value = "Authorization", required = true) String bearerToken);

    @RequestMapping(method = RequestMethod.POST, value = "/class/search", consumes = "application/json")
    SearchResult searchClass(
            @RequestBody Search search,
            @RequestHeader(value = "Authorization") String bearerToken);
} @Service
@PropertySource("classpath:bootstrap.yml")
public class IndexingClientController {

    private IndexingClient nimbleIndexClient;

    private IndexingClient federatedIndexClient;

    private List<IndexingClient> clients;

    @Value("${nimble.indexing.url}")
    private String nimbleIndexUrl;

    @Value("${federated-index-enabled}")
    private boolean federatedIndexEnabled;

    @Value("${nimble.indexing.federated-index-url}")
    private String federatedIndexUrl;

    @Value("${federated-index-platform-name}")
    private String federatedIndexPlatformName;

    @Autowired
    IndexingClientFallback indexingFallback;

    public String getFederatedIndexPlatformName() {
        return federatedIndexPlatformName;
    }

    public IndexingClientController() {

    }

    public IndexingClient getNimbleIndexClient() {
        if (nimbleIndexClient == null) {
            nimbleIndexClient = createIndexingClient(nimbleIndexUrl);
        }
        return nimbleIndexClient;
    }


    public IndexingClient getFederatedIndexClient() {
        if (federatedIndexEnabled && federatedIndexClient == null) {
            federatedIndexClient = createIndexingClient(federatedIndexUrl);
        }
        return federatedIndexClient;
    }

    public List<IndexingClient> getClients() {
        if (clients == null) {
            clients = new ArrayList<IndexingClient>();
            clients.add(getNimbleIndexClient());
            if (federatedIndexEnabled) {
                clients.add(getFederatedIndexClient());
            }
        }
        return clients;
    }

    private IndexingClient createIndexingClient(String url) {
        return HystrixFeign.builder().contract(new SpringMvcContract())
                .encoder(new JacksonEncoder())
                .decoder(new JacksonDecoder())
                .retryer(new Retryer.Default(1,100,3))
                .target(IndexingClient.class, url, indexingFallback);
    }

} public class CompanyDetailsUpdates {

    private Map<NimbleConfigurationProperties.LanguageID, String> brandName = null;

    private Map<NimbleConfigurationProperties.LanguageID, String> legalName = null;

    private String vatNumber = null;

    private String verificationInformation = null;

    private Address address = null;

    private String businessType = null;

    private Map<NimbleConfigurationProperties.LanguageID, String> businessKeywords = null;

    private Integer yearOfCompanyRegistration = null;

    private Map<NimbleConfigurationProperties.LanguageID, String> industrySectors = null;


    public CompanyDetailsUpdates() {
    }

    public Map<NimbleConfigurationProperties.LanguageID, String> getBrandName() {
        return brandName;
    }

    public void setBrandName(Map<NimbleConfigurationProperties.LanguageID, String> brandName) {
        this.brandName = brandName;
    }

    public Map<NimbleConfigurationProperties.LanguageID, String> getLegalName() {
        return legalName;
    }

    public void setLegalName(Map<NimbleConfigurationProperties.LanguageID, String> legalName) {
        this.legalName = legalName;
    }

    public String getVatNumber() {
        return vatNumber;
    }

    public void setVatNumber(String vatNumber) {
        this.vatNumber = vatNumber;
    }

    public String getVerificationInformation() {
        return verificationInformation;
    }

    public void setVerificationInformation(String verificationInformation) {
        this.verificationInformation = verificationInformation;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public String getBusinessType() {
        return businessType;
    }

    public void setBusinessType(String businessType) {
        this.businessType = businessType;
    }

    public Map<NimbleConfigurationProperties.LanguageID, String> getBusinessKeywords() {
        return businessKeywords;
    }

    public void setBusinessKeywords(Map<NimbleConfigurationProperties.LanguageID, String> businessKeywords) {
        this.businessKeywords = businessKeywords;
    }

    public Integer getYearOfCompanyRegistration() {
        return yearOfCompanyRegistration;
    }

    public void setYearOfCompanyRegistration(Integer yearOfCompanyRegistration) {
        this.yearOfCompanyRegistration = yearOfCompanyRegistration;
    }

    public Map<NimbleConfigurationProperties.LanguageID, String> getIndustrySectors() {
        return industrySectors;
    }

    public void setIndustrySectors(Map<NimbleConfigurationProperties.LanguageID, String> industrySectors) {
        this.industrySectors = industrySectors;
    }

    public boolean areCompanyDetailsUpdated() {
        return this.brandName != null || this.address != null || this.businessKeywords != null || this.businessType != null || this.industrySectors != null ||
                this.legalName != null || this.vatNumber != null || this.verificationInformation != null || this.yearOfCompanyRegistration != null;
    }

}
 @Entity
@JsonInclude
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"id", "company_hjid"})})
public class NegotiationSettings implements Serializable {

    @Id
    @JsonIgnore
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToOne(cascade = CascadeType.MERGE, fetch = FetchType.LAZY)
    @JoinColumn(unique = true)
    private PartyType company;

    @JoinTable
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Range> deliveryPeriodRanges = new ArrayList<>();

    @ElementCollection(targetClass = String.class)
    private List<String> deliveryPeriodUnits = new ArrayList<>();

    @JoinTable
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Range> warrantyPeriodRanges = new ArrayList<>();

    @ElementCollection(targetClass = String.class)
    private List<String> warrantyPeriodUnits = new ArrayList<>();

    @ElementCollection(targetClass = String.class)
    private List<String> incoterms = new ArrayList<>();

    @ElementCollection(targetClass = String.class)
    private List<String> paymentTerms = new ArrayList<>();

    @ElementCollection(targetClass = String.class)
    private List<String> paymentMeans = new ArrayList<>();

    private String serviceLevel;

    @JoinTable
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List<CompanySensor> sensors = new ArrayList<>();


    public NegotiationSettings(PartyType company, List<Range> deliveryPeriodRanges, List<String> deliveryPeriodUnits, List<Range> warrantyPeriodRange,
                               List<String> warrantyPeriodUnits, List<String> incoterms, List<String> paymentTerms, List<String> paymentMeans,
                               List<CompanySensor> sensors, String serviceLevel) {
        this.company = company;
        this.deliveryPeriodRanges = deliveryPeriodRanges;
        this.deliveryPeriodUnits = deliveryPeriodUnits;
        this.warrantyPeriodRanges = warrantyPeriodRange;
        this.warrantyPeriodUnits = warrantyPeriodUnits;
        this.incoterms = incoterms;
        this.paymentTerms = paymentTerms;
        this.paymentMeans = paymentMeans;
        this.serviceLevel = serviceLevel;
        this.sensors = sensors;
    }

    public NegotiationSettings() {
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public PartyType getCompany() {
        return company;
    }

    public void setCompany(PartyType company) {
        this.company = company;
    }

    public List<Range> getDeliveryPeriodRanges() {
        return deliveryPeriodRanges;
    }

    public void setDeliveryPeriodRanges(List<Range> deliveryPeriodRanges) {
        this.deliveryPeriodRanges = deliveryPeriodRanges;
    }

    public List<String> getDeliveryPeriodUnits() {
        return deliveryPeriodUnits;
    }

    public void setDeliveryPeriodUnits(List<String> deliveryPeriodUnits) {
        this.deliveryPeriodUnits = deliveryPeriodUnits;
    }

    public List<Range> getWarrantyPeriodRanges() {
        return warrantyPeriodRanges;
    }

    public void setWarrantyPeriodRanges(List<Range> warrantyPeriodRanges) {
        this.warrantyPeriodRanges = warrantyPeriodRanges;
    }

    public List<String> getWarrantyPeriodUnits() {
        return warrantyPeriodUnits;
    }

    public void setWarrantyPeriodUnits(List<String> warrantyPeriodUnits) {
        this.warrantyPeriodUnits = warrantyPeriodUnits;
    }

    public List<String> getIncoterms() {
        return incoterms;
    }

    public void setIncoterms(List<String> incoterms) {
        this.incoterms = incoterms;
    }

    public List<String> getPaymentTerms() {
        return paymentTerms;
    }

    public void setPaymentTerms(List<String> paymentTerms) {
        this.paymentTerms = paymentTerms;
    }

    public List<String> getPaymentMeans() {
        return paymentMeans;
    }

    public void setPaymentMeans(List<String> paymentMeans) {
        this.paymentMeans = paymentMeans;
    }

    public List<CompanySensor> getSensors() {
        return sensors;
    }

    public void setSensors(List<CompanySensor> sensors) {
        this.sensors = sensors;
    }

    public String getServiceLevel() {
        return serviceLevel;
    }

    public void setServiceLevel(String serviceLevel) {
        this.serviceLevel = serviceLevel;
    }

    public void update(NegotiationSettings newSettings) {
        this.deliveryPeriodRanges.clear();
        this.deliveryPeriodRanges.addAll(newSettings.getDeliveryPeriodRanges());

        this.deliveryPeriodUnits.clear();
        this.deliveryPeriodUnits.addAll(newSettings.getDeliveryPeriodUnits());

        this.warrantyPeriodRanges.clear();
        this.warrantyPeriodRanges.addAll(newSettings.getWarrantyPeriodRanges());

        this.warrantyPeriodUnits.clear();
        this.warrantyPeriodUnits.addAll(newSettings.getWarrantyPeriodUnits());

        this.incoterms.clear();
        this.incoterms.addAll(newSettings.getIncoterms());

        this.paymentMeans.clear();
        this.paymentMeans.addAll(newSettings.getPaymentMeans());

        this.paymentTerms.clear();
        this.paymentTerms.addAll(newSettings.getPaymentTerms());

        if (null != newSettings.company) {
            this.company.setSalesTerms(newSettings.company.getSalesTerms());
            this.company.getProcessIDItems().clear();
            this.company.setProcessID(newSettings.company.getProcessID());

            this.company.getNetwork().clear();
            this.company.getNetwork().addAll(newSettings.company.getNetwork());
        }
    }

    @Entity
    public static class CompanySensor {

        @Id
        @JsonIgnore
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;

        @Column
        @JsonProperty("machine")
        @SerializedName("machine")
        private String machine;

        @Column
        @JsonProperty("sensor")
        @SerializedName("sensor")
        private String sensor;

        @Column
        @JsonProperty("format")
        @SerializedName("format")
        private String format;
    }

    @Entity
    public static class Range {

        @Id
        @JsonIgnore
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;

        @Column
        @JsonProperty("start")
        @SerializedName("start")
        private Long rangeStart;

        @Column
        @JsonProperty("end")
        @SerializedName("end")
        private Long rangeEnd;

        public Range(long start, long end) {
            this.rangeStart = start;
            this.rangeEnd = end;
        }

        public Range() {
        }

        public long getRangeStart() {
            return rangeStart;
        }

        public long getRangeEnd() {
            return rangeEnd;
        }
    }
} @Entity
public class UaaUser implements Serializable {

    @Id
    @Column(nullable = false, unique = true)
    private String externalID;

    private String username;

    @OneToOne
    private PersonType ublPerson;

    @ColumnDefault("false")
    private Boolean showWelcomeInfo = false;

    public UaaUser() {
    }

    public UaaUser(String username, PersonType ublPerson, String externalID) {
        this.username = username;
        this.ublPerson = ublPerson;
        this.externalID = externalID;
        this.showWelcomeInfo = true;
    }

    public String getUsername() {
        return username;
    }

    public String getExternalID() {
        return externalID;
    }

    public PersonType getUBLPerson() {
        return this.ublPerson;
    }

    public Boolean getShowWelcomeInfo() {
        return showWelcomeInfo;
    }

    public void setShowWelcomeInfo(Boolean showWelcomeInfo) {
        this.showWelcomeInfo = showWelcomeInfo;
    }
} public class CompanyDetails {

    @ApiModelProperty(value = "Trade name name of the company")
    private Map<LanguageID, String> brandName = null;

    @ApiModelProperty(value = "Legal name of the company")
    private Map<LanguageID, String> legalName = null;

    @ApiModelProperty(value = "VAT identification number of the company")
    private String vatNumber = null;

    @ApiModelProperty(value = "Additional info used for verifying the company")
    private String verificationInformation = null;

    @ApiModelProperty(value = "Main address of the company")
    private Address address = null;

    @ApiModelProperty(value = "Business type identifier")
    private String businessType = null;

    @ApiModelProperty(value = "Keywords explaining business objective")
    private Map<LanguageID, String> businessKeywords = new HashMap<>();

    @ApiModelProperty(value = "Year since company is active")
    private Integer yearOfCompanyRegistration;

    @ApiModelProperty(value = "List of industry sectors in which company is active in.")
    private Map<LanguageID, String> industrySectors = new HashMap<>();

    @ApiModelProperty(value = "List of process instance ids which are included in the workflow of company")
    private List<String> processIds = new ArrayList<>();

    @ApiModelProperty(value = "List of users participating in the company")
    private List<PersonType> person = null;

    public CompanyDetails() {
    }

    public CompanyDetails(Map<LanguageID, String> brandName, Map<LanguageID, String> legalName, String vatNumber,
                          String verificationInformation, Address address, String businessType, Map<LanguageID, String> businessKeywords,
                          Integer yearOfCompanyRegistration, Map<LanguageID, String> industrySectors, List<String> processIds, List<PersonType> person) {
        this.brandName = brandName;
        this.legalName = legalName;
        this.vatNumber = vatNumber;
        this.verificationInformation = verificationInformation;
        this.address = address;
        this.businessType = businessType;
        this.businessKeywords = businessKeywords;
        this.yearOfCompanyRegistration = yearOfCompanyRegistration;
        this.industrySectors = industrySectors;
        this.processIds = processIds;
        this.person = person;
    }

    public Map<LanguageID, String> getBrandName() {
        return brandName;
    }

    public void setBrandName(Map<LanguageID, String> brandName) {
        this.brandName = brandName;
    }

    public Map<LanguageID, String> getLegalName() {
        return legalName;
    }

    public void setLegalName(Map<LanguageID, String> legalName) {
        this.legalName = legalName;
    }

    public String getVatNumber() {
        return vatNumber;
    }

    public void setVatNumber(String vatNumber) {
        this.vatNumber = vatNumber;
    }

    public String getVerificationInformation() {
        return verificationInformation;
    }

    public void setVerificationInformation(String verificationInformation) {
        this.verificationInformation = verificationInformation;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public String getBusinessType() {
        return businessType;
    }

    public void setBusinessType(String businessType) {
        this.businessType = businessType;
    }

    public Map<LanguageID, String> getBusinessKeywords() {
        return businessKeywords;
    }

    public void setBusinessKeywords(Map<LanguageID, String> businessKeywords) {
        this.businessKeywords = businessKeywords;
    }

    public Integer getYearOfCompanyRegistration() {
        return yearOfCompanyRegistration;
    }

    public void setYearOfCompanyRegistration(Integer yearOfCompanyRegistration) {
        this.yearOfCompanyRegistration = yearOfCompanyRegistration;
    }

    public Map<LanguageID, String> getIndustrySectors() {
        return industrySectors;
    }

    public void setIndustrySectors(Map<LanguageID, String> industrySectors) {
        this.industrySectors = industrySectors;
    }

    public List<String> getProcessIds() {
        return processIds;
    }

    public void setProcessIds(List<String> processIds) {
        this.processIds = processIds;
    }

    public List<PersonType> getPerson() {
        return person;
    }

    public void setPerson(List<PersonType> person) {
        this.person = person;
    }
}
 public class CompanySettings {

    @ApiModelProperty(value = "Identifier of company")
    private String companyID;

    @ApiModelProperty(value = "General details related to the company")
    private CompanyDetails details = new CompanyDetails();

    @ApiModelProperty(value = "Descriptive data of the company")
    private CompanyDescription description = new CompanyDescription();

    @ApiModelProperty(value = "Trade details of the company")
    private CompanyTradeDetails tradeDetails = new CompanyTradeDetails();

    @ApiModelProperty(value = "Certificates of company")
    private List<CompanyCertificate> certificates = new ArrayList<>();

    @ApiModelProperty(value = "List of preferred product categories")
    private Set<String> preferredProductCategories = new HashSet<>();

    @ApiModelProperty(value = "List of recently used product categories")
    private Set<String> recentlyUsedProductCategories = new HashSet<>();

    @ApiModelProperty(value = "Set of subscribed company ids")
    private Set<String> subscribedCompanyIds = new HashSet<>();

    @ApiModelProperty(value = "List of subscribed category codes")
    private List<CodeType> subscribedProductCategories = new ArrayList<>();

    @ApiModelProperty(value = "List of terms and conditions files")
    private List<DocumentReferenceType> termsAndConditions = new ArrayList<>();

    public CompanySettings() {
    }

    public CompanySettings(String companyID, CompanyDetails details, CompanyDescription description, CompanyTradeDetails tradeDetails,
                           List<CompanyCertificate> certificates, Set<String> preferredProductCategories,
                           Set<String> recentlyUsedProductCategories,Set<String> subscribedCompanyIds,List<CodeType> subscribedProductCategories) {
        this.companyID = companyID;
        this.details = details;
        this.description = description;
        this.tradeDetails = tradeDetails;
        this.certificates = certificates;
        this.preferredProductCategories = preferredProductCategories;
        this.recentlyUsedProductCategories = recentlyUsedProductCategories;
        this.subscribedCompanyIds = subscribedCompanyIds;
        this.subscribedProductCategories = subscribedProductCategories;
    }

    public String getCompanyID() {
        return companyID;
    }

    public void setCompanyID(String companyID) {
        this.companyID = companyID;
    }

    public CompanyDetails getDetails() {
        return details;
    }

    public void setDetails(CompanyDetails details) {
        this.details = details;
    }

    public CompanyDescription getDescription() {
        return description;
    }

    public void setDescription(CompanyDescription description) {
        this.description = description;
    }

    public CompanyTradeDetails getTradeDetails() {
        return tradeDetails;
    }

    public List<CompanyCertificate> getCertificates() {
        return certificates;
    }

    public void setCertificates(List<CompanyCertificate> certificates) {
        this.certificates = certificates;
    }

    public void setTradeDetails(CompanyTradeDetails tradeDetails) {
        this.tradeDetails = tradeDetails;
    }

    public Set<String> getPreferredProductCategories() {
        return preferredProductCategories;
    }

    public void setPreferredProductCategories(Set<String> preferredProductCategories) {
        this.preferredProductCategories = preferredProductCategories;
    }

    public Set<String> getRecentlyUsedProductCategories() {
        return recentlyUsedProductCategories;
    }

    public void setRecentlyUsedProductCategories(Set<String> recentlyUsedProductCategories) {
        this.recentlyUsedProductCategories = recentlyUsedProductCategories;
    }

    public Set<String> getSubscribedCompanyIds() {
        return subscribedCompanyIds;
    }

    public void setSubscribedCompanyIds(Set<String> subscribedCompanyIds) {
        this.subscribedCompanyIds = subscribedCompanyIds;
    }

    public List<CodeType> getSubscribedProductCategories() {
        return subscribedProductCategories;
    }

    public void setSubscribedProductCategories(List<CodeType> subscribedProductCategories) {
        this.subscribedProductCategories = subscribedProductCategories;
    }

    public List<DocumentReferenceType> getTermsAndConditions() {
        return termsAndConditions;
    }

    public void setTermsAndConditions(List<DocumentReferenceType> termsAndConditions) {
        this.termsAndConditions = termsAndConditions;
    }
} @Service
@SuppressWarnings("Duplicates")
public class EmailService {

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

    @Autowired
    private JavaMailSender emailSender;

    @Autowired
    private UblUtils ublUtils;

    @Autowired
    private MessageSource messageSource;

    @Autowired
    private TemplateEngine textMailTemplateEngine;

    @Value("${spring.mail.defaultFrom}")
    private String defaultFrom;

    @Value("${spring.mail.debug:false}")
    private Boolean debug;

    @Value("${nimble.frontend.url}")
    private String frontendUrl;

    @Value("${nimble.supportEmail}")
    private String supportEmail;

    @Value("${nimble.platformName}")
    private String platformVersion;

    @Value("${spring.mail.platformName}")
    private String platformName;

    @Value("${nimble.frontend.registration.url}")
    private String frontendRegistrationUrl;

    @Value("${nimble.frontend.company-details.url}")
    private String companyDetailsUrl;

    @Value("${spring.mail.languages}")
    private String mailTemplateLanguages;

    @Value("${nimble.companyDataUpdateEmail}")
    private String companyDataUpdateEmail;

    public void sendResetCredentialsLink(String toEmail, String credentials, String language) throws UnsupportedEncodingException{
        String resetCredentialsURL = frontendUrl + "/#/user-mgmt/forgot/?key=" + URLEncoder.encode(credentials, "UTF-8");
        Context context = new Context();
        context.setVariable("resetPasswordURL", resetCredentialsURL);
        context.setVariable("platformName",platformName);

        String version = Strings.isNullOrEmpty(platformVersion) ? "": String.format(" (%s)",platformVersion);
        String subject = getMailSubject(NimbleMessageCode.MAIL_SUBJECT_RESET_CREDENTIALS_LINK,language,Arrays.asList(platformName,version));

        this.send(new String[]{toEmail}, subject, getTemplateName("password-reset",language), context);
    }

    public void sendInvite(String toEmail, String senderName, String companyName, Collection<String> roles, String language) throws UnsupportedEncodingException {
        String invitationUrl = String.format("%s/%s/?email=%s",frontendUrl,frontendRegistrationUrl,URLEncoder.encode(toEmail, "UTF-8"));

        Context context = new Context();
        context.setVariable("senderName", senderName);
        context.setVariable("companyName", companyName);
        context.setVariable("invitationUrl", invitationUrl);
        context.setVariable("roles", roles);
        context.setVariable("platformName",platformName);

        String version = Strings.isNullOrEmpty(platformVersion) ? "": String.format(" (%s)",platformVersion);
        String subject = getMailSubject(NimbleMessageCode.MAIL_SUBJECT_INVITATION,language,Arrays.asList(platformName,version));

        this.send(new String[]{toEmail}, subject, getTemplateName("invitation",language), context);
    }

    public void sendSubscriptionSummary(List<String> toEmail, List<SubscriptionSummary> subscriptionSummaryList, String language) throws UnsupportedEncodingException {
        Context context = new Context();

        // keeps the list of SubscriptionMailModel which corresponds to the 'subscriptions' variable in mail template
        List<SubscriptionMailModel> subscriptionMailModels = new ArrayList<>();
        // populate subscriptionMailModels array
        for (SubscriptionSummary subscriptionSummary : subscriptionSummaryList) {
            // retrieve the urls for product details
            List<String> productDetailsUrls = new ArrayList<>();
            for (int i = 0; i < subscriptionSummary.getCatalogueIds().size(); i++) {
                productDetailsUrls.add(String.format("%s/#/product-details?catalogueId=%s&id=%s",frontendUrl,URLEncoder.encode(subscriptionSummary.getCatalogueIds().get(i), "UTF-8"),
                        URLEncoder.encode(subscriptionSummary.getProductIds().get(i), "UTF-8")));
            }
            // set title of subscription
            String title;
            // company
            if(subscriptionSummary.getCompanyName() != null){
                title = String.format("The following products are published/updated by %s",subscriptionSummary.getCompanyName());
            }
            // category
            else{
                title = String.format("The following products are published/updated on category: %s",subscriptionSummary.getCategoryName());
            }
            // create the subscription mail model
            SubscriptionMailModel subscriptionMailModel = new SubscriptionMailModel();
            subscriptionMailModel.setTitle(title);
            subscriptionMailModel.setProductUrls(productDetailsUrls);

            subscriptionMailModels.add(subscriptionMailModel);
        }
        // set context variables
        context.setVariable("subscriptions", subscriptionMailModels);
        context.setVariable("platformName",platformName);
        // set mail subject
        String version = Strings.isNullOrEmpty(platformVersion) ? "": String.format(" (%s)",platformVersion);
        String subject = getMailSubject(NimbleMessageCode.MAIL_SUBJECT_SUBSCRIPTION,language,Arrays.asList(platformName,version));

        this.send(toEmail.toArray(new String[0]), subject, getTemplateName("subscription",language), context);
    }

    public void informInviteExistingCompany(String toEmail, String senderName, String companyName, Collection<String> roles, String language) {
        Context context = new Context();
        context.setVariable("senderName", senderName);
        context.setVariable("companyName", companyName);
        context.setVariable("nimbleUrl", frontendUrl);
        context.setVariable("roles", roles);
        context.setVariable("platformName",platformName);

        String version = Strings.isNullOrEmpty(platformVersion) ? "": String.format(" (%s)",platformVersion);
        String subject = getMailSubject(NimbleMessageCode.MAIL_SUBJECT_INVITATION_EXISTING_COMPANY, language, Arrays.asList(companyName,platformName,version));

        this.send(new String[]{toEmail}, subject, getTemplateName("invitation_existing_company",language), context);
    }

    public void notifyPlatformManagersCompanyDataUpdates(List<String> emails, PersonType user, PartyType company, CompanyDetailsUpdates companyDetailsUpdates, String language){
        Context context = new Context();

        // collect info of user
        String username = user.getFirstName() + " " + user.getFamilyName();
        context.setVariable("username", username);

        // collect info of company and platform
        context.setVariable("companyName", ublUtils.getName(company));
        context.setVariable("platformName",platformName);
        // company updates
        context.setVariable("companyUpdates",getMailContentForCompanyDetailsUpdates(companyDetailsUpdates,language));
        // link to the company details
        context.setVariable("companyDetailsUrl", String.format("%s/%s?id=%s&viewMode=mgmt",frontendUrl,companyDetailsUrl,company.getPartyIdentification().get(0).getID()));

        String version = Strings.isNullOrEmpty(platformVersion) ? "": String.format(" (%s)",platformVersion);
        String subject = getMailSubject(NimbleMessageCode.MAIL_SUBJECT_COMPANY_DATA_UPDATED, language, Arrays.asList(platformName,version));

        // if the specific email address is defined for the company data updates, use it to send the email
        // otherwise, send the email to all platform managers
        if(!Strings.isNullOrEmpty(this.companyDataUpdateEmail)){
            emails = Arrays.asList(this.companyDataUpdateEmail);
        }

        this.send(emails.toArray(new String[]{}), subject, getTemplateName("company_data_updated",language), context);
    }

    public void notifyPlatformManagersNewCompany(List<String> emails, PersonType representative, PartyType company, String language) {

        Context context = new Context();

        // collect info of user
        String username = representative.getFirstName() + " " + representative.getFamilyName();
        context.setVariable("username", username);

        String userEmail = null;
        if (representative.getContact() != null)
            userEmail = representative.getContact().getElectronicMail();
        context.setVariable("userEmail", userEmail);

        // collect info of user
        context.setVariable("companyName", ublUtils.getName(company));
        context.setVariable("companyID", company.getHjid());
        context.setVariable("platformName",platformName);

        // collect info of company
        if (company.getPostalAddress() != null) {
            AddressType address = company.getPostalAddress();
            String countryName = address.getCountry() != null && address.getCountry().getIdentificationCode() != null? CountryUtil.getCountryNameByISOCode(address.getCountry().getIdentificationCode().getValue()) : null;
            context.setVariable("companyCountry", countryName);
            context.setVariable("companyStreet", address.getStreetName());
            context.setVariable("companyBuildingNumber", address.getBuildingNumber());
            context.setVariable("companyCity", address.getCityName());
            context.setVariable("companypostalCode", address.getPostalZone());
        }

        String version = Strings.isNullOrEmpty(platformVersion) ? "": String.format(" (%s)",platformVersion);
        String subject = getMailSubject(NimbleMessageCode.MAIL_SUBJECT_COMPANY_REGISTERED, language, Arrays.asList(platformName,version));

        this.send(emails.toArray(new String[]{}), subject, getTemplateName("new_company",language), context);
    }

    public void notifyVerifiedCompany(String email, PersonType legalRepresentative, PartyType company, String language) {

        Context context = new Context();
        context.setVariable("firstName", legalRepresentative.getFirstName());
        context.setVariable("familyName", legalRepresentative.getFamilyName());
        context.setVariable("companyName", ublUtils.getName(company));
        context.setVariable("supportEmail", supportEmail);
        context.setVariable("nimbleUrl", frontendUrl);
        context.setVariable("platformName",platformName);

        String version = Strings.isNullOrEmpty(platformVersion) ? "": String.format(" (%s)",platformVersion);
        String subject = getMailSubject(NimbleMessageCode.MAIL_SUBJECT_COMPANY_VERIFIED,language,Arrays.asList(platformName,version));

        this.send(new String[]{email}, subject, getTemplateName("company_verified",language), context);
    }

    public void notifyDeletedCompany(List<PersonType> legalRepresentatives, PartyType company, String language) {

        String companyName = ublUtils.getName(company);
        for (PersonType legalRepresentative : legalRepresentatives) {
            Context context = new Context();
            context.setVariable("firstName", legalRepresentative.getFirstName());
            context.setVariable("familyName", legalRepresentative.getFamilyName());
            context.setVariable("companyName", companyName);
            context.setVariable("platformName",platformName);

            String version = Strings.isNullOrEmpty(platformVersion) ? "": String.format(" (%s)",platformVersion);
            String subject = getMailSubject(NimbleMessageCode.MAIL_SUBJECT_COMPANY_DELETED,language,Arrays.asList(platformName,version));

            try {
                this.send(new String[]{legalRepresentative.getContact().getElectronicMail()}, subject, getTemplateName("company_deleted",language), context);
            }catch (Exception e){
                logger.error("Failed to send email:",e);
            }
        }
    }

    private void send(String[] to, String subject, String template, Context context) {

        SimpleMailMessage mailMessage = new SimpleMailMessage();
        String message = this.textMailTemplateEngine.process(template, context);

        if (debug) {
            logger.info(message);
            return;
        }

        mailMessage.setFrom(this.defaultFrom);
        mailMessage.setTo(to);
        mailMessage.setSubject(subject);
        mailMessage.setText(message);

        this.emailSender.send(mailMessage);
    }

    private String getTemplateName(String templateName,String language){
        List<String> languages = Arrays.asList(mailTemplateLanguages.split(","));
        if(languages.contains(language)){
            return String.format("%s_%s",templateName,language);
        }
        return String.format("%s_%s",templateName,languages.get(0));
    }

    private String getMailSubject(NimbleMessageCode messageCode, String language, List<String> parameters){
        return this.messageSource.getMessage(messageCode.toString(), parameters.toArray(), getLocale(language));
    }

    /**
     * Returns the {{@link Locale}} for the given language
     * */
    private Locale getLocale(String language){
        List<String> languages = Arrays.asList(mailTemplateLanguages.split(","));
        String mailSubjectLanguage = languages.contains(language) ? language :languages.get(0) ;
        return new Locale(mailSubjectLanguage);
    }

    /**
     * Returns the mail content for company details updates
     * @param companyDetailsUpdates the updated fields of company details
     * @param language the language id to be used to put proper translations of company fields
     * @return the mail content for company details updates as string
     * */
    private String getMailContentForCompanyDetailsUpdates(CompanyDetailsUpdates companyDetailsUpdates,String language){
        Locale locale = getLocale(language);

        StringBuilder content = new StringBuilder();
        // company name
        if(companyDetailsUpdates.getLegalName() != null){
            content.append(this.messageSource.getMessage(NimbleMessageCode.COMPANY_NAME.toString(),null,locale)).append(":\n");
            content.append(stringifyLanguageMap(companyDetailsUpdates.getLegalName())).append("\n");
        }
        // brand name
        if(companyDetailsUpdates.getBrandName() != null){
            content.append(this.messageSource.getMessage(NimbleMessageCode.BRAND_NAME.toString(),null,locale)).append(":\n");
            content.append(stringifyLanguageMap(companyDetailsUpdates.getBrandName())).append("\n");
        }
        // vat number
        if(companyDetailsUpdates.getVatNumber() != null){
            content.append(this.messageSource.getMessage(NimbleMessageCode.VAT_NUMBER.toString(),null,locale)).append(":\n");
            content.append(companyDetailsUpdates.getVatNumber()).append("\n\n");
        }
        // verification info
        if(companyDetailsUpdates.getVerificationInformation() != null){
            content.append(this.messageSource.getMessage(NimbleMessageCode.VERIFICATION_INFO.toString(),null,locale)).append(":\n");
            content.append(companyDetailsUpdates.getVerificationInformation()).append("\n\n");
        }
        // business type
        if(companyDetailsUpdates.getBusinessType() != null){
            String businessTypeKey = companyDetailsUpdates.getBusinessType().replace(" ","_").toUpperCase();
            content.append(this.messageSource.getMessage(NimbleMessageCode.BUSINESS_TYPE.toString(),null,locale)).append(":\n");
            content.append(this.messageSource.getMessage(businessTypeKey,null,locale)).append("\n\n");
        }
        // activity sectors
        if(companyDetailsUpdates.getIndustrySectors() != null){
            content.append(this.messageSource.getMessage(NimbleMessageCode.ACTIVITY_SECTORS.toString(),null,locale)).append(":\n");
            content.append(stringifyLanguageMap(companyDetailsUpdates.getIndustrySectors())).append("\n");
        }
        // business keywords
        if(companyDetailsUpdates.getBusinessKeywords() != null){
            content.append(this.messageSource.getMessage(NimbleMessageCode.BUSINESS_KEYWORDS.toString(),null,locale)).append(":\n");
            content.append(stringifyLanguageMap(companyDetailsUpdates.getBusinessKeywords())).append("\n");
        }
        // year of foundation
        if(companyDetailsUpdates.getYearOfCompanyRegistration() != null){
            content.append(this.messageSource.getMessage(NimbleMessageCode.YEAR_FOUNDATION.toString(),null,locale)).append(":\n");
            content.append(companyDetailsUpdates.getYearOfCompanyRegistration()).append("\n\n");
        }
        // address
        if(companyDetailsUpdates.getAddress() != null){
            // street
            content.append(this.messageSource.getMessage(NimbleMessageCode.STREET.toString(),null,locale)).append(":\n");
            content.append(companyDetailsUpdates.getAddress().getStreetName()).append("\n\n");
            // building number
            content.append(this.messageSource.getMessage(NimbleMessageCode.BUILDING_NUMBER.toString(),null,locale)).append(":\n");
            content.append(companyDetailsUpdates.getAddress().getBuildingNumber()).append("\n\n");
            // city/town
            content.append(this.messageSource.getMessage(NimbleMessageCode.CITY_TOWN.toString(),null,locale)).append(":\n");
            content.append(companyDetailsUpdates.getAddress().getCityName()).append("\n\n");
            // state/province
            content.append(this.messageSource.getMessage(NimbleMessageCode.STATE_PROVINCE.toString(),null,locale)).append(":\n");
            content.append(companyDetailsUpdates.getAddress().getRegion()).append("\n\n");
            // postal code
            content.append(this.messageSource.getMessage(NimbleMessageCode.POSTAL_CODE.toString(),null,locale)).append(":\n");
            content.append(companyDetailsUpdates.getAddress().getPostalCode()).append("\n\n");
            // country
            content.append(this.messageSource.getMessage(NimbleMessageCode.COUNTRY.toString(),null,locale)).append(":\n");
            content.append(companyDetailsUpdates.getAddress().getCountry()).append("\n");
        }
        return content.toString();
    }

    /**
     * Stringifies the given language map
     * @param languageMap the language id - value map
     * @return language id - value pairs separeted by new line
     * */
    private String stringifyLanguageMap(Map<NimbleConfigurationProperties.LanguageID, String> languageMap){
        String newLineChar = "\n";
        StringBuilder stringBuilder = new StringBuilder();
        languageMap.forEach((languageID, value) -> {
            List<String> values = Arrays.asList(value.split(newLineChar).clone());
            values.forEach(v -> stringBuilder.append(v).append("(").append(languageID).append(")\n"));
        });
        return stringBuilder.toString();
    }
}
 @Component
public class KafkaSender {

    @Value("${nimble.kafka.topics.companyUpdates}")
    private String companyUpdatesTopic;

    @Value("${nimble.kafka.topics.ratingsUpdates}")
    private String ratingsUpdatesTopic;

    @Autowired
    private KafkaTemplate<String, AuthorizedCompanyUpdate> kafkaTemplate;

    public void broadcastCompanyUpdate(String companyID, String accessToken) {
        accessToken = accessToken.replace("Bearer ", "");
        AuthorizedCompanyUpdate update = new AuthorizedCompanyUpdate(companyID, accessToken);
        kafkaTemplate.send(companyUpdatesTopic, update);
        System.out.println("Message: " + update + " sent to topic: " + companyUpdatesTopic);
    }

    public void broadcastRatingsUpdate(String companyId, String accessToken) {
        accessToken = accessToken.replace("Bearer ", "");
        AuthorizedCompanyUpdate update = new AuthorizedCompanyUpdate(companyId, accessToken);
        kafkaTemplate.send(ratingsUpdatesTopic, update);
        System.out.println("Message: " + update + " sent to topic: " + ratingsUpdatesTopic);
    }
} @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
    }
} @Service
public class CertificateService {

    @Autowired
    private CertificateRepository certificateRepository;

    @Transactional
    public CertificateType queryCertificate(Long certificateId) {
        CertificateType certificateType = certificateRepository.findOne(certificateId);

        // pre fetch binary (legacy due to dedicated binary database)
        if (certificateType != null && certificateType.getDocumentReference() != null && certificateType.getDocumentReference().isEmpty() == false)
            certificateType.getDocumentReference().get(0).getAttachment().getEmbeddedDocumentBinaryObject().getValue();

        return certificateType;
    }
} @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);
    }
} @SuppressWarnings("Convert2MethodRef")
@Service
public class KeycloakAdmin {

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

    public static final String NIMBLE_USER_ROLE = "nimble_user";
    public static final String INITIAL_REPRESENTATIVE_ROLE = "initial_representative";
    public static final String LEGAL_REPRESENTATIVE_ROLE = "legal_representative";
    public static final String PLATFORM_MANAGER_ROLE = "platform_manager";
    public static final String NIMBLE_DELETED_USER = "nimble_deleted_user";

    public static final String PLATFORM_MANAGER_GROUP = "Platform Manager";

    public static final List<String> NON_ASSIGNABLE_ROLES = Arrays.asList("platform_manager", "uma_authorization",
            "offline_access", "admin", "create-realm",
            "create-realm", "nimble_user", "initial_representative");

    public static final List<String> NON_NIMBLE_ROLES = Arrays.asList("uma_authorization", "offline_access", "admin", "create-realm", "create-realm");

    @Value("${nimble.oauth.identityProvider.eFactory}")
    private String eFactoryIdentityProvider;

    @Autowired
    private KeycloakConfig keycloakConfig;

    @Autowired
    private OAuthClient oAuthClient;

    @Autowired
    private OAuthClientConfig oAuthClientConfig;

    private Keycloak keycloak;

    private static long oneHourInMilliSeconds = 3600000;

    @PostConstruct
    @SuppressWarnings("unused")
    public void init() {
        ResteasyClient client = new ResteasyClientBuilder().connectionPoolSize(10).build();
        this.keycloak = KeycloakBuilder.builder()
                .serverUrl(keycloakConfig.getServerUrl())
                .realm(keycloakConfig.getRealm())
                .grantType(OAuth2Constants.PASSWORD)
                .username(keycloakConfig.getAdmin().getUsername())
                .password(keycloakConfig.getAdmin().getPassword())
                .clientId(keycloakConfig.getAdmin().getCliendId())
                .clientSecret(keycloakConfig.getAdmin().getCliendSecret())
                .resteasyClient(client)
                .build();
    }

    public String initiatePasswordRecoveryProcess(String email) {
        RealmResource realmResource = this.keycloak.realm(keycloakConfig.getRealm());
        UsersResource userResource = realmResource.users();

        List<UserRepresentation> userList = userResource.search(email);
        if (userList.size() != 0) {
            UserRepresentation user = userList.get(0);
            return JWT.create()
                    .withJWTId(user.getId())
                    .withClaim(GlobalConstants.JWT_TYPE_ATTRIBUTE_STRING, "reset-credentials")
                    .withSubject(user.getId())
                    .withExpiresAt(new Date(System.currentTimeMillis() + oneHourInMilliSeconds))
                    .sign(HMAC512(keycloakConfig.getAdmin().getCliendSecret().getBytes()));
        }else {
            throw new ResourceNotFoundException("Resource not found for the user name");
        }
    }

    public void resetPasswordViaRecoveryProcess(String token, String newPassword) {

        try {
            JWTVerifier verifier = JWT.require(HMAC512(keycloakConfig.getAdmin().getCliendSecret().getBytes())).build();
            verifier.verify(token);
        } catch (SignatureVerificationException e) {
            logger.warn("Invalid token received for password reset");
            throw new BadRequestException("Invalid Token");
        }

        long exp = JWT.decode(token).getClaim(GlobalConstants.JWT_EXPIRY_ATTRIBUTE_STRING).asLong();
        if (System.currentTimeMillis() > exp) {
            String sub = JWT.decode(token).getClaim(GlobalConstants.JWT_SUBJECT_ATTRIBUTE_STRING).asString();
            RealmResource realmResource = this.keycloak.realm(keycloakConfig.getRealm());
            UserResource use = realmResource.users().get(sub);
            CredentialRepresentation passwordCredential = createPasswordCredentials(newPassword);
            passwordCredential.setTemporary(false);
            this.keycloak.realm(keycloakConfig.getRealm()).users().get(sub).resetPassword(passwordCredential);
        }else {
            throw new NotAuthorizedException("URL expired");
        }
    }

    /**
     * Verify the jwt token provided by the Keycloak
     * TODO: Implement verification by using the certs
     * https://stackoverflow.com/questions/39890232/how-to-decode-keys-from-keycloak-openid-connect-cert-api
     * @param jwtToken
     * @return
     */
    public boolean verify(String jwtToken) throws ServerException {

        String uri = keycloakConfig.getServerUrl() + "/realms/master";

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> entity = new HttpEntity<String>(headers);
        RestTemplate rs = new RestTemplate();

        try {
            ResponseEntity<String> response = rs.exchange(uri, HttpMethod.GET, entity, String.class);
            ObjectMapper mapper = new ObjectMapper();
            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

            RealmConfigs realmConfigs = mapper.readValue(response.getBody(), RealmConfigs.class);
            JwtHelper.decodeAndVerify(jwtToken, new RsaVerifier(getRSAPublicKey(realmConfigs.getPublic_key())));
        } catch (IOException e) {
            logger.error("Error in extracting the data from Keycloak realm response{}", e);
            throw new ServerException("Keycloak server error", e);
        } catch (Exception e) {
            logger.error("Error in verifying token{}", e);
            return false;
        }
        return true;
    }


    /**
     * Generates the RSA Public key from Keycloak public key
     * @param publicKey
     * @return
     * @throws Exception
     */
    private RSAPublicKey getRSAPublicKey(String publicKey) throws Exception {
        if( StringUtils.isBlank(publicKey)) return null;
        try {
            KeyFactory keyFactory = java.security.KeyFactory.getInstance("RSA");
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(java.util.Base64.getDecoder().decode(publicKey));
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
        } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
            logger.error("Error forming RSA key {}", e);
            throw new Exception(e);
        }
    }

    /**
     * throws javax.ws.rs.WebApplicationException with corrensponding response for error
     **/
    public String registerUser(String firstName, String lastName, String password, String email) {

        RealmResource realmResource = this.keycloak.realm(keycloakConfig.getRealm());
        UsersResource userResource = realmResource.users();

        // create proper credentials
        CredentialRepresentation passwordCredentials = createPasswordCredentials(password);

        // create user
        UserRepresentation user = new UserRepresentation();
        user.setUsername(email);
        user.setCredentials(Collections.singletonList(passwordCredentials));
        user.setFirstName(firstName);
        user.setLastName(lastName);
        user.setEmail(email);
        user.setRequiredActions(Collections.emptyList());
        user.setEnabled(true);
        user.setEmailVerified(false);

        // extract identifier of user
        Response response = userResource.create(user);
        String userId = extractCreatedId(response);
        UserResource createdUser = fetchUserResource(userId);

        // ToDo: throw exception if user already exists

        // set password
        createdUser.resetPassword(passwordCredentials);

//        // send verification mail
//        createdUser.executeActionsEmail(oAuthClientConfig.getCliendId(), "http://localhost:102", Collections.singletonList("VERIFY_EMAIL"));
//        createdUser.sendVerifyEmail(oAuthClientConfig.getCliendId());

        addRole(userId, NIMBLE_USER_ROLE);

        return createdUser.toRepresentation().getId();
    }

    public void deleteUser(String externalId) {
        RealmResource realmResource = this.keycloak.realm(keycloakConfig.getRealm());
        UsersResource userResource = realmResource.users();

        // delete user
        userResource.delete(externalId);
    }

    public void deleteUserByUsername(String username) {
        RealmResource realmResource = this.keycloak.realm(keycloakConfig.getRealm());
        UsersResource userResource = realmResource.users();

        List<UserRepresentation> userRepresentations = userResource.search(username);
        userRepresentations.forEach(userRepresentation -> userResource.delete(userRepresentation.getId()));
    }

    public Map<String, String> getAssignableRoles() {
        RealmResource realmResource = this.keycloak.realm(keycloakConfig.getRealm());

        return realmResource.roles().list().stream()
                .filter(r -> NON_ASSIGNABLE_ROLES.contains(r.getName()) == false)
                .collect(Collectors.toMap(r -> r.getId(), r -> r.getName()));
    }

    public Set<String> getUserRoles(String userId) {
        return getUserRoles(userId, new ArrayList<>());
    }

    public Set<String> getUserRoles(String userId, List<String> excludeRoles) {

        List<String> finalExcludeRoles = (excludeRoles == null) ? new ArrayList<>() : excludeRoles;
        UserResource userResource = fetchUserResource(userId);
        return userResource.roles().realmLevel().listAll().stream()
                .map(r -> r.getName())
                .filter(role -> finalExcludeRoles.contains(role) == false)
                .collect(Collectors.toSet());
    }

    public UserResource getUserResource(String userId) {
        return fetchUserResource(userId);
    }

    public String getEFactoryUserId(UserResource userResource){
        for (FederatedIdentityRepresentation federatedIdentityRepresentation : userResource.getFederatedIdentity()) {
            if(federatedIdentityRepresentation.getIdentityProvider().contentEquals(eFactoryIdentityProvider)){
                    return federatedIdentityRepresentation.getUserId();
            }
        }
        return null;
    }
    public void addRole(String userId, String role) {
        UserResource userResource = fetchUserResource(userId);

        RealmResource realmResource = this.keycloak.realm(keycloakConfig.getRealm());
        RoleRepresentation roleRepresentation = realmResource.roles().get(role).toRepresentation();
        userResource.roles().realmLevel().add(Collections.singletonList(roleRepresentation));
    }

    public void removeRole(String userId, String role) {
        UserResource userResource = fetchUserResource(userId);

        RealmResource realmResource = this.keycloak.realm(keycloakConfig.getRealm());
        RoleRepresentation roleRepresentation = realmResource.roles().get(role).toRepresentation();
        userResource.roles().realmLevel().remove(Collections.singletonList(roleRepresentation));
    }

    public List<UserRepresentation> getPlatformManagers() {

        RealmResource realmResource = this.keycloak.realm(keycloakConfig.getRealm());
        List<GroupRepresentation> groups = realmResource.groups().groups();

        Optional<GroupRepresentation> platformManagerGroup = groups.stream().filter(g -> "Platform Manager".equals(g.getName())).findFirst();
        if (platformManagerGroup.isPresent() == false) {
            logger.warn("No platform managers found!");
            return new ArrayList<>();  // empty list as fallback
        }
        return realmResource.groups().group(platformManagerGroup.get().getId()).members();
    }

    private UserResource fetchUserResource(String userId) {
        RealmResource realmResource = this.keycloak.realm(keycloakConfig.getRealm());
        return realmResource.users().get(userId);
    }

    public boolean resetPassword(UaaUser user, String oldPassword, String newPassword) {

        try {
            // verify existing password
            OAuth2AccessToken accessToken = oAuthClient.getToken(user.getUsername(), oldPassword);
            if (accessToken == null)
                return false;

            // set new password
            CredentialRepresentation passwordCredential = createPasswordCredentials(newPassword);
            this.keycloak.realm(keycloakConfig.getRealm()).users().get(user.getExternalID()).resetPassword(passwordCredential);
        } catch (OAuth2AccessDeniedException ex) {
            logger.info("Authentication error while setting new password");
            return false;
        }

        return true;
    }

    private String extractCreatedId(Response response) {
        URI location = response.getLocation();
        if (!response.getStatusInfo().equals(Response.Status.CREATED)) {
            Response.StatusType statusInfo = response.getStatusInfo();
            throw new WebApplicationException("Create method returned status " +
                    statusInfo.getReasonPhrase() + " (Code: " + statusInfo.getStatusCode() + "); expected status: Created (201)",
                    statusInfo.getStatusCode());
        }
        if (location == null) {
            return null;
        }
        String path = location.getPath();
        return path.substring(path.lastIndexOf('/') + 1);
    }

    public int applyRoles(String userID, Set<String> rolesToApply) {
        // setting proper set of roles
        Set<String> currentRoles = getUserRoles(userID, NON_ASSIGNABLE_ROLES);
        Set<String> rolesToRemove = Sets.difference(currentRoles, rolesToApply);
        Set<String> rolesToAdd = Sets.difference(rolesToApply, currentRoles);
        logger.info("Applying new roles to user {}: add: {}, remove: {}", userID, rolesToAdd, rolesToRemove);
        for (String role : rolesToRemove)
            removeRole(userID, role);
        for (String role : rolesToAdd)
            addRole(userID, role);
        return rolesToAdd.size() + rolesToRemove.size();
    }

    public static List<String> prettfiyRoleIDs(List<String> roleIDs) {
        return roleIDs.stream().map(r -> WordUtils.capitalize(r.replace("_", " "))).collect(Collectors.toList());
    }

    private static CredentialRepresentation createPasswordCredentials(String password) {
        CredentialRepresentation passwordCredentials = new CredentialRepresentation();
        passwordCredentials.setTemporary(false);
        passwordCredentials.setType(CredentialRepresentation.PASSWORD);
        passwordCredentials.setValue(password);
        return passwordCredentials;
    }
} 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());
    }
}

import com.fasterxml.jackson.core.JsonProcessingException;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import eu.nimble.core.infrastructure.identity.clients.IndexingClient;
import eu.nimble.core.infrastructure.identity.clients.IndexingClientController;
import eu.nimble.core.infrastructure.identity.entity.CompanyDetailsUpdates;
import eu.nimble.core.infrastructure.identity.entity.NegotiationSettings;
import eu.nimble.core.infrastructure.identity.entity.UaaUser;
import eu.nimble.core.infrastructure.identity.entity.dto.CompanyDetails;
import eu.nimble.core.infrastructure.identity.entity.dto.CompanySettings;
import eu.nimble.core.infrastructure.identity.mail.EmailService;
import eu.nimble.core.infrastructure.identity.messaging.KafkaSender;
import eu.nimble.core.infrastructure.identity.repository.*;
import eu.nimble.core.infrastructure.identity.service.AdminService;
import eu.nimble.core.infrastructure.identity.service.CertificateService;
import eu.nimble.core.infrastructure.identity.service.IdentityService;
import eu.nimble.core.infrastructure.identity.uaa.KeycloakAdmin;
import eu.nimble.core.infrastructure.identity.utils.*;
import eu.nimble.service.model.ubl.commonaggregatecomponents.*;
import eu.nimble.service.model.ubl.commonbasiccomponents.BinaryObjectType;
import eu.nimble.service.model.ubl.commonbasiccomponents.CodeType;
import eu.nimble.utility.ExecutionContext;
import eu.nimble.utility.JsonSerializationUtility;
import eu.nimble.utility.persistence.binary.BinaryContentService;
import feign.Response;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.keycloak.representations.idm.UserRepresentation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.thymeleaf.util.StringUtils;
import java.io.IOException;
import java.math.BigInteger;
import java.util.*;
import java.util.stream.Collectors;
import static eu.nimble.core.infrastructure.identity.uaa.OAuthClient.Role.*;
import static eu.nimble.core.infrastructure.identity.utils.UblAdapter.*;
import static eu.nimble.service.model.ubl.extension.QualityIndicatorParameter.*;
import static eu.nimble.utility.HttpResponseUtil.createResponseEntityAndLog;
package eu.nimble.core.infrastructure.identity.system;




/**
 * Created by Johannes Innerbichler on 04/07/17.
 */
@RestController
@RequestMapping("/company-settings")
@SuppressWarnings({"SpringJavaAutowiredFieldsWarningInspection", "FieldCanBeLocal"})
@Api(value = "company-settings", description = "API for handling settings of companies.")
public class CompanySettingsController {

    private final Long MAX_IMAGE_SIZE = 10 * 1024L * 1024L; // in bytes

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

    @Autowired
    private PartyRepository partyRepository;

    @Autowired
    private QualifyingPartyRepository qualifyingPartyRepository;

    @Autowired
    private CertificateRepository certificateRepository;

    @Autowired
    private DocumentReferenceRepository documentReferenceRepository;

    @Autowired
    private NegotiationSettingsRepository negotiationSettingsRepository;

    @Autowired
    private IdentityService identityService;

    @Autowired
    private CertificateService certificateService;

    private BinaryContentService binaryContentService = new BinaryContentService();

    @Autowired
    private IndexingClientController indexingController;

    @Autowired
    private AdminService adminService;

    @Autowired
    private KeycloakAdmin keycloakAdmin;

    @Autowired
    private EmailService emailService;