package org.slc.sli.ingestion.tenant;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;

import org.apache.commons.io.IOUtils;
import org.slc.sli.ingestion.routes.IngestionRouteBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;

/**
 * Populates the tenant database collection with default tenant collections.
 *
 * @author jtully
 */
@Component
public class TenantPopulator implements ResourceLoaderAware {

    Logger log = LoggerFactory.getLogger(IngestionRouteBuilder.class);
    
    @Autowired
    private TenantDA tenantDA;

    private ResourceLoader resourceLoader;

    private String parentLandingZoneDir;
    
    private List<String> tenantRecordResourcePaths;

    private static final String HOSTNAME_PLACEHOLDER = "<hostname>";
    private static final String PARENT_LZ_PATH_PLACEHOLDER = "<lzpath>";

    /**
     * Add a specified tenantRecord to the tenant collection.
     * 
     * @param tenantRecord, the record to add to the collection.
     * @param deriveIngestionFields, whether to derive ingestion-specific fields.
     */
    public boolean addTenant(TenantRecord tenantRecord, boolean deriveIngestionFields) {
        try {
            String hostname = getHostname();
            
            if (deriveIngestionFields) { 
                deriveTenantFields(tenantRecord, hostname);
            }
            tenantDA.insertTenant(tenantRecord);
            
        } catch (Exception e) {
            log.error("Exception adding tenant " + tenantRecord + " :", e);
            return false;
        }
        return true;
    }
    
    /**
     * Populate the tenant data store with a default set of tenants.
     *
     */
    public void populateDefaultTenants() {
        try {
            String hostName = InetAddress.getLocalHost().getHostName();
            List<TenantRecord> tenants = constructDefaultTenantCollection(hostName);
            tenantDA.dropTenants();
            for (TenantRecord tenant : tenants) {
                tenantDA.insertTenant(tenant);
            }
        } catch (Exception e) {
            log.error("Exception encountered populating default tenants:", e);
        }
    }
    
    /**
     * Construct the default tenant collection based on the configured TenantRecord resources.
     *
     * @param hostName
     * @return a list of constructed TenantRecord objects
     */
    private List<TenantRecord> constructDefaultTenantCollection(String hostname) {
        List<TenantRecord> tenants = new ArrayList<TenantRecord>();
        for (String tenantResourcePath : tenantRecordResourcePaths) {
            TenantRecord tenant = loadTenant(tenantResourcePath);
            replaceTenantPlaceholderFields(tenant, hostname);
            createTenantLzDirectory(tenant);
            tenants.add(tenant);
        }
        return tenants;
    }

    /**
     *
     * Create the landing zone directory for a tenant.
     *
     * @param tenant, the tenant for which to create landing zone directories
     *
     */
    private void createTenantLzDirectory(TenantRecord tenant) {
        List<LandingZoneRecord> landingZones = tenant.getLandingZone();
        for (LandingZoneRecord lz : landingZones) {
            String lzPath = lz.getPath();
            File lzDirectory = new File(lzPath);
            lzDirectory.mkdir();
            lzDirectory.setReadable(true, false);
            lzDirectory.setWritable(true, false);
        }
    }

    /**
     *
     * Process TenantRecord, Replacing hostname and lzPath placeholder fields with
     * the actual values.
     *
     * @param tenant record to be processed
     * @param hostname the hostname to be used in placeholder replacement
     */
    private void replaceTenantPlaceholderFields(TenantRecord tenant, String hostname) {
        List<LandingZoneRecord> landingZones = tenant.getLandingZone();
        for (LandingZoneRecord lz : landingZones) {
            //replace hostname field
            String serverVal = lz.getIngestionServer();
            serverVal = serverVal.replaceFirst(HOSTNAME_PLACEHOLDER, hostname);
            lz.setIngestionServer(serverVal);

            String pathVal = lz.getPath();
            pathVal = pathVal.replaceFirst(PARENT_LZ_PATH_PLACEHOLDER, Matcher.quoteReplacement(parentLandingZoneDir));
            lz.setPath(new File(pathVal).getAbsolutePath());
        }
    }
    
    /**
    *
    * Derive and set ingestion specific fields:
    *  landingZone.ingestionServer and landingZone.path.
    *
    * @param tenant record to be processed
    * @param hostname the hostname to be used in placeholder replacement
    */
   private void deriveTenantFields(TenantRecord tenant, String hostname) {
       List<LandingZoneRecord> landingZones = tenant.getLandingZone();
       for (LandingZoneRecord lz : landingZones) {
           //override hostname field
           lz.setIngestionServer(hostname);
           
           //override path field
           String pathVal = parentLandingZoneDir + "/" + tenant.getTenantId() + "-" + lz.getEducationOrganization();
           lz.setPath(new File(pathVal).getAbsolutePath());
       }
   }

    /**
     * Loads a TenantRecord from a tenant resource
     * @param resourcePath to load into a tenant record
     * @return the loaded tenant
     */
    private TenantRecord loadTenant(String resourcePath) {
        TenantRecord tenant = null;
        InputStream tenantIs = null;
        try {
            Resource config = resourceLoader.getResource(resourcePath);

            if (config.exists()) {
                tenantIs = config.getInputStream();
                tenant = TenantRecord.parse(tenantIs);
            }
        } catch (IOException e) {
            log.error("Exception encountered loading tenant resource: ", e);
        } finally {
            IOUtils.closeQuietly(tenantIs);
        }
        return tenant;
    }
    
    /**
     * Obtain the hostname for the ingestion server running
     * @throws UnknownHostException 
     */
    private String getHostname() throws UnknownHostException {
        return InetAddress.getLocalHost().getHostName();
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    /**
     * @return the parentLandingZoneDir
     */
    public String getParentLandingZoneDir() {
        return parentLandingZoneDir;
    }

    /**
     * @param parentLandingZoneDir the parentLandingZoneDir to set
     */
    public void setParentLandingZoneDir(String singleLandingZoneDir) {
        File lzFile = new File(singleLandingZoneDir);
        this.parentLandingZoneDir = lzFile.getAbsolutePath();
    }

    public List<String> getTenantRecordResourcePaths() {
        return tenantRecordResourcePaths;
    }

    public void setTenantRecordResourcePaths(List<String> tenantRecordResourcePaths) {
        this.tenantRecordResourcePaths = tenantRecordResourcePaths;
    }


}

@@ -2233,24 +2233,63 @@
 HostName();%0A
+            createParentLzDirectory();%0A
             
@@ -3299,32 +3299,427 @@
 tenants;%0A    %7D%0A%0A
+    /**%0A     * %0A     * Create the landing zone directory for the parent landing zone%0A     * %0A     */%0A    private void createParentLzDirectory() %7B%0A        String lzPath = Matcher.quoteReplacement(parentLandingZoneDir);%0A        File lzDirectory = new File(lzPath);%0A        lzDirectory.mkdir();%0A        lzDirectory.setReadable(true, false);%0A        lzDirectory.setWritable(true, false);%0A    %7D%0A    %0A
     /**%0A     *%0A 

Fix for parent landing zone directory permissions - required for ingestion tenant acceptance test.