package com.indeed.proctor.webapp.controllers;

import com.google.common.collect.ImmutableMap;
import com.indeed.proctor.common.model.TestDefinition;
import com.indeed.proctor.common.model.TestMatrixVersion;
import com.indeed.proctor.store.ProctorStore;
import com.indeed.proctor.store.Revision;
import com.indeed.proctor.store.StoreException;
import com.indeed.proctor.webapp.db.Environment;
import com.indeed.proctor.webapp.model.WebappConfiguration;

import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
import java.util.Map;

/**
 * @author parker
 */
public class AbstractController {
    private final WebappConfiguration configuration;
    private final Map<Environment, ProctorStore> stores;

    public AbstractController(final WebappConfiguration configuration,
                              final ProctorStore trunkStore,
                              final ProctorStore qaStore,
                              final ProctorStore productionStore) {
        this.configuration = configuration;
        stores = ImmutableMap.of(
            Environment.WORKING, trunkStore,
            Environment.QA, qaStore,
            Environment.PRODUCTION, productionStore
        );
    }

    protected static String printThrowable(final Throwable t) {
        final StringWriter sw = new StringWriter();
        final PrintWriter pw = new PrintWriter(sw);
        t.printStackTrace(pw);
        pw.close();
        return sw.toString();
    }

    protected static boolean isAJAXRequest(final HttpServletRequest request) {
        final String xhrHeader = request.getHeader("X-Requested-With");
        if ("XMLHttpRequest".equals(xhrHeader)) {
            return true;
        }
        // For redirects in an AJAX request, it's common to append parameter
        final String xhrParameter = request.getParameter("X-Requested-With");
        return "XMLHttpRequest".equals(xhrHeader);
    }

    protected Environment determineEnvironmentFromParameter(String branch) {
        final Environment b = Environment.fromName(branch);
        return b != null ? b : Environment.WORKING;
    }

    protected WebappConfiguration getConfiguration() {
        return configuration;
    }

    protected ProctorStore determineStoreFromEnvironment(final Environment branch) {
        final ProctorStore store = stores.get(branch);
        if(store == null) {
            throw new RuntimeException("Unknown store for branch " + branch);
        }
        return store;
    }

    protected TestMatrixVersion getCurrentMatrix(final Environment branch) {
        try {
            return determineStoreFromEnvironment(branch).getCurrentTestMatrix();
        } catch (StoreException e) {
            return null;
        }
    }

    protected List<Revision> queryMatrixHistory(final Environment branch, final int start, final int limit) throws StoreException {
        return determineStoreFromEnvironment(branch).getMatrixHistory(start, limit);
    }

    protected TestMatrixVersion queryMatrixFromBranchOrRevision(final String branchOrRevision) throws StoreException {
        final BranchOrRevision bor = new BranchOrRevision(branchOrRevision);
        return bor.queryTestMatrixVersion();
    }

    protected TestDefinition queryTestDefinition(final String branchOrRevision, final String testName) throws StoreException {
        final BranchOrRevision bor = new BranchOrRevision(branchOrRevision);
        return bor.queryTestDefinition(testName);
    }

    protected List<Revision> queryTestDefinitionHistory(final String branchOrRevision, final String testName, final int start, final int limit) throws StoreException {
        final BranchOrRevision bor = new BranchOrRevision(branchOrRevision);
        return bor.queryTestDefinitionHistory(testName, start, limit);
    }

    @Nullable
    private ProctorStore determineStoreFromRevision(final String revision) throws StoreException {
        for (final ProctorStore store : stores.values()) {
            if (store.getTestMatrix(revision) != null) {
                return store;
            }
        }
        return null;
    }

    private class BranchOrRevision {
        final String stringValue;
        @Nullable
        final Environment branch;
        final ProctorStore store;

        private BranchOrRevision(final String branchOrRevision) throws StoreException {
            stringValue = branchOrRevision;
            branch = Environment.fromName(stringValue);
            if (isBranch()) {
                store = determineStoreFromEnvironment(branch);
            } else {
                store = determineStoreFromRevision(stringValue);
            }

            if (store == null) {
                throw new StoreException("Invalid branch or revision name "+stringValue);
            }
        }

        private boolean isBranch() {
            return null != branch;
        }

        private TestMatrixVersion queryTestMatrixVersion() throws StoreException {
            if (isBranch()) {
                return store.getCurrentTestMatrix();
            } else {
                return store.getTestMatrix(stringValue);
            }
        }

        private TestDefinition queryTestDefinition(final String testName) throws StoreException {
            if (isBranch()) {
                return store.getCurrentTestDefinition(testName);
            } else {
                return store.getTestDefinition(testName, stringValue);
            }
        }

        private List<Revision> queryTestDefinitionHistory(final String testName, final int start, final int limit) throws StoreException {
            if (isBranch()) {
                return store.getHistory(testName, start, limit);
            } else {
                return store.getHistory(testName, stringValue, start, limit);
            }
        }
    }
}

@@ -445,16 +445,86 @@
 uration;
+%0Aimport org.apache.log4j.Logger;%0Aimport org.eclipse.jgit.lib.ObjectId;
 %0A%0Aimport
@@ -760,16 +760,102 @@
 oller %7B%0A
+    private static final Logger LOGGER = Logger.getLogger(AbstractController.class);%0A%0A
     priv
@@ -1297,32 +1297,36 @@
 of(%0A            
+    
 Environment.WORK
@@ -1346,32 +1346,36 @@
 re,%0A            
+    
 Environment.QA, 
@@ -1375,32 +1375,36 @@
 nt.QA, qaStore,%0A
+    
             Envi
@@ -2600,16 +2600,17 @@
       if
+ 
 (store =
@@ -4131,38 +4131,16 @@
 evision)
- throws StoreException
  %7B%0A     
@@ -4189,24 +4189,46 @@
 values()) %7B%0A
+            try %7B%0A    
             
@@ -4280,32 +4280,36 @@
                 
+    
 return store;%0A  
@@ -4302,24 +4302,196 @@
 turn store;%0A
+                %7D%0A            %7D catch (final StoreException e) %7B%0A                LOGGER.info(String.format(%22Failed to find revision %25s in %25s%22, revision, store.getName()));%0A
             
@@ -4605,26 +4605,8 @@
 ue;%0A
-        @Nullable%0A
     
@@ -4961,32 +4961,64 @@
           %7D else
+ if (ObjectId.isId(stringValue))
  %7B%0A             
@@ -5074,32 +5074,83 @@
 );%0A            %7D
+ else %7B%0A                store = null;%0A            %7D
 %0A%0A            if
@@ -5243,17 +5243,19 @@
 n name %22
-+
+ + 
 stringVa

resolve logic error in handling bad input.