package org.initialde.yakasave.Unit;

import org.initialde.yakasave.Api.Requests.CreatePersonalSavingsFundRequest;
import org.initialde.yakasave.Application.CreatePersonalSavingsFund;
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 CreatePersonalSavingFundTests {
    @Test
    public void shouldCreatePersonalSavingFund() {
        String goalTitle = "Trip to Paris!";
        double goalAmount = 5000.0;
        LocalDate launchDate = LocalDate.now();
        LocalDate deadline = launchDate.plusDays(1);
        String reference = UUID.randomUUID().toString();

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

        var authenticationGateway = new FakeAuthenticationGateway(tokenGenerator);
        var createPersonalSavingsFundRequest = new CreatePersonalSavingsFundRequest(
                goalTitle,
                goalAmount,
                launchDate,
                deadline
        );

        CreatePersonalSavingsFund createPersonalSavingsFund = new CreatePersonalSavingsFund(savingsFundRepository, authenticationGateway, () -> reference);

        createPersonalSavingsFund.create(createPersonalSavingsFundRequest);

        assertTrue(savingsFundRepository.existsByReference(reference));
    }

    @Test
    public void shouldNotCreatePersonalSavingFundIfDeadlineIsBeforeLaunchDate() {
        String goalTitle = "Trip to Paris!";
        double goalAmount = 5000.0;
        LocalDate launchDate = LocalDate.now();
        LocalDate invalidDeadline = launchDate.minusDays(2);
        String reference = UUID.randomUUID().toString();

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

        var authenticationGateway = new FakeAuthenticationGateway(tokenGenerator);
        var createPersonalSavingsFundRequest = new CreatePersonalSavingsFundRequest(
                goalTitle,
                goalAmount,
                launchDate,
                invalidDeadline
        );

        CreatePersonalSavingsFund createPersonalSavingsFund = new CreatePersonalSavingsFund(savingsFundRepository, authenticationGateway, () -> reference);


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

    @Test
    public void shouldNotCreatePersonalSavingFundIfGoalAmountIsLessThanMinimumAmount() {
        String goalTitle = "Trip to Paris!";
        double nullAmount = 0.0;
        LocalDate launchDate = LocalDate.now();
        LocalDate deadline = launchDate.plusDays(2);
        String reference = UUID.randomUUID().toString();

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

        var authenticationGateway = new FakeAuthenticationGateway(tokenGenerator);
        var createPersonalSavingsFundRequest = new CreatePersonalSavingsFundRequest(
                goalTitle,
                nullAmount,
                launchDate,
                deadline
        );

        CreatePersonalSavingsFund createPersonalSavingsFund = new CreatePersonalSavingsFund(savingsFundRepository, authenticationGateway, () -> reference);

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

    @Test
    public void shouldNotCreatePersonalSavingFundWithNegativeAmount() {
        String goalTitle = "Trip to Paris!";
        double invalidAmount = -50.0;
        LocalDate launchDate = LocalDate.now();
        LocalDate deadline = launchDate.plusDays(2);
        String reference = UUID.randomUUID().toString();

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

        var authenticationGateway = new FakeAuthenticationGateway(tokenGenerator);
        var createPersonalSavingsFundRequest = new CreatePersonalSavingsFundRequest(
                goalTitle,
                invalidAmount,
                launchDate,
                deadline
        );

        CreatePersonalSavingsFund createPersonalSavingsFund = new CreatePersonalSavingsFund(savingsFundRepository, authenticationGateway, () -> reference);

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