VBAF.Center.FakeTMS.ps1

#Requires -Version 5.1
<#
.SYNOPSIS
    VBAF-Center — Fake TMS Server
.DESCRIPTION
    Simulates a real Transport Management System that accepts
    write commands from VBAF-Center Phase 18 Write-back.

    Runs as a local HTTP server on port 8082.
    Accepts POST commands and returns realistic responses.
    Logs every command received with timestamp.

    When a real customer gives API write access — replace the
    fake URL with their real URL. Everything else stays the same.

    Functions:
      Start-VBAFFakeTMS — start the fake TMS server
      Stop-VBAFFakeTMS — stop the fake TMS server
      Get-VBAFFakeTMSLog — show all commands received
      Clear-VBAFFakeTMSLog — reset the log
#>


$script:FakeTMSListener = $null
$script:FakeTMSRunning  = $false
$script:FakeTMSPort     = 8082
$script:FakeTMSLogPath  = Join-Path $env:USERPROFILE "VBAFCenter\faketms"
$script:FakeTMSLog      = @()

# Fake fleet state — trucks and jobs
$script:FakeTMSFleet = @(
    [PSCustomObject]@{ TruckID="DK-4471"; Driver="Lars Nielsen";    Status="Idle";   Location="Roskilde";   Job="" }
    [PSCustomObject]@{ TruckID="DK-3892"; Driver="Mette Andersen";  Status="Active"; Location="Copenhagen"; Job="J-2847" }
    [PSCustomObject]@{ TruckID="DK-5511"; Driver="Søren Pedersen";  Status="Active"; Location="Odense";     Job="J-2901" }
    [PSCustomObject]@{ TruckID="DK-2244"; Driver="Hanne Christensen";Status="Idle";  Location="Aarhus";     Job="" }
    [PSCustomObject]@{ TruckID="DK-7731"; Driver="Peter Jensen";    Status="Active"; Location="Aalborg";    Job="J-2955" }
)

$script:FakeTMSJobs = @(
    [PSCustomObject]@{ JobID="J-2847"; Customer="Netto Lager";    From="Copenhagen"; To="Roskilde";  Status="InProgress"; ETA="14:23" }
    [PSCustomObject]@{ JobID="J-2901"; Customer="Coop DC";        From="Odense";     To="Svendborg"; Status="InProgress"; ETA="15:10" }
    [PSCustomObject]@{ JobID="J-2955"; Customer="Arla Foods";     From="Aalborg";    To="Viborg";    Status="InProgress"; ETA="13:45" }
    [PSCustomObject]@{ JobID="J-3001"; Customer="PostNord";       From="Roskilde";   To="Køge";      Status="Pending";    ETA="16:00" }
    [PSCustomObject]@{ JobID="J-3002"; Customer="Lidl DC";        From="Aarhus";     To="Silkeborg"; Status="Pending";    ETA="17:30" }
)

function Initialize-VBAFFakeTMSStore {
    if (-not (Test-Path $script:FakeTMSLogPath)) {
        New-Item -ItemType Directory -Path $script:FakeTMSLogPath -Force | Out-Null
    }
}

