import java.util.Map.Entry

import org.slf4j.Logger
import org.slf4j.LoggerFactory

import com.gridnine.xtrip.common.model.EntityContainer
import com.gridnine.xtrip.common.model.EntityReference
import com.gridnine.xtrip.common.model.dict.ContractType
import com.gridnine.xtrip.common.model.entity.EntityStorage
import com.gridnine.xtrip.common.model.profile.BaseProfile
import com.gridnine.xtrip.common.model.profile.CardIndex
import com.gridnine.xtrip.common.model.profile.Contract
import com.gridnine.xtrip.common.model.profile.ContractCustomerIndex
import com.gridnine.xtrip.common.model.profile.Organization
import com.gridnine.xtrip.common.model.profile.OrganizationType
import com.gridnine.xtrip.common.model.profile.Person
import com.gridnine.xtrip.common.model.profile.PersonEmployment
import com.gridnine.xtrip.common.search.SearchCriterion
import com.gridnine.xtrip.common.search.SearchQuery
import com.gridnine.xtrip.common.search.SortOrder

Logger logger = LoggerFactory.getLogger('groovy-script')

def ownerByUid = { CardIndex index ->
    
    def uid = ''

    EntityReference<Organization> organization = EntityStorage.get().actualize(new EntityReference<Organization>(uid, Organization.class, null))

    return organization
}

def ownerByParent = { CardIndex index ->
    
    EntityReference<Organization> organization = null

    if (Person.class.isAssignableFrom(index.getSource().getType())) {

        EntityContainer<Person> personContainer = EntityStorage.get().resolve((EntityReference<Person>) index.getSource())

        if(personContainer != null) {

            Person person = personContainer.getEntity()

            for(PersonEmployment employment : person.getEmployments()) {

                if(employment.getOrganization() != null && (organization == null || employment.isMainEmployment())) {
                    organization = employment.getOrganization()
                }
            }
            
        } else {
            logger.info("CIR: unable to load person " + index.getSource() + "(" + index.getSource().getUid() + ")")
        }
        
    } else if(Organization.class.isAssignableFrom(index.getSource().getType())) {
        organization = (EntityReference<Organization>) index.getSource()
    }

    if(organization != null) {
        
        logger.info("CIR: found organization " + organization + " for " + index.getSource() + " (" + index.getSource().getUid() + ")")
        
        List<String> uids = new ArrayList<>()
        
        while(true) {

            if(uids.contains(organization.getUid())) {

                logger.info("CIR: parent loop found for " + index.getSource() + "(" + index.getSource().getUid() + ")")
                break
            }

            uids.add(organization.getUid())

            EntityContainer<Organization> organizationContainer = EntityStorage.get().resolve(organization)

            if(organizationContainer != null && !organizationContainer.getEntity().getTypes().contains(OrganizationType.AGENCY) && organizationContainer.getEntity().getParent() != null) {
                organization = organizationContainer.getEntity().getParent()
            } else {
                break
            }
        }

    } else {
        logger.info("CIR: unable to define organization for " + index.getSource() + " (" + index.getSource().getUid() + ")")
    }

    return organization
}

