package no.valg.valgnatt.frontend;

import java.io.IOException;
import java.net.URLDecoder;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;

@Component
public class Kontroller {

	private static final int SIFFER_I_ÅRSTALL = 4;
	private static final Logger LOG = LogManager.getLogger(Kontroller.class);
	private static final int SERVERINSTANS_INDEKS = 1;
	private static final int VALGÅR_INDEKS = 2;
	private static final int STATUS_INDEKS = 3;
	private RapportCache rapportCache;
	private ToppnivåRapport toppnivåRapport;

	private long serverInstansId = 0;
	private int sisteSekvensnr = 0;
	private int backendValgår = 0;
	private boolean sperrefrist = true;

	// eksempler på requestStr (se RapportMottaker.sjekkStatus())
	// status:3456:2013:publiser (frontend mottar resultater)
	// status:0931:2017:sperrefrist (frontend mottar ingenting)
	// status:0931:2017:sperrefrist-nullrapporter (frontend mottar nullrapporter - det har ikke kommet inn stemmer ennå)
	String prosesserStatus(String requestStr) throws IOException {
		LOG.info("Backend-server sjekker status på Frontend: " + requestStr.trim());
		String[] arr = requestStr.split(":");
		long nyServerInstansId = new Long(arr[SERVERINSTANS_INDEKS]);
		backendValgår = new Integer(arr[VALGÅR_INDEKS]);
		boolean nySperrefrist = !"publiser".equals(arr[STATUS_INDEKS].trim());
		if (omstart(nyServerInstansId, nySperrefrist)) {
			tilbakestillStatus(nyServerInstansId, nySperrefrist, backendValgår);
			initialiser();
		}
		return Integer.toString(sisteSekvensnr);
	}

	protected boolean omstart(long nyServerInstansId, boolean nySperrefrist) {
		boolean omstart = false;
		if (serverInstansId == 0) {
			LOG.warn("Resetter status fordi serverInstansId = 0, dvs. første kontakt med Backend oppnås.");
			omstart = true;
		} else if (nyServerInstansId != serverInstansId) {
			LOG.warn("Resetter status fordi nyServerInstansId != serverInstansId (" + nyServerInstansId + " !=" + serverInstansId + ")");
			omstart = true;
		} else if (nySperrefrist != sperrefrist) {
			LOG.warn("Resetter status fordi nySperrefrist != sperrefrist (" + nySperrefrist + " !=" + sperrefrist + ")");
			omstart = true;
		}
		return omstart;
	}

	private void tilbakestillStatus(long nyesteServerInstansId, boolean nySperrefrist, int backendValgår) throws IOException {
		sisteSekvensnr = 0;
		sperrefrist = nySperrefrist;
		serverInstansId = nyesteServerInstansId;
		if (sperrefrist) {
			RapportSamlingslager.navngiVanligRapportSlikAtDenIkkeLengerBlirFunnet(backendValgår);
		}
	}

	void initialiser() throws IOException {
		LOG.info("Initialisering av server - ny cache og innlesing av lagrede rapportsamlinger");
		RapportCache tmpCache = new RapportCache();
		for (int valgår : RapportSamlingslager.lagredeValgår()) {
			RapportSamling rs = RapportSamlingslager.les(valgår, valgår == backendValgår && sperrefrist);
			if (rs != null) {
				tmpCache.addRapportSamlingTilCache(rs);
			}
		}
		toppnivåRapport = new ToppnivåRapport();
		rapportCache = tmpCache;
	}

	void setStatusVedOppstart() {
		Integer valgårSomHarNullrapportOgIkkeVanligRapport = RapportSamlingslager.finnValgårSomHarNullrapportOgIkkeVanligRapport();
		if (valgårSomHarNullrapportOgIkkeVanligRapport != null) {
			backendValgår = valgårSomHarNullrapportOgIkkeVanligRapport;
			sperrefrist = true;
		} else {
			backendValgår = 0;
			sperrefrist = false;
		}
		serverInstansId = 0;
		sisteSekvensnr = 0;
	}

