"""
    gumbelfit()

Estimate the Gumbel parameters by maximum likelihood.

# Details

The Gumbel distribution is a particular case of the generalized extreme value distribution when the shape parameter is zero.

!!! note "Extreme value theory"

    In extreme value theory, it is best to avoid the choice of a sub-family of extreme value familu as the Gumbel. This is because the choice of family is made with the data at hand, 
    and when extrapolating to large quantiles, i.e. larger than the range of the data, the uncertainty associated with this choice is not taken into account. If the data suggest that 
    the Gumbel family is the best one, this does not imply that the other families are not plausible. In applications, the confidence intervals on the shape parameter are often wide, 
    representing the difficulty of discriminating the tail behavior using only the limited number of data. Therefore, the use of the GEV distribution for the block maxima model makes 
    more sense. As Coles (2001) also argued in Page 64, this is  "...the safest option is to accept there is uncertainty about the value of the shape parameter ... and to prefer the
     inference based on the GEV model. The larger measures of uncertainty generated by the GEV model then provide a more realistic quantification of the genuine uncertainties involved 
     in model extrapolation." 

# Implementation

The function uses Nelder-Mead solver implemented in the [Optim.jl](https://github.com/JuliaNLSolvers/Optim.jl)
package to find the point where the log-likelihood is maximal.

The Gumbel parameters can be modeled as function of covariates as follows:
```math
μ = X₁ × β₁,
```
```math
ϕ = X₂ × β₂,
```

The covariates are standardized before estimating the parameters to help fit the
 model. They are transformed back on their original scales before returning the
 fitted model.

See also [`gumbelfit`](@ref) for the other methods and [`gevfit`](@ref) for estiming the parameters of the GEV distribution.

"""
function gumbelfit end

"""
    gumbelfit(y,
        locationcov = Vector{Variable}(),
        logscalecov = Vector{Variable}()
        )

Estimate the Gumbel parameters.

# Arguments

- `y::Vector{<:Real}`: The vector of block maxima.
- `locationcov::Vector{<:DataItem} = Vector{Variable}()`: The covariates of the location parameter.
- `logscalecov::Vector{<:DataItem} = Vector{Variable}()`: The covariates of the log-scale parameter.
"""
function gumbelfit(y::Vector{<:Real};
    locationcov::Vector{<:DataItem} = Vector{Variable}(),
    logscalecov::Vector{<:DataItem} = Vector{Variable}())::MaximumLikelihoodAbstractExtremeValueModel

    locationcovstd = standardize.(locationcov)
    logscalecovstd = standardize.(logscalecov)

    model = BlockMaxima{Gumbel}(Variable("y", y), locationcov = locationcovstd, logscalecov = logscalecovstd)

    fittedmodel = fit(model)

    return transform(fittedmodel)

end

"""
    gumbelfit(y,
        initialvalues,
        locationcov = Vector{Variable}(),
        logscalecov = Vector{Variable}()
        )

Estimate the Gumbel parameters.

# Arguments

- `y::Vector{<:Real}`: the vector of block maxima.
- `initialvalues::Vector{<:Real}`: Vector of parameters initial values.
- `locationcov::Vector{<:DataItem} = Vector{Variable}()`: The covariates of the location parameter.
- `logscalecov::Vector{<:DataItem} = Vector{Variable}()`: The covariates of the log-scale parameter.
"""
function gumbelfit(y::Vector{<:Real}, initialvalues::Vector{<:Real};
    locationcov::Vector{<:DataItem} = Vector{Variable}(),
    logscalecov::Vector{<:DataItem} = Vector{Variable}())::MaximumLikelihoodAbstractExtremeValueModel

    model = BlockMaxima{Gumbel}(Variable("y", y), locationcov = locationcov, logscalecov = logscalecov)

    return fit(model, initialvalues)

end

"""
    gumbelfit(df::DataFrame,
        datacol::Symbol,
        locationcovid = Vector{Symbol}(),
        logscalecovid = Vector{Symbol}(),
        shapecovid = Vector{Symbol}()
        )

Estimate the Gumbel parameters.

# Arguments
- `df::DataFrame`: The dataframe containing the data.
- `datacol::Symbol`: The symbol of the column of `df` containing the block maxima data.
- `locationcovid::Vector{Symbol} = Vector{Symbol}()`: The symbols of the columns of `df` containing the covariates of the location parameter.
- `logscalecovid::Vector{Symbol} = Vector{Symbol}()`: The symbols of the columns of `df` containing the covariates of the log-scale parameter.
"""
function gumbelfit(df::DataFrame, datacol::Symbol;
    locationcovid::Vector{Symbol}=Symbol[],
    logscalecovid::Vector{Symbol}=Symbol[])::MaximumLikelihoodAbstractExtremeValueModel

    locationcovstd = standardize.(buildVariables(df, locationcovid))
    logscalecovstd = standardize.(buildVariables(df, logscalecovid))

    model = BlockMaxima{Gumbel}(Variable(string(datacol), df[:, datacol]), locationcov = locationcovstd, logscalecov = logscalecovstd)

    fittedmodel = fit(model)

    return transform(fittedmodel)

end

"""
    gumbelfit(df::DataFrame,
        datacol::Symbol,
        locationcovid = Vector{Symbol}(),
        logscalecovid = Vector{Symbol}()
        )

Estimate the Gumbel parameters.

# Arguments
- `df::DataFrame`: The dataframe containing the data.
- `datacol::Symbol`: The symbol of the column of `df` containing the block maxima data.
- `initialvalues::Vector{<:Real}`: Vector of parameters initial values.
- `locationcovid::Vector{Symbol} = Vector{Symbol}()`: The symbols of the columns of `df` containing the covariates of the location parameter.
- `logscalecovid::Vector{Symbol} = Vector{Symbol}()`: The symbols of the columns of `df` containing the covariates of the log-scale parameter.
"""
function gumbelfit(df::DataFrame, datacol::Symbol, initialvalues::Vector{<:Real};
    locationcovid::Vector{Symbol}=Symbol[],
    logscalecovid::Vector{Symbol}=Symbol[])::MaximumLikelihoodAbstractExtremeValueModel

    locationcov = buildVariables(df, locationcovid)
    logscalecov = buildVariables(df, logscalecovid)

    model = BlockMaxima{Gumbel}(Variable(string(datacol), df[:, datacol]), locationcov = locationcov, logscalecov = logscalecov)

    return fit(model, initialvalues)

end

"""
    gumbelfit(model::{BlockMaxima{Gumbel}}, initialvalues::Vector{<:Real})

Estimate the parameters of the `BlockMaxima` model using the given initialvalues.
"""
function gumbelfit(model::BlockMaxima{Gumbel}, initialvalues::Vector{<:Real})::MaximumLikelihoodAbstractExtremeValueModel

    fit(model, initialvalues)

end
