internal/functions/Test-HawkDateParameter.ps1

Function Test-HawkDateParameter {
    <#
    .SYNOPSIS
        Internal helper function that processes and validates date parameters for Hawk investigations.
 
    .DESCRIPTION
        The Test-HawkDateParmeter function is an internal helper used by Start-HawkTenantInvestigation
        and Start-HawkUserInvestigation to process date-related parameters. It handles both direct date
        specifications and the DaysToLookBack parameter, performing initial validation and date conversions.
 
        The function:
        - Validates the combination of provided date parameters
        - Processes DaysToLookBack into concrete start/end dates
        - Converts dates to UTC format
        - Performs bounds checking on date ranges
        - Handles both absolute dates and relative date calculations
 
        This is designed as an internal function and should not be called directly by end users.
 
    .PARAMETER PSBoundParameters
        The PSBoundParameters hashtable from the calling function. Used to check which parameters
        were explicitly passed to the parent function. Must contain information about whether
        StartDate, EndDate, and/or DaysToLookBack were provided.
 
    .PARAMETER StartDate
        The starting date for the investigation period, if specified directly.
        Can be null if using DaysToLookBack instead.
        When provided with EndDate, defines an explicit date range for the investigation.
 
    .PARAMETER EndDate
        The ending date for the investigation period, if specified directly.
        Can be null if using DaysToLookBack instead.
        When provided with StartDate, defines an explicit date range for the investigation.
 
    .PARAMETER DaysToLookBack
        The number of days to look back from either the current date or a specified EndDate.
        Must be between 1 and 365.
        Cannot be used together with StartDate.
 
    .OUTPUTS
        PSCustomObject containing:
        - StartDate [DateTime]: The calculated or provided start date in UTC
        - EndDate [DateTime]: The calculated or provided end date in UTC
    .EXAMPLE
        $dates = Test-HawkDateParameter -PSBoundParameters $PSBoundParameters -DaysToLookBack 30
 
        Processes a request to look back 30 days from the current date, returning appropriate
        start and end dates in UTC format.
    .EXAMPLE
        $dates = Test-HawkDateParameter `
            -PSBoundParameters $PSBoundParameters `
            -StartDate "2024-01-01" `
            -EndDate "2024-01-31"
         
        Processes explicit start and end dates, validating them and converting to UTC format.
    .EXAMPLE
        $dates = Test-HawkDateParameter `
            -PSBoundParameters $PSBoundParameters `
            -DaysToLookBack 30 `
            -EndDate "2024-01-31"
         
        Processes a request to look back 30 days from a specific end date.
    .NOTES
        Author: Jonathan Butler
        Internal Function: This function is not meant to be called directly by users
        Dependencies: Requires PSFramework module for error handling
        Validation: Initial parameter validation only; complete validation is done by Test-HawkInvestigationParameter
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [hashtable]$PSBoundParameters,

        [AllowNull()]
        [Nullable[DateTime]]$StartDate,

        [AllowNull()]  
        [Nullable[DateTime]]$EndDate,

        [int]$DaysToLookBack
    )

    # Check if user provided both StartDate AND DaysToLookBack
    if ($PSBoundParameters.ContainsKey('DaysToLookBack') -and $PSBoundParameters.ContainsKey('StartDate')) {
        Stop-PSFFunction -Message "DaysToLookBack cannot be used together with StartDate in non-interactive mode." -EnableException $true
    }

    # Must specify either StartDate or DaysToLookBack
    if (-not $PSBoundParameters.ContainsKey('DaysToLookBack') -and -not $PSBoundParameters.ContainsKey('StartDate')) {
        Stop-PSFFunction -Message "Either StartDate or DaysToLookBack must be specified in non-interactive mode" -EnableException $true
    }

    # Process DaysToLookBack if provided
    if ($PSBoundParameters.ContainsKey('DaysToLookBack')) {
        if ($DaysToLookBack -lt 1 -or $DaysToLookBack -gt 365) {
            Stop-PSFFunction -Message "DaysToLookBack must be between 1 and 365" -EnableException $true
        }
        
        if ($PSBoundParameters.ContainsKey('EndDate') -and -not $PSBoundParameters.ContainsKey('StartDate')) {
            # Check EndDate is not more than one day in future
            $tomorrow = (Get-Date).ToUniversalTime().Date.AddDays(1)
            if ($EndDate.ToUniversalTime().Date -gt $tomorrow) {
                Stop-PSFFunction -Message "EndDate cannot be more than one day in the future" -EnableException $true
            }

            $EndDateUTC = $EndDate.ToUniversalTime().Date.AddDays(1)
            $StartDateUTC = $EndDate.ToUniversalTime().Date.AddDays(-$DaysToLookBack)

            $StartDate = $StartDateUTC
            $EndDate = $EndDateUTC
        }
        else {
            # Convert DaysToLookBack to StartDate/EndDate
            $ConvertedDates = Convert-HawkDaysToDate -DaysToLookBack $DaysToLookBack
            $StartDate = $ConvertedDates.StartDate
            $EndDate = $ConvertedDates.EndDate
        }
    }
    else {
        # For explicit start/end dates
        if ($StartDate) {
            $StartDate = $StartDate.ToUniversalTime().Date
        }

        if ($EndDate) {
            # Validate against tomorrow to allow for the extra day
            $tomorrow = (Get-Date).ToUniversalTime().Date.AddDays(1)
            if ($EndDate.ToUniversalTime().Date -gt $tomorrow) {
                Stop-PSFFunction -Message "EndDate cannot be more than one day in the future" -EnableException $true
            }
            # Add one day to include full end date
            $EndDate = $EndDate.ToUniversalTime().Date.AddDays(1)
        }

        # Validate date range
        if ($StartDate -and $EndDate) {
            if ($StartDate -gt $EndDate) {
                Stop-PSFFunction -Message "StartDate must be before EndDate" -EnableException $true
            }

            # Test against 366 days to account for extra day added for last day inclusiveness
            $daysDifference = ($EndDate.Date - $StartDate.Date).Days
            if ($daysDifference -gt 366) {
                Stop-PSFFunction -Message "Date range cannot exceed 365 days" -EnableException $true
            }
        }
    }

    [PSCustomObject]@{
        StartDate = $StartDate
        EndDate = $EndDate 
    }
}