package org.initialde.yakasave.Unit;

import org.initialde.yakasave.Api.Requests.CreateCollectiveSavingsFundRequest;
import org.initialde.yakasave.Application.CreateCollectiveSavingsFund;
import org.initialde.yakasave.Domain.Exceptions.InvalidAmountException;
import org.initialde.yakasave.Domain.Exceptions.InvalidDeadlineException;
import org.initialde.yakasave.Domain.Exceptions.MinimumGoalAmountException;
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 CreateCollectiveSavingsFundTests {
    @Test
    public void shouldCreateCollectiveSavingsFund() {
        String goalTitle = "Buy a cancer's traitment!";
        double goalAmount = 5000.0;
        String description = "This is a description for a cancer's traitment!";
        LocalDate launchDate = LocalDate.now();
        LocalDate deadline = launchDate.plusDays(1);
        String reference = UUID.randomUUID().toString();
        boolean memberApproval = false;
        int maxAllowedMembers = 5;

        var savingsFundRepository = new InMemorySavingsFundRepository();
        var tokenGenerator = new FakeJwtService();

        var authenticationGateway = new FakeAuthenticationGateway(tokenGenerator);
        var createCollectiveSavingsFundRequest = new CreateCollectiveSavingsFundRequest(
                goalTitle,
                description,
                goalAmount,
                launchDate,
                deadline,
                reference,
                memberApproval,
                maxAllowedMembers
        );

        CreateCollectiveSavingsFund createCollectiveSavingsFund = new CreateCollectiveSavingsFund(savingsFundRepository,
                authenticationGateway,
                () -> reference);
        createCollectiveSavingsFund.create(createCollectiveSavingsFundRequest);

        assertTrue(savingsFundRepository.existsByReference(reference));
    }

    @Test
    public void shouldNotCreateCollectiveSavingFundIfDeadlineIsBeforeLaunchDate() {
        String goalTitle = "Buy a cancer's traitment!";
        double goalAmount = 5000.0;
        String description = "This is a description for a cancer's traitment!";
        LocalDate launchDate = LocalDate.now();
        LocalDate invalidDeadline = launchDate.minusDays(1);
        String reference = UUID.randomUUID().toString();
        boolean memberApproval = false;
        int maxAllowedMembers = 5;

        var savingsFundRepository = new InMemorySavingsFundRepository();
        var tokenGenerator = new FakeJwtService();

        var authenticationGateway = new FakeAuthenticationGateway(tokenGenerator);
        var createCollectiveSavingsFundRequest = new CreateCollectiveSavingsFundRequest(
                goalTitle,
                description,
                goalAmount,
                launchDate,
                invalidDeadline,
                reference,
                memberApproval,
                maxAllowedMembers
        );

        CreateCollectiveSavingsFund createCollectiveSavingsFund = new CreateCollectiveSavingsFund(savingsFundRepository, authenticationGateway, () -> reference);

        assertThrowsExactly(InvalidDeadlineException.class, () -> createCollectiveSavingsFund.create(createCollectiveSavingsFundRequest));
        assertFalse(savingsFundRepository.existsByReference(reference));
    }

    @Test
    public void shouldNotCreateCollectiveSavingFundIfGoalAmountIsLessThanMinimumAmount() {
        String goalTitle = "Buy a cancer's traitment!";
        double nullGoalAmount = 0.0;
        String description = "This is a description for a cancer's traitment!";
        LocalDate launchDate = LocalDate.now();
        LocalDate deadline = launchDate.plusDays(1);
        String reference = UUID.randomUUID().toString();
        boolean memberApproval = false;
        int maxAllowedMembers = 5;

        var savingsFundRepository = new InMemorySavingsFundRepository();
        var tokenGenerator = new FakeJwtService();

        var authenticationGateway = new FakeAuthenticationGateway(tokenGenerator);
        var createCollectiveSavingsFundRequest = new CreateCollectiveSavingsFundRequest(
                goalTitle,
                description,
                nullGoalAmount,
                launchDate,
                deadline,
                reference,
                memberApproval,
                maxAllowedMembers
        );

        CreateCollectiveSavingsFund createCollectiveSavingsFund = new CreateCollectiveSavingsFund(savingsFundRepository, authenticationGateway, () -> reference);

        assertThrowsExactly(MinimumGoalAmountException.class, () -> createCollectiveSavingsFund.create(createCollectiveSavingsFundRequest));
        assertFalse(savingsFundRepository.existsByReference(reference));
    }

    @Test
    public void shouldNotCreateCollectiveSavingFundWithNegativeAmount() {
        String goalTitle = "Buy a cancer's traitment!";
        double invalidGoalAmount = -200.0;
        String description = "This is a description for a cancer's traitment!";
        LocalDate launchDate = LocalDate.now();
        LocalDate deadline = launchDate.plusDays(1);
        String reference = UUID.randomUUID().toString();
        boolean memberApproval = false;
        int maxAllowedMembers = 5;

        var savingsFundRepository = new InMemorySavingsFundRepository();
        var tokenGenerator = new FakeJwtService();

        var authenticationGateway = new FakeAuthenticationGateway(tokenGenerator);
        var createCollectiveSavingsFundRequest = new CreateCollectiveSavingsFundRequest(
                goalTitle,
                description,
                invalidGoalAmount,
                launchDate,
                deadline,
                reference,
                memberApproval,
                maxAllowedMembers
        );

        CreateCollectiveSavingsFund createCollectiveSavingsFund = new CreateCollectiveSavingsFund(savingsFundRepository, authenticationGateway, () -> reference);

        assertThrowsExactly(InvalidAmountException.class, () -> createCollectiveSavingsFund.create(createCollectiveSavingsFundRequest));
        assertFalse(savingsFundRepository.existsByReference(reference));
    }
}
