package com.kpl.CourierMgmt.CourierManager;

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

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

public class BasicFareCalculatorTests {

	
	@Test
    void StandardFareLowerBound()
    {
        // ARRANGE

        IFareCalculator fc = new BasicFareCalculator();

        // ACT

        int fare = fc.CalculateFare(3.0, 5.0);

        // ASSERT

        assertEquals(1500, fare);
    }

	@Test
    void StandardFareHigh()
    {
        // ARRANGE

        IFareCalculator fc = new BasicFareCalculator();

        // ACT

        int fare = fc.CalculateFare(30.0, 50.0);

        // ASSERT

        assertEquals(150000, fare);
    }

	@Test
    void FifteenEuroMinimumFareLowerBound()
    {
        // ARRANGE

        IFareCalculator fc = new BasicFareCalculator();

        // ACT

        int fare = fc.CalculateFare(0.0, 0.0);

        // ASSERT

        assertEquals(1500, fare);
    }

	@Test
    void FifteenEuroMinimumFareUpperBound()
    {
        // ARRANGE

        IFareCalculator fc = new BasicFareCalculator();

        // ACT

        int fare = fc.CalculateFare(2.99, 4.99);

        // ASSERT

        assertEquals(1500, fare);
    }
	
	@Test
    void NegativeDistance()
    {
        // ARRANGE

        IFareCalculator fc = new BasicFareCalculator();

        // ACT

        IllegalArgumentException argEx = assertThrows(IllegalArgumentException.class, () -> {
        	fc.CalculateFare(-0.01, 1.0);
        });
        
        assertNotNull(argEx);
        assertEquals("Distance cannot be negative", argEx.getMessage());
    }
	
	@Test
    void NegativeWeight()
    {
        // ARRANGE

        IFareCalculator fc = new BasicFareCalculator();

        // ACT

        IllegalArgumentException argEx = assertThrows(IllegalArgumentException.class, () -> {
        	fc.CalculateFare(1.0, -0.01);
        });
        
        assertNotNull(argEx);
        assertEquals("Weight cannot be negative", argEx.getMessage());
    }
	
	@Test
    void NegativeWeightAndDistance()
    {
        // ARRANGE

        IFareCalculator fc = new BasicFareCalculator();

        // ACT

        assertThrows(IllegalArgumentException.class, () -> {
        	fc.CalculateFare(-0.01, -0.01);
        });
    }
	
	@ParameterizedTest
	@MethodSource("distanceWeightAndExpectedFareProvider")
	void ValidFares(double distance, double weight, int expectedFare)
    {
        // ARRANGE

        IFareCalculator fc = new BasicFareCalculator();

        // ACT

        int fare = fc.CalculateFare(distance, weight);

        // ASSERT

        assertEquals(expectedFare, fare);
    }
	
	static Stream<Arguments> distanceWeightAndExpectedFareProvider() {
		return Stream.of(
			Arguments.of(3.0, 5.0, 1500),
			Arguments.of(30.0, 50.0, 150000),
			Arguments.of(0.0, 0.0, 1500),
			Arguments.of(2.99, 4.99, 1500)
		);
	}
	
	@ParameterizedTest
	@MethodSource("distanceAndWeightProvider")
	void InValidFares(double distance, double weight)
    {
		// ARRANGE

        IFareCalculator fc = new BasicFareCalculator();

        // ACT

        IllegalArgumentException argEx = assertThrows(IllegalArgumentException.class, () -> {
        	fc.CalculateFare(distance, weight);
        });
        
        assertNotNull(argEx);
    }
	
	static Stream<Arguments> distanceAndWeightProvider() {
		return Stream.of(
			Arguments.of(-0.01, 1.0),
			Arguments.of(1.0, -0.01),
			Arguments.of(-0.01, -0.01)
		);
	}
	
	@ParameterizedTest
	@MethodSource("stringIntAndListProvider")
	void testWithMultiArgMethodSource(String str, int num, List<String> list) {
	    assertEquals(3, str.length());
	    assertTrue(num >=1 && num <=2);
	    assertEquals(2, list.size());
	}

	static Stream<Arguments> stringIntAndListProvider() {
	    return Stream.of(
	        Arguments.of("foo", 1, Arrays.asList("a", "b")),
	        Arguments.of("bar", 2, Arrays.asList("x", "y"))
	    );
	}
}
