# Tests for Profit DEA Models

# Struct for testing DEA model without monetary option
struct wrongProfitDEAmodel <: AbstractProfitDEAModel
    n::Int64
    eff::Vector
end

@testset "ProfitDEAModel" begin

    ## Test Profit DEA Model
    # Test with Zofio, Pastor and Aparicio (2013) data
    X = [1 1; 1 1; 0.75 1.5; 0.5 2; 0.5 2; 2 2; 2.75 3.5; 1.375 1.75]
    Y = [1 11; 5 3; 5 5; 2 9; 4 5; 4 2; 3 3; 4.5 3.5]
    P = [2 1; 2 1; 2 1; 2 1; 2 1; 2 1; 2 1; 2 1]
    W = [2 1; 2 1; 2 1; 2 1; 2 1; 2 1; 2 1; 2 1]

    GxGydollar = 1 ./ (sum(P, dims = 2) + sum(W, dims = 2))
    GxGydollar = repeat(GxGydollar, 1, 2)

    deaprofitdollar = deaprofit(X, Y, W, P, Gx = GxGydollar, Gy = GxGydollar)

    @test typeof(deaprofitdollar) == ProfitDEAModel
    
    @test nobs(deaprofitdollar) == 8
    @test ninputs(deaprofitdollar) == 2
    @test noutputs(deaprofitdollar) == 2
    @test efficiency(deaprofitdollar, :Economic)   ≈ [2; 2; 0; 2; 2; 8; 12; 4] atol = 1e-3
    @test efficiency(deaprofitdollar, :Technical)  ≈ [0; 0; 0; 0; 0; 6; 12; 3] atol = 1e-3
    @test efficiency(deaprofitdollar, :Allocative) ≈ [2; 2; 0; 2; 2; 2; 0; 1] atol = 1e-3

    @test efficiency(deaprofit(X, Y, W, P, Gx = :Monetary, Gy = :Monetary)) == efficiency(deaprofitdollar)

    @test efficiency(deaprofit(targets(deaprofitdollar, :X), targets(deaprofitdollar, :Y), W, P, Gx = :Monetary, Gy = :Monetary)) ≈ zeros(8)

    @test normfactor(deaprofitdollar) == ones(8);

    @test peersmatrix(deaprofitdollar) == deaprofitdollar.lambda

    # Check directions checking technical efficiency
    @test efficiency(deaprofit(X, Y, W, P, Gx = :Zeros, Gy = :Ones), :Technical) == efficiency(deaddf(X, Y, Gx = :Zeros, Gy = :Ones, rts = :VRS))
    @test efficiency(deaprofit(X, Y, W, P, Gx = :Ones, Gy = :Zeros), :Technical) == efficiency(deaddf(X, Y, Gx = :Ones, Gy = :Zeros, rts = :VRS))
    @test efficiency(deaprofit(X, Y, W, P, Gx = :Observed, Gy = :Observed), :Technical) == efficiency(deaddf(X, Y, Gx = :Observed, Gy = :Observed, rts = :VRS))
    @test efficiency(deaprofit(X, Y, W, P, Gx = :Mean, Gy = :Mean), :Technical) == efficiency(deaddf(X, Y, Gx = :Mean, Gy = :Mean, rts = :VRS))

    # Print
    show(IOBuffer(), deaprofitdollar)

    # Check normalization factor with different direction
    @test normfactor(deaprofit(X, Y, W, P, Gx = :Ones, Gy = :Ones)) == vec(sum(P .* ones(size(Y)), dims = 2) .+ sum(W .* ones(size(X)), dims = 2))

    # Check monetary option
    @test ismonetary(deaprofitdollar) == false

    deaprofitmonetary = deaprofit(X, Y, W, P, Gx = :Ones, Gy = :Ones, monetary = true)
    @test efficiency(deaprofitmonetary, :Economic)   ≈ [2; 2; 0; 2; 2; 8; 12; 4] atol = 1e-3
    @test efficiency(deaprofitmonetary, :Technical)  ≈ [0; 0; 0; 0; 0; 6; 12; 3] atol = 1e-3
    @test efficiency(deaprofitmonetary, :Allocative) ≈ [2; 2; 0; 2; 2; 2; 0; 1] atol = 1e-3
    @test ismonetary(deaprofitmonetary) == true

    # Test errors
    @test_throws DimensionMismatch deaprofit([1; 2 ; 3], [4 ; 5], [1; 1; 1], [4; 5], Gx = [1; 2 ; 3], Gy = [4 ; 5]) #  Different number of observations
    @test_throws DimensionMismatch deaprofit([1; 2; 3], [4; 5; 6], [1; 2; 3; 4], [4; 5; 6], Gx = [1; 2; 3], Gy = [4; 5; 6]) # Different number of observation in input prices
    @test_throws DimensionMismatch deaprofit([1; 2; 3], [4; 5; 6], [1; 2; 3], [4; 5; 6; 7], Gx = [1; 2; 3], Gy = [4; 5; 6]) # Different number of observation in output prices
    @test_throws DimensionMismatch deaprofit([1 1; 2 2; 3 3], [4; 5; 6], [1 1 1; 2 2 2; 3 3 3], [4; 5; 6], Gx = [1 1; 2 2; 3 3], Gy = [4; 5; 6]) # Different number of input prices and inputs
    @test_throws DimensionMismatch deaprofit([1; 2; 3], [4 4; 5 5; 6 6], [1; 2; 3], [4 4 4; 5 5 5; 6 6 6], Gx = [1; 2; 3], Gy = [4 4; 5 5; 6 6]) # Different number of oputput prices and outputs
    @test_throws DimensionMismatch deaprofit([1 1; 2 2; 3 3], [4; 5; 6], [1 1; 2 2; 3 3], [4; 5; 6], Gx = [1 1 1; 2 2 2; 3 3 3], Gy = [4; 5; 6]) # Different size of inputs direction
    @test_throws DimensionMismatch deaprofit([1; 2; 3], [4 4; 5 5; 6 6], [1; 2; 3], [4 4; 5 5; 6 6], Gx = [1; 2; 3], Gy = [4 4 4; 5 5 5; 6 6 6]) # Different size of outputs direction
    @test_throws ArgumentError deaprofit([1; 2; 3], [1; 2; 3], [1; 1; 1], [1; 1; 1], Gx = :Error, Gy = :Ones) # Invalid inputs direction
    @test_throws ArgumentError deaprofit([1; 2; 3], [1; 2; 3], [1; 1; 1], [1; 1; 1], Gx = :Ones, Gy = :Error) # Invalid outputs direction
    @test_throws ArgumentError targets(deaprofitdollar, :error) # Invalid target

    # Test struct defaults and errors
    @test_throws ArgumentError ismonetary(wrongProfitDEAmodel(3, [1; 2; 3])) # Model does not have info on peers
    
    # ------------------
    # Test Vector and Matrix inputs and outputs
    # ------------------
    # Tests against results in R

    # Inputs is Matrix, Outputs is Vector
    X = [2 2; 1 4; 4 1; 4 3; 5 5; 6 1; 2 5; 1.6	8]
    Y = [1; 1; 1; 1; 1; 1; 1; 1]
    W = [1 1; 1 1; 1 1; 1 1; 1 1; 1 1; 1 1; 1 1]
    P = [1; 1; 1; 1; 1; 1; 1; 1]

    @test efficiency(deaprofit(X, Y, W, P, Gx = X, Gy = Y)) ≈ [0; 0.1666666667; 0.1666666667; 0.375; 0.5454545455; 0.375; 0.375; 0.5283018868]

    # Inputs is Vector, Output is Matrix
    X = [1; 1; 1; 1; 1; 1; 1; 1]
    Y = [7 7; 4 8; 8 4; 3 5; 3 3; 8 2; 6 4; 1.5 5]
    W = [1; 1; 1; 1; 1; 1; 1; 1]
    P = [1 1; 1 1; 1 1; 1 1; 1 1; 1 1; 1 1; 1 1]

    @test efficiency(deaprofit(X, Y, W, P, Gx = X, Gy = Y)) ≈ [0; 0.1538461538; 0.1538461538; 0.6666666667; 1.142857143; 0.3636363636; 0.3636363636; 1]

    # Inputs is Vector, Output is Vector
    X = [2; 4; 8; 12; 6; 14; 14; 9.412]
    Y = [1; 5; 8; 9; 3; 7; 9; 2.353]
    W = [1; 1; 1; 1; 1; 1; 1; 1]
    P = [1; 1; 1; 1; 1; 1; 1; 1]

    @test efficiency(deaprofit(X, Y, W, P, Gx = X, Gy = Y)) ≈ [0.6666666667; 0; 0.0625; 0.1904761905; 0.4444444444; 0.3809523810; 0.2608695652; 0.6849978751]

end