# ============================================================
# PROCESS INCOMING COMMAND
# ============================================================
function Invoke-VBAFFakeTMSCommand {
    param(
        [string] $Endpoint,
        [string] $Body
    )

    $timestamp   = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
    $actionNames = @{
        "/api/assign" = "Assign truck to job"
        "/api/route"  = "Reroute truck"
        "/api/alert"  = "Send alert"
        "/api/status" = "Get fleet status"
        "/api/fleet"  = "Get fleet list"
        "/api/jobs"   = "Get job list"
    }

    $actionName = if ($actionNames.ContainsKey($Endpoint)) { $actionNames[$Endpoint] } else { "Unknown command" }

    # Parse body if JSON
    $params = @{}
    try {
        if ($Body -and $Body -ne "") {
            $parsed = $Body | ConvertFrom-Json
            $parsed.PSObject.Properties | ForEach-Object { $params[$_.Name] = $_.Value }
        }
    } catch {}

    # Build response based on endpoint
    $response = switch ($Endpoint) {

        "/api/assign" {
            $truckID = if ($params.truck) { $params.truck } else { "DK-4471" }
            $jobID   = if ($params.job)   { $params.job   } else { "J-3001"  }

            # Update fake fleet state
            $truck = $script:FakeTMSFleet | Where-Object { $_.TruckID -eq $truckID }
            $job   = $script:FakeTMSJobs  | Where-Object { $_.JobID   -eq $jobID   }

            if ($truck) { $truck.Status = "Active"; $truck.Job = $jobID }
            if ($job)   { $job.Status = "Assigned" }

            $eta = (Get-Date).AddMinutes((Get-Random -Minimum 15 -Maximum 60)).ToString("HH:mm")

            [PSCustomObject]@{
                success  = $true
                command  = "assign"
                truck    = $truckID
                job      = $jobID
                eta      = $eta
                message  = "Truck $truckID assigned to job $jobID. ETA $eta."
                timestamp = $timestamp
            }
        }

        "/api/route" {
            $truckID = if ($params.truck) { $params.truck } else { "DK-4471" }
            $route   = if ($params.route) { $params.route } else { "via-ringvej" }

            $eta = (Get-Date).AddMinutes((Get-Random -Minimum 10 -Maximum 45)).ToString("HH:mm")

            [PSCustomObject]@{
                success   = $true
                command   = "route"
                truck     = $truckID
                route     = $route
                eta       = $eta
                distance  = (Get-Random -Minimum 15 -Maximum 80)
                message   = "Truck $truckID rerouted via $route. New ETA $eta."
                timestamp = $timestamp
            }
        }

        "/api/alert" {
            $alertType = if ($params.type)    { $params.type    } else { "warning" }
            $message   = if ($params.message) { $params.message } else { "VBAF alert" }
            $contact   = if ($params.contact) { $params.contact } else { "dispatcher" }

            [PSCustomObject]@{
                success   = $true
                command   = "alert"
                type      = $alertType
                message   = $message
                contact   = $contact
                notified  = $true
                result    = ("Alert sent to {0} - {1}" -f $contact, $message)
                timestamp = $timestamp
            }
        }

        "/api/status" {
            [PSCustomObject]@{
                success      = $true
                command      = "status"
                fleet        = $script:FakeTMSFleet
                activeTrucks = @($script:FakeTMSFleet | Where-Object { $_.Status -eq "Active" }).Count
                idleTrucks   = @($script:FakeTMSFleet | Where-Object { $_.Status -eq "Idle"   }).Count
                pendingJobs  = @($script:FakeTMSJobs  | Where-Object { $_.Status -eq "Pending" }).Count
                timestamp    = $timestamp
            }
        }

        "/api/fleet" {
            [PSCustomObject]@{
                success = $true
                command = "fleet"
                trucks  = $script:FakeTMSFleet
                count   = $script:FakeTMSFleet.Count
                timestamp = $timestamp
            }
        }

        "/api/jobs" {
            [PSCustomObject]@{
                success = $true
                command = "jobs"
                jobs    = $script:FakeTMSJobs
                count   = $script:FakeTMSJobs.Count
                timestamp = $timestamp
            }
        }

        default {
            [PSCustomObject]@{
                success   = $false
                command   = "unknown"
                endpoint  = $Endpoint
                message   = "Unknown endpoint. Available: /api/assign /api/route /api/alert /api/status /api/fleet /api/jobs"
                timestamp = $timestamp
            }
        }
    }

    # Log the command
    $logEntry = [PSCustomObject]@{
        Timestamp  = $timestamp
        Endpoint   = $Endpoint
        Action     = $actionName
        Parameters = ($params.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join " | "
        Success    = $response.success
        Response   = $response.message
    }

    $script:FakeTMSLog += $logEntry

    # Save log to file
    Initialize-VBAFFakeTMSStore
    $logFile = Join-Path $script:FakeTMSLogPath "faketms-log.json"
    $script:FakeTMSLog | ConvertTo-Json -Depth 5 | Set-Content $logFile -Encoding UTF8

    # Console output
    $color = if ($response.success) { "Green" } else { "Red" }
    Write-Host ("")
    Write-Host (" [{0}] {1}" -f $timestamp, $actionName) -ForegroundColor $color
    if ($params.Count -gt 0) {
        Write-Host (" Params : {0}" -f $logEntry.Parameters) -ForegroundColor White
    }
    Write-Host (" Result : {0}" -f $response.message) -ForegroundColor $color
    Write-Host ("")

    return $response
}

# ============================================================
# START-VBAFFAKETMS
# ============================================================
function Start-VBAFFakeTMS {
    param([int] $Port = 8082)

    if ($script:FakeTMSRunning) {
        Write-Host "Fake TMS already running at http://localhost:$script:FakeTMSPort" -ForegroundColor Yellow
        return
    }

    $script:FakeTMSPort    = $Port
    $script:FakeTMSRunning = $true
    $script:FakeTMSLog     = @()

    Write-Host ""
    Write-Host " +--------------------------------------------------+" -ForegroundColor Cyan
    Write-Host " | VBAF Fake TMS Server |" -ForegroundColor Cyan
    Write-Host " | Simulates a real Transport Management System |" -ForegroundColor Cyan
    Write-Host " +--------------------------------------------------+" -ForegroundColor Cyan
    Write-Host (" URL : http://localhost:{0}" -f $Port) -ForegroundColor White
    Write-Host ""
    Write-Host " Available endpoints:" -ForegroundColor Yellow
    Write-Host " POST http://localhost:$Port/api/assign — assign truck to job" -ForegroundColor White
    Write-Host " POST http://localhost:$Port/api/route — reroute truck"       -ForegroundColor White
    Write-Host " POST http://localhost:$Port/api/alert — send alert"          -ForegroundColor White
    Write-Host " GET http://localhost:$Port/api/status — fleet status"        -ForegroundColor White
    Write-Host " GET http://localhost:$Port/api/fleet — list all trucks"     -ForegroundColor White
    Write-Host " GET http://localhost:$Port/api/jobs — list all jobs"       -ForegroundColor White
    Write-Host ""
    Write-Host " Fleet loaded: $($script:FakeTMSFleet.Count) trucks, $($script:FakeTMSJobs.Count) jobs" -ForegroundColor Green
    Write-Host " Waiting for commands from VBAF Write-back..." -ForegroundColor DarkGray
    Write-Host " Press Ctrl+C to stop." -ForegroundColor DarkGray
    Write-Host ""

    $listener = [System.Net.HttpListener]::new()
    $listener.Prefixes.Add("http://localhost:$Port/")
    $listener.Start()
    $script:FakeTMSListener = $listener

    try {
        while ($script:FakeTMSRunning) {
            $context  = $listener.GetContext()
            $request  = $context.Request
            $response = $context.Response

            $endpoint = $request.Url.AbsolutePath
            $method   = $request.HttpMethod

            # Read body
            $body = ""
            if ($request.HasEntityBody) {
                $reader = [System.IO.StreamReader]::new($request.InputStream)
                $body   = $reader.ReadToEnd()
                $reader.Close()
            }

            # Process command
            $result     = Invoke-VBAFFakeTMSCommand -Endpoint $endpoint -Body $body
            $jsonResult = $result | ConvertTo-Json -Depth 5

            # Send response
            $buffer = [System.Text.Encoding]::UTF8.GetBytes($jsonResult)
            $response.ContentType     = "application/json; charset=utf-8"
            $response.ContentLength64 = $buffer.Length
            $response.StatusCode      = if ($result.success) { 200 } else { 400 }
            $response.OutputStream.Write($buffer, 0, $buffer.Length)
            $response.OutputStream.Close()
        }
    }
    finally {
        $listener.Stop()
        $script:FakeTMSRunning = $false
        Write-Host " Fake TMS stopped." -ForegroundColor Yellow
    }
}

# ============================================================
# STOP-VBAFFAKETMS
# ============================================================
function Stop-VBAFFakeTMS {
    $script:FakeTMSRunning = $false
    if ($script:FakeTMSListener) {
        $script:FakeTMSListener.Stop()
        Write-Host "Fake TMS stopped." -ForegroundColor Yellow
    }
}

# ============================================================
# GET-VBAFFAKETMSLOG
# ============================================================
function Get-VBAFFakeTMSLog {
    param([int] $Last = 20)

    Initialize-VBAFFakeTMSStore
    $logFile = Join-Path $script:FakeTMSLogPath "faketms-log.json"

    $log = @()
    if (Test-Path $logFile) {
        try { $log = @(Get-Content $logFile -Raw | ConvertFrom-Json) } catch {}
    }

    if ($log.Count -eq 0 -and $script:FakeTMSLog.Count -gt 0) {
        $log = $script:FakeTMSLog
    }

    if ($log.Count -eq 0) {
        Write-Host "No commands received yet." -ForegroundColor Yellow
        return
    }

    $recent = $log | Select-Object -Last $Last

    Write-Host ""
    Write-Host "Fake TMS Command Log (last $($recent.Count)):" -ForegroundColor Cyan
    Write-Host (" {0,-22} {1,-15} {2,-30} {3}" -f "Timestamp","Action","Parameters","Result") -ForegroundColor Yellow
    Write-Host (" {0}" -f ("-" * 90)) -ForegroundColor DarkGray

    foreach ($entry in $recent) {
        $color = if ($entry.Success) { "Green" } else { "Red" }
        Write-Host (" {0,-22} {1,-15} {2,-30} {3}" -f `
            $entry.Timestamp,
            $entry.Action,
            $entry.Parameters,
            $entry.Response) -ForegroundColor $color
    }
    Write-Host ""
    return $log
}

# ============================================================
# CLEAR-VBAFFAKETMSLOG
# ============================================================
function Clear-VBAFFakeTMSLog {
    $script:FakeTMSLog = @()
    $logFile = Join-Path $script:FakeTMSLogPath "faketms-log.json"
    if (Test-Path $logFile) { Remove-Item $logFile -Force }
    Write-Host "Fake TMS log cleared." -ForegroundColor Green
}

# ============================================================
# LOAD MESSAGE
# ============================================================
Write-Host ""
Write-Host " +--------------------------------------------------+" -ForegroundColor Cyan
Write-Host " | VBAF Fake TMS Server loaded |" -ForegroundColor Cyan
Write-Host " | Simulates a real TMS for Write-back testing |" -ForegroundColor Cyan
Write-Host " +--------------------------------------------------+" -ForegroundColor Cyan
Write-Host ""
Write-Host " Start-VBAFFakeTMS — start fake TMS on port 8082" -ForegroundColor White
Write-Host " Stop-VBAFFakeTMS — stop fake TMS"               -ForegroundColor White
Write-Host " Get-VBAFFakeTMSLog — show received commands"      -ForegroundColor White
Write-Host " Clear-VBAFFakeTMSLog — reset the log"               -ForegroundColor White
Write-Host ""
Write-Host " Fleet: $($script:FakeTMSFleet.Count) trucks ready" -ForegroundColor Green
Write-Host " Jobs : $($script:FakeTMSJobs.Count) jobs ready"    -ForegroundColor Green
Write-Host ""