package org.initialde.yakasave.Unit;

import org.initialde.yakasave.Application.CloseSavingsFund;
import org.initialde.yakasave.Domain.Entities.SavingsFund;

import org.initialde.yakasave.UserFactory;
import org.initialde.yakasave.Domain.Enums.TypeSavingsFund;
import org.initialde.yakasave.Domain.Exceptions.AlreadyClosedSavingsFunException;
import org.initialde.yakasave.Domain.Exceptions.NotAuthorizeToCloseSavingsFundException;
import org.initialde.yakasave.Domain.ValueObject.Amount;
import org.initialde.yakasave.Infrastructure.Persistence.Fake.InMemorySavingsFundRepository;
import org.initialde.yakasave.Infrastructure.authentication.FakeAuthenticationGateway;
import org.initialde.yakasave.Infrastructure.authentication.Jwt.fake.FakeJwtService;
import org.junit.jupiter.api.Test;

import java.time.LocalDate;
import java.util.UUID;

import static org.junit.jupiter.api.Assertions.*;

public class CloseSavingsFundTest {
    @Test
    public void shouldDisableSavingsFundByClosingWhenBalanceIsZero() {
        var reference = UUID.randomUUID().toString();
        var owner = UserFactory.create( "owner", "1234");
        var savingsFund = SavingsFund
                .builder()
                .reference(reference)
                .owner(owner)
                .balance(0)
                .isActive(true)
                .goalAmount(Amount.MINIMUM_GOAL_AMOUNT)
                .type(TypeSavingsFund.PERSONAL)
                .deadline(LocalDate.now().plusDays(1))
                .build();

        var savingsFundRepository = new InMemorySavingsFundRepository();
        savingsFundRepository.save(savingsFund);

        var tokenGenerator = new FakeJwtService();

        var authenticationGateway = new FakeAuthenticationGateway(tokenGenerator);
        authenticationGateway.authenticate(owner);

        var closeSavingsFund = new CloseSavingsFund(savingsFundRepository, authenticationGateway);
        closeSavingsFund.close(savingsFund);

        var actualSavingFund = savingsFundRepository.findByReference(reference).get().isActive();
        assertFalse(actualSavingFund);
    }

    @Test
    public void shouldFailDisableSavingsFundByClosingWhenBalanceIsGreaterThanZero() {
        var reference = UUID.randomUUID().toString();
        var owner = UserFactory.create( "owner", "1234");
        var savingsFund = SavingsFund
                .builder()
                .reference(reference)
                .owner(owner)
                .balance(3000)
                .isActive(true)
                .goalAmount(Amount.MINIMUM_GOAL_AMOUNT)
                .type(TypeSavingsFund.PERSONAL)
                .deadline(LocalDate.now().plusDays(1))
                .build();

        var savingsFundRepository = new InMemorySavingsFundRepository();
        savingsFundRepository.save(savingsFund);

        var tokenGenerator = new FakeJwtService();

        var authenticationGateway = new FakeAuthenticationGateway(tokenGenerator);
        authenticationGateway.authenticate(owner);

        var closeSavingsFund = new CloseSavingsFund(savingsFundRepository, authenticationGateway);
        assertThrowsExactly(NotAuthorizeToCloseSavingsFundException.class, () -> closeSavingsFund.close(savingsFund));
    }

    @Test
    public void shouldFailDisableSavingsFundByClosingWhenAlreadyClosed() {
        var reference = UUID.randomUUID().toString();
        var owner = UserFactory.create( "owner", "1234");
        var savingsFund = SavingsFund
                .builder()
                .reference(reference)
                .owner(owner)
                .balance(0)
                .isActive(false)
                .goalAmount(Amount.MINIMUM_GOAL_AMOUNT)
                .type(TypeSavingsFund.PERSONAL)
                .deadline(LocalDate.now().plusDays(1))
                .build();

        var savingsFundRepository = new InMemorySavingsFundRepository();
        savingsFundRepository.save(savingsFund);

        var tokenGenerator = new FakeJwtService();

        var authenticationGateway = new FakeAuthenticationGateway(tokenGenerator);
        authenticationGateway.authenticate(owner);

        var closeSavingsFund = new CloseSavingsFund(savingsFundRepository, authenticationGateway);
        assertThrowsExactly(AlreadyClosedSavingsFunException.class, () -> closeSavingsFund.close(savingsFund));
    }
}