	String prosesserRapporterFraBackend(String json) throws IOException {

		RapportSamling rapportSamling = new RapportSamling(json, true);
		sisteSekvensnr = rapportSamling.getSisteSekvensnr();
		if (rapportSamling.getAntallRapporter() > 0) {
			LOG.info("Rapporter mottatt: serverInstansId=" + serverInstansId + ", antall=" + rapportSamling.getAntallRapporter() + ", siste rapport-id er nå " +
					sisteSekvensnr);
		}

		// Oppdater cache og persister rapportsamling basert på alt som ligger i cache
		rapportCache.addRapportSamlingTilCache(rapportSamling);
		int valgår = rapportSamling.getValgår();
		boolean stemmetallMottatt = rapportSamling.erStemmetallMottatt();
		RapportSamling rapportSamlingByggetFraCache = new RapportSamling(rapportCache.getRapporterFraIdCache(valgår), valgår, stemmetallMottatt);
		RapportSamlingslager.lagre(rapportSamlingByggetFraCache);

		return Integer.toString(sisteSekvensnr);
	}


	String getRapport(String path) {
		try {
			if ("/".equals(path)) {
				return toppnivåRapport.getToppnivå(rapportCache);
			} else if (path.length() <= SIFFER_I_ÅRSTALL + 2) { // Vi kan ha '/' på begge sider av årstallet i path
				int valgår = Integer.parseInt(path.substring(1, SIFFER_I_ÅRSTALL + 1));
				return toppnivåRapport.getToppnivåValgår(valgår, rapportCache.getRapporterFraIdCache(valgår).keySet());
			} else { // returner faktisk rapport
				return rapportCache.getRapportFraCache(URLDecoder.decode(path, "UTF-8"));
			}
		} catch (Exception e) {
			LOG.warn("Feil oppstod ved forespørsel etter rapport med URL " + path + ": " + e.getClass() + " : " + e.getMessage());
			LOG.debug(e);
			return null;
		}
	}

	String getSøketreff(String valgår, String valgtype, String søkeStreng) {
		try {
			return rapportCache.søkEtterRapporterFraNavnCache(Integer.parseInt(valgår), valgtype, søkeStreng);
		} catch (Exception e) {
			LOG.warn("Feil oppstod ved forespørsel etter rapport med søkeparametre: " + valgår + ", " + valgtype + " og " + søkeStreng + " : " + e.getClass() + " : " +
					e.getMessage());
			LOG.debug(e);
			return null;
		}
	}

	/**
	 * Denne listen av rapporter trengs bare til Simulator (som kjører valgsimuleringer) - se valg_utils-pakken
	 */
	String getSimRapporter() {
		Set<String> simRapporter = new TreeSet<>(new RapportComparator());
		Map<String, Map<String, String>> rapporterForBackendValgår = rapportCache.getRapporterFraIdCache(backendValgår);
		for (String cacheNøkkel : rapporterForBackendValgår.keySet()) {
			if ("totalrapport".equals(cacheNøkkel)) {
				continue;
			}
			simRapporter.addAll(rapporterForBackendValgår.get(cacheNøkkel).keySet());
		}
		StringBuilder sb = new StringBuilder();
		for (String sr : simRapporter) {
			sb.append(sr).append("\n");
		}
		return sb.toString();
	}

	boolean erSperrefrist(String path) {
		if (path != null && path.length() > SIFFER_I_ÅRSTALL) {
			try {
				return sperrefrist && backendValgår == Integer.parseInt(path.substring(0, SIFFER_I_ÅRSTALL));
			} catch (NumberFormatException nfe) {
				// Trenger ikke håndtere dette, man spør da etter en ugyldig URL - og da defaulter man til false
			}
		}
		return false;
	}
}