def ownerByContract = { CardIndex index ->
    
    EntityReference<Organization> organization = null

    if (Person.class.isAssignableFrom(index.getSource().getType())) {

        EntityContainer<Person> personContainer = EntityStorage.get().resolve((EntityReference<Person>) index.getSource())

        if(personContainer != null) {

            Person person = personContainer.getEntity()

            for(PersonEmployment employment : person.getEmployments()) {

                if(employment.getOrganization() != null && (organization == null || employment.isMainEmployment())) {
                    organization = employment.getOrganization()
                }
            }
            
        } else {
            logger.info("CIR: unable to load person " + index.getSource() + " (" + index.getSource().getUid() + ")")
        }
        
    } else if(Organization.class.isAssignableFrom(index.getSource().getType())) {
        organization = (EntityReference<Organization>) index.getSource()
    }

    if(organization != null) {
        
        logger.info("CIR: found organization " + organization + " for " + index.getSource() + " (" + index.getSource().getUid() + ")")

        EntityContainer<Organization> organizationContainer = EntityStorage.get().resolve(organization)

        if(organizationContainer != null && organizationContainer.getEntity().getTypes().contains(OrganizationType.CORPORATE_CLIENT)) {

            logger.info("CIR: organization is client")

            query = new SearchQuery()

            query.getCriteria().getCriterions().add(SearchCriterion.eq(ContractCustomerIndex.Property.customer.name(), organization))
            query.getCriteria().getCriterions().add(SearchCriterion.eq(ContractCustomerIndex.Property.contractType.name(), ContractType.CLIENT))
            query.getCriteria().getCriterions().add(SearchCriterion.ne(ContractCustomerIndex.Property.supplier.name(), null))

            query.getCriteria().getOrders().put("containerUid", SortOrder.ASC)

            query.getPreferredProperties().add("containerUid")

            List<ContractCustomerIndex> contractIndexes = EntityStorage.get().search(ContractCustomerIndex.class, query).getData()

            logger.info("CIR: found contracts " + contractIndexes.size())

            for(ContractCustomerIndex contractIndex : contractIndexes) {

                EntityContainer<Contract> contractContainer = EntityStorage.get().resolve(contractIndex.getSource())

                if(contractContainer != null) {

                    Contract contract = contractContainer.getEntity()

                    if(contract.getSupplier() != null) {

                        organization = contract.getSupplier()

                        logger.info("CIR: found contract supplier " + organization)

                        break
                    }
                }
            }
        }
        
    } else {
        logger.info("CIR: unable to define organization for " + index.getSource() + " (" + index.getSource().getUid() + ")")
    }

    return organization
}

logger.info("CIR: statistics started")

int processed = 0
int loops = 0

Map<EntityReference<Organization>, List<EntityReference<BaseProfile>>> organizations = new LinkedHashMap()

try {

    SearchQuery query = new SearchQuery()

    query.getCriteria().getOrders().put("containerUid", SortOrder.ASC)

    query.getPreferredProperties().add("containerUid")

    List<CardIndex> indexes = EntityStorage.get().search(CardIndex.class, query).getData()

    logger.info("CIR: found " + indexes.size() + " cards")

    for(CardIndex index : indexes) {

        logger.info("CIR: processing " + processed + " | " + index.getSource().getUid())

        EntityReference<Organization> owner = ownerByParent(index)

        List<EntityReference<BaseProfile>> profiles = organizations.get(owner)

        if(profiles == null) {

            profiles = new ArrayList<EntityReference<BaseProfile>>()
            organizations.put(owner, profiles)
        }

        if(!profiles.contains(index.getSource())) {
            profiles.add(index.getSource())
        }

        if(owner != null) {
            logger.info("CIR: organization found " + owner + " (" + owner.getUid() + ")")
        } else {
            logger.info("CIR: organization not found")
        }

        processed++
    }
    
} catch(Throwable t) {
    logger.error('', t)
}

logger.info("=================================")

for(Entry<EntityReference<Organization>, List<EntityReference<BaseProfile>>> entry : organizations.entrySet()) {

    logger.info("CIR: " + entry.getKey())

    for(EntityReference<BaseProfile> profile : entry.getValue()) {
        logger.info("CIR: \t" + profile + " (" + profile.getUid() + ")")
    }
}

logger.info("=================================")

for(EntityReference<Organization> organization : organizations.keySet()) {
    
    if(organization != null) {
        logger.info("CIR: organization " + organization + " (" + organization.getUid() + ")")
    } else {
        logger.info("CIR: organization " + organization)
    }
}

logger.info("=================================")

logger.info("CIR: processed " + processed)

logger.info("CIR: statistics finished")
