package no.valg.valgnatt.database;

import no.valg.valgnatt.database.dao.BydelDao;
import no.valg.valgnatt.database.dao.KandidatDao;
import no.valg.valgnatt.database.dao.KommuneDao;
import no.valg.valgnatt.database.dao.MandatberegningDao;
import no.valg.valgnatt.database.dao.PartiListeDao;
import no.valg.valgnatt.database.dao.StemmekretsDao;
import no.valg.valgnatt.database.dao.TilstandDao;
import no.valg.valgnatt.database.dao.ValgdistriktDao;
import no.valg.valgnatt.database.domene.ValgType;
import no.valg.valgnatt.database.domene.ValgTypeAar;
import no.valg.valgnatt.database.geografi.Land;
import no.valg.valgnatt.database.geografi.MandatData;
import no.valg.valgnatt.database.geografi.Mandater;
import no.valg.valgnatt.database.geografi.PartiMandater;
import no.valg.valgnatt.database.hibernate.Bydel;
import no.valg.valgnatt.database.hibernate.BydelId;
import no.valg.valgnatt.database.hibernate.Kandidat;
import no.valg.valgnatt.database.hibernate.KandidatId;
import no.valg.valgnatt.database.hibernate.Kommune;
import no.valg.valgnatt.database.hibernate.KommuneId;
import no.valg.valgnatt.database.hibernate.Mandatberegning;
import no.valg.valgnatt.database.hibernate.MandaterId;
import no.valg.valgnatt.database.hibernate.Partiliste;
import no.valg.valgnatt.database.hibernate.PartilisteId;
import no.valg.valgnatt.database.hibernate.Stemmekrets;
import no.valg.valgnatt.database.hibernate.StemmekretsId;
import no.valg.valgnatt.database.hibernate.Tilstand;
import no.valg.valgnatt.database.hibernate.Valgdistrikt;
import no.valg.valgnatt.database.hibernate.ValgdistriktId;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import static java.util.Collections.singletonList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class GrunnlagsdataServiceTest {

    @InjectMocks
    private GrunnlagsdataService grunnlagsdataService;

    @Mock
    private TilstandDao tilstandDao;
    @Mock
    private Tilstand tilstand;
    @Mock
    private PartiListeDao partiListeDao;
    @Mock
    private Partiliste partiliste;
    @Mock
    private Map<PartilisteId, Partiliste> partilisteMap;
    @Mock
    private List<Partiliste> partilisteList;
    @Mock
    private Iterator<Partiliste> iterator;
    @Mock
    private KommuneDao kommuneDao;
    @Mock
    private ValgdistriktDao valgdistriktDao;
    @Mock
    private StemmekretsDao stemmekretsDao;
    @Mock
    private BydelDao bydelDao;
    @Mock
    private MandatberegningDao mandatberegningDao;
    @Mock
    private KandidatDao kandidatDao;
    @Mock
    private GrunnlagsdataCache grunnlagsdataCache;
    @Mock
    private DataSource dataSource;
    @Mock
    private Connection connection;
    @Mock
    private PreparedStatement preparedStatement;
    @Mock
    private ResultSet resultSet;
    @Mock
    private PartiMandater partiMandater;
    @Mock
    private MandatData mandatData;

    @Test
    public void getTilstand_medNøkkel_returnererTilstand() {
        when(tilstandDao.findByNokkel("foo")).thenReturn(tilstand);

        Tilstand foo = grunnlagsdataService.getTilstand("foo");

        assertEquals(foo, tilstand);
    }

    @Test
    public void lagreTilstand_medTilstand_lagrerTilstand() {
        grunnlagsdataService.lagreTilstand(tilstand);

        verify(tilstandDao, times(1)).saveOrUpdate(tilstand);
    }

    @Test
    public void populerPartilister_medPartilister_populererPartilister() {
        when(partiListeDao.list()).thenReturn(partilisteList);
        when(partilisteList.iterator()).thenReturn(iterator);
        when(iterator.hasNext()).thenReturn(true, false);
        when(iterator.next()).thenReturn(partiliste);

        grunnlagsdataService.populerPartilister(partilisteMap);

        verify(partilisteMap, times(1)).put(partiliste.getId(), partiliste);
    }

    @Test
    public void initialiserGrunnlagsdatamodell_medValgTypeKo_populererGeografier() {
        ValgType valgType = ValgType.KO;
        Land land = initialiserGrunnlagsdatamodell(valgType);

        assertNotNull(land.getFylke("07"));
        assertNotNull(land.getKommune("0701"));
        assertNotNull(land.getStemmekretser().get("0701-0001"));
        assertNotNull(land.getKommune("0701").getBydel("1"));
        assertEquals(valgType, land.getValgType());
    }

    private Land initialiserGrunnlagsdatamodell(ValgType valgType) {
        when(kommuneDao.list()).thenReturn(singletonList(getKommune(valgType.name())));
        when(valgdistriktDao.list()).thenReturn(singletonList(getValgdistrikt(valgType.name())));
        when(stemmekretsDao.list()).thenReturn(singletonList(getStemmekrets(valgType.name())));
        when(bydelDao.list()).thenReturn(singletonList(getBydel()));

        Map<ValgTypeAar, Land> geografier = new HashMap<>();
        grunnlagsdataService.initialiserGrunnlagsdatamodell(geografier);

        ValgTypeAar vta = new ValgTypeAar(valgType, 2019);
        return geografier.get(vta);
    }

    private Kommune getKommune(String valgtype) {
        return Kommune.builder().id(getKommuneId(valgtype)).build();
    }

    private KommuneId getKommuneId(String valgtype) {
        return KommuneId.builder().kommnr("0701").valgaar(2019).valgtype(valgtype).build();
    }

    private Valgdistrikt getValgdistrikt(String valgtype) {
        return Valgdistrikt.builder().id(getValgdistriktId(valgtype)).build();
    }

    private ValgdistriktId getValgdistriktId(String valgtype) {
        return ValgdistriktId.builder().nr("0701").valgaar(2019).valgtype(valgtype).build();
    }

    private Stemmekrets getStemmekrets(String valgtype) {
        return Stemmekrets.builder().id(getStemmekretsId(valgtype)).bydelnr("1").build();
    }

    private StemmekretsId getStemmekretsId(String valgtype) {
        return StemmekretsId.builder().kommnr("0701").kretsnr("0001").valgaar(2019).valgtype(valgtype).build();
    }

    private Bydel getBydel() {
        return Bydel.builder().id(getBydelId()).build();
    }

    private BydelId getBydelId() {
        return BydelId.builder().bydelnr(1).kommnr("0701").build();
    }

    @Test
    public void initialiserGrunnlagsdatamodell_medValgTypeSt_populererGeografier() {
        ValgType valgType = ValgType.ST;
        Land land = initialiserGrunnlagsdatamodell(valgType);

        assertNotNull(land.getFylke("07"));
        assertNotNull(land.getKommune("0701"));
        assertNotNull(land.getStemmekretser().get("0701-0001"));
        assertNotNull(land.getKommune("0701").getBydel("1"));
        assertEquals(valgType, land.getValgType());
    }

    @Test
    public void initialiserGrunnlagsdatamodell_medValgTypeFy_populererGeografier() {
        ValgType valgType = ValgType.FY;
        Land land = initialiserGrunnlagsdatamodell(valgType);

        assertNotNull(land.getFylke("07"));
        assertNotNull(land.getKommune("0701"));
        assertNotNull(land.getStemmekretser().get("0701-0001"));
        assertNotNull(land.getKommune("0701").getBydel("1"));
        assertEquals(valgType, land.getValgType());
    }

    @Test
    public void initialiserGrunnlagsdatamodell_medValgTypeSa_populererGeografier() {
        ValgType valgType = ValgType.SA;
        Land land = initialiserGrunnlagsdatamodell(valgType);

        assertEquals(valgType, land.getValgType());
    }

    @Test
    public void initialiserMandaterOgKandidater_medValgTypeKo() throws SQLException {
        ValgType valgType = ValgType.KO;

        when(resultSet.next()).thenReturn(true, false);
        when(resultSet.getString("valgtype")).thenReturn(valgType.name());
        when(resultSet.getString("partikode")).thenReturn("Y");
        when(resultSet.getInt(anyString())).thenReturn(1);
        when(preparedStatement.executeQuery()).thenReturn(resultSet);
        when(connection.prepareStatement(anyString())).thenReturn(preparedStatement);
        when(dataSource.getConnection()).thenReturn(connection);

        when(mandatberegningDao.list()).thenReturn(singletonList(getMandatberegning(valgType.name())));
        when(kandidatDao.list()).thenReturn(singletonList(getKandidat(valgType.name())));
        when(partiMandater.getResultatData()).thenReturn(mandatData);
        when(partiMandater.getPrognoseData()).thenReturn(mandatData);
        Valgdistrikt valgdistrikt = spy(getValgdistrikt(valgType.name()));
        when(valgdistrikt.getPartiMandater(anyString())).thenReturn(partiMandater);
        when(grunnlagsdataCache.getValgdistrikt(eq(valgType), anyString())).thenReturn(valgdistrikt);
        when(grunnlagsdataCache.getPartiliste(eq(valgType), anyString())).thenReturn(getPartiliste());

        grunnlagsdataService.initialiserMandaterOgKandidater(grunnlagsdataCache);

        verify(valgdistrikt, atLeast(1)).addPartiMandater(any(PartiMandater.class));
        verify(partiMandater, times(1)).addKandidat(any(Kandidat.class));
        verify(mandatData, times(2)).setDistriktsmandater(any(Mandater.class));
        verify(mandatData, times(2)).setUtjevningmandater(any(Mandater.class));
    }

    private Mandatberegning getMandatberegning(String valgType) {
        return Mandatberegning.builder().id(getMandaterId(valgType)).build();
    }

    private MandaterId getMandaterId(String valgtype) {
        return MandaterId.builder().valgtype(valgtype).valgdistriktnr("0701").partikode("X").build();
    }

    private Kandidat getKandidat(String valgType) {
        return Kandidat.builder().id(getKandidatId(valgType)).build();
    }

    private KandidatId getKandidatId(String valgType) {
        return KandidatId.builder().partikode("X").plassnr(1).valgtype(valgType).build();
    }

    private Partiliste getPartiliste() {
        return Partiliste.builder().partinavn("Partinavn").partikategori(1).visningssekvens(1).build();
    }

//    private PartilisteId getPartilisteId() {
//        return PartilisteId.builder().partikode("X").build();
//    }

    @Test
    public void lesResultaterFraMandatberegning() {
    }

    @Test
    public void populerPartierPrStemmekrets() {
    }

    @Test
    public void populerOppgjørPrKrets() {
    }

    @Test
    public void populerStemmetallPrParti() {
    }

    @Test
    public void populerHistoriskeStemmetallPrParti() {
    }

    @Test
    public void populerHistoriskeStemmeberettigede() {
    }

    @Test
    public void oppdaterStemmeberettigedeForÅretsValg() {
    }

    @Test
    public void nullstillDatabase() {
    }
}
