package danielcortes.xyz.models.documentos;

import static org.assertj.core.api.Assertions.assertThat;

import com.github.javafaker.Faker;
import danielcortes.xyz.data.ConnectionHolder;
import danielcortes.xyz.data.DAOManager;
import danielcortes.xyz.data.SQLiteConnectionHolder;
import danielcortes.xyz.models.caja.Caja;
import danielcortes.xyz.models.caja.CajaDAO;
import danielcortes.xyz.models.version.SQLiteVersionDAO;
import danielcortes.xyz.models.version.VersionDAO;
import danielcortes.xyz.utils.RandomLocalDate;
import java.io.File;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class SQLiteDocumentosDAOTest {

  private static final int databaseVersion = 2;
  private static Faker faker;
  private static VersionDAO versionDAO;
  private DocumentosDAO documentosDAO;
  private CajaDAO cajaDAO;

  @BeforeAll
  static void setAll() {
    ConnectionHolder connectionHolder = new SQLiteConnectionHolder("jdbc:sqlite:/tmp/tmp.sqlite");
    DAOManager.setup(connectionHolder);
    versionDAO = new SQLiteVersionDAO(connectionHolder);
    faker = new Faker();
  }

  @BeforeEach
  void setUp() {
    versionDAO.updateTo(databaseVersion);
    this.documentosDAO = DAOManager.getDocumentosDAO();
    this.cajaDAO = DAOManager.getCajaDAO();
  }

  @AfterEach
  void tearDown() {
    //noinspection ResultOfMethodCallIgnored
    new File("/tmp/tmp.sqlite").delete();
  }

  @Test
  void getAll() {
    Caja c1 = giveRandomCaja();

    this.cajaDAO.insert(c1);

    Documentos d1 = giveRandomDocumentos(c1);
    Documentos d2 = giveRandomDocumentos(c1);

    this.documentosDAO.insert(d1);
    this.documentosDAO.insert(d2);

    List<Documentos> l = this.documentosDAO.getAll();

    assertThat(l)
        .hasSize(2)
        .containsExactlyInAnyOrder(d1, d2);
  }

  @Test
  void getById() {
    Caja c1 = giveRandomCaja();

    this.cajaDAO.insert(c1);

    Documentos d1 = giveRandomDocumentos(c1);
    Documentos d2 = giveRandomDocumentos(c1);

    this.documentosDAO.insert(d1);
    this.documentosDAO.insert(d2);

    Optional<Documentos> o1 = this.documentosDAO.getById(d1.getId());
    Optional<Documentos> o2 = this.documentosDAO.getById(d2.getId());
    Optional<Documentos> o3 = this.documentosDAO.getById(-1);

    assertThat(o1)
        .isPresent()
        .contains(d1);

    assertThat(o2)
        .isPresent()
        .contains(d2);

    assertThat(o3)
        .isEmpty();
  }

  @Test
  void getByCaja() {
    Caja c1 = giveRandomCaja();
    Caja c2 = giveRandomCaja();

    this.cajaDAO.insert(c1);
    this.cajaDAO.insert(c2);

    Documentos d1 = giveRandomDocumentos(c1);
    Documentos d2 = giveRandomDocumentos(c2);

    this.documentosDAO.insert(d1);
    this.documentosDAO.insert(d2);

    Optional<Documentos> o1 = this.documentosDAO.getByCaja(c1);
    Optional<Documentos> o2 = this.documentosDAO.getByCaja(c2);
    Optional<Documentos> o3 = this.documentosDAO.getByCaja(Caja.EMPTY);

    assertThat(o1)
        .isPresent()
        .contains(d1);

    assertThat(o2)
        .isPresent()
        .contains(d2);

    assertThat(o3)
        .isEmpty();
  }

  @Test
  void getTotalDocumentos() {
    Caja c1 = giveRandomCaja();
    Caja c2 = giveRandomCaja();

    this.cajaDAO.insert(c1);
    this.cajaDAO.insert(c2);

    Documentos d1 = giveRandomDocumentos(c1);
    Documentos d2 = giveRandomDocumentos(c2);

    this.documentosDAO.insert(d1);
    this.documentosDAO.insert(d2);

    int t1 = this.documentosDAO.getTotalDocumentos(c1);
    int t2 = this.documentosDAO.getTotalDocumentos(c2);

    assertThat(t1)
        .isEqualTo(d1.getCheques() + d1.getRetiros() + d1.getTarjetas());

    assertThat(t2)
        .isEqualTo(d2.getCheques() + d2.getRetiros() + d2.getTarjetas());
  }

  @Test
  void insert() {
    Caja c1 = giveRandomCaja();

    this.cajaDAO.insert(c1);

    Documentos d1 = giveRandomDocumentos(c1);

    assertThat(d1.getId())
        .isEqualTo(0);

    this.documentosDAO.insert(d1);

    assertThat(d1.getId())
        .isNotEqualTo(0);

    List<Documentos> l = this.documentosDAO.getAll();

    assertThat(l)
        .hasSize(1)
        .containsExactlyInAnyOrder(d1);
  }

  @Test
  void update() {
    Caja c1 = giveRandomCaja();
    Caja c2 = giveRandomCaja();

    this.cajaDAO.insert(c1);
    this.cajaDAO.insert(c2);

    Documentos d1 = giveRandomDocumentos(c1);

    this.documentosDAO.insert(d1);

    Documentos d2 = updateRandomDocumentos(d1);

    this.documentosDAO.update(d2);

    assertThat(d1.getId()).isEqualTo(d2.getId());

    Optional<Documentos> o = this.documentosDAO.getById(d2.getId());

    assertThat(o)
        .isPresent()
        .contains(d2);
  }

  @Test
  void delete() {
    Caja c1 = giveRandomCaja();
    this.cajaDAO.insert(c1);

    Documentos d1 =giveRandomDocumentos(c1);
    this.documentosDAO.insert(d1);

    assertThat(this.documentosDAO.getById(d1.getId()))
        .isPresent()
        .contains(d1);

    this.documentosDAO.delete(d1);
    assertThat(this.documentosDAO.getById(d1.getId()))
        .isEmpty();

    assertThat(this.cajaDAO.getById(c1.getId()))
        .isPresent()
        .contains(c1);

  }

  Caja giveRandomCaja() {
    Caja caja = new Caja();
    caja.setFondo(faker.number().numberBetween(0, 10000000));
    caja.setFecha(RandomLocalDate.random());
    return caja;
  }

  Documentos giveRandomDocumentos(Caja caja) {
    Documentos d = new Documentos();
    d.setCaja(caja);
    d.setRetiros(faker.number().numberBetween(0, 10000000));
    d.setCheques(faker.number().numberBetween(0, 10000000));
    d.setTarjetas(faker.number().numberBetween(0, 10000000));
    return d;
  }

  Documentos updateRandomDocumentos(Documentos documentos) {
    Documentos d = giveRandomDocumentos(documentos.getCaja());
    d.setId(documentos.getId());
    return d;
  }
}