Public/Session.ps1

<#
.SYNOPSIS
    Public functions for session management
 
.DESCRIPTION
    Cmdlets for starting, stopping, and managing chat sessions.
#>


function Start-ChatSession {
    <#
    .SYNOPSIS
        Start a new encrypted chat session
 
    .DESCRIPTION
        Initiates or accepts an encrypted chat session with a peer.
        Uses manual connection string or mDNS discovery.
 
    .PARAMETER Peer
        Connection string from peer (format: host:port:publickey)
 
    .PARAMETER Listen
        Start listening for incoming connections
 
    .PARAMETER Port
        Local port to use (default: random)
 
    .PARAMETER Anonymous
        Use anonymous (ephemeral) identity for this session
 
    .PARAMETER Timeout
        Session idle timeout in seconds (default: 300)
 
    .PARAMETER Discover
        Use mDNS to discover peers on local network
 
    .EXAMPLE
        Start-ChatSession -Peer "192.168.1.100:9000:BASE64KEY..."
        Connect to peer using connection string
 
    .EXAMPLE
        Start-ChatSession -Listen -Port 9000
        Listen for incoming connections on port 9000
 
    .EXAMPLE
        Start-ChatSession -Discover
        Discover and connect to peers on local network
    #>

    [CmdletBinding(DefaultParameterSetName = 'Connect')]
    param(
        [Parameter(ParameterSetName = 'Connect', Position = 0)]
        [string]$Peer,

        [Parameter(ParameterSetName = 'Listen')]
        [switch]$Listen,

        [Parameter()]
        [int]$Port = 0,

        [Parameter()]
        [switch]$Anonymous,

        [Parameter()]
        [int]$Timeout = 300,

        [Parameter(ParameterSetName = 'Discover')]
        [switch]$Discover
    )

    # Get or create identity
    $identity = $script:CurrentIdentity
    if ($Anonymous -or $null -eq $identity) {
        Write-Verbose "Creating anonymous identity for session"
        $identity = [IdentityManager]::CreateIdentity([IdentityMode]::Anonymous)
    }

    # Create session
    $session = [SessionManager]::CreateSession($identity, $Timeout)
    Write-Verbose "Created session: $($session.SessionId)"

    # Create transport
    $transport = [UdpTransport]::new($Port)
    $transport.Start()

    # Store transport in session (using script scope for now)
    $script:ActiveSessions[$session.SessionId] = @{
        Session   = $session
        Transport = $transport
    }

    if ($Discover) {
        # mDNS discovery
        Write-Host "Discovering peers on local network..." -ForegroundColor Cyan
        $discovery = [PeerDiscovery]::new($true)
        $discovery.Start()

        # Announce ourselves
        $discovery.Announce($session.SessionId, $transport.LocalPort, $identity.PublicKey)

        # Listen for peers
        $peers = $discovery.FindPeers(5000)
        $discovery.Stop()

        if ($peers.Count -eq 0) {
            Write-Warning "No peers found on local network"
            return Get-ChatSession -SessionId $session.SessionId
        }

        Write-Host "Found $($peers.Count) peer(s):" -ForegroundColor Green
        $peers | ForEach-Object { Write-Host " - $($_.Name) at $($_.Host):$($_.Port)" }

        # Connect to first discovered peer
        # For multiple peers, users can use Find-ChatPeer and Start-ChatSession -Peer with specific connection string
        $peerInfo = $peers[0]
        Write-Host "Connecting to $($peerInfo.Name)..." -ForegroundColor Cyan
        $transport.Connect($peerInfo.Host, $peerInfo.Port)
        $session.CompleteHandshake($peerInfo.PublicKey)
    }
    elseif ($Listen) {
        # Listen mode
        $endpoint = $transport.GetLocalEndpointString()
        $connectionString = "${endpoint}:$($identity.PublicKey)"

        Write-Host "Listening for connections..." -ForegroundColor Cyan
        Write-Host "Share this connection string with peer:" -ForegroundColor Yellow
        Write-Host $connectionString -ForegroundColor White
        Write-Host ""

        # Send handshake when we receive connection
        # (Simplified - real impl would wait for incoming handshake)
    }
    elseif ($Peer) {
        # Connect to peer
        $peerInfo = [ManualDiscovery]::ParseConnectionString($Peer)

        Write-Verbose "Connecting to $($peerInfo.Host):$($peerInfo.Port)"
        $transport.Connect($peerInfo.Host, $peerInfo.Port)

        # Send handshake
        $handshake = [MessageProtocol]::CreateHandshake($identity.PublicKey, $session.SessionId)
        $transport.SendString($handshake)

        # Complete our side
        $session.CompleteHandshake($peerInfo.PublicKey)

        Write-Host "Connected to peer!" -ForegroundColor Green
    }

    return Get-ChatSession -SessionId $session.SessionId
}

function Stop-ChatSession {
    <#
    .SYNOPSIS
        Stop and clean up a chat session
 
    .PARAMETER SessionId
        ID of session to stop
 
    .PARAMETER All
        Stop all active sessions
    #>

    [CmdletBinding()]
    param(
        [Parameter(Position = 0)]
        [string]$SessionId,

        [switch]$All
    )

    if ($All) {
        foreach ($sid in @($script:ActiveSessions.Keys)) {
            Stop-ChatSession -SessionId $sid
        }
        return
    }

    if (-not $SessionId) {
        # Use most recent session
        $SessionId = @($script:ActiveSessions.Keys)[-1]
        if (-not $SessionId) {
            Write-Warning "No active sessions"
            return
        }
    }

    $sessionData = $script:ActiveSessions[$SessionId]
    if ($null -eq $sessionData) {
        Write-Warning "Session not found: $SessionId"
        return
    }

    # Close session
    [SessionManager]::CloseSession($SessionId)

    # Close transport
    $sessionData.Transport.Stop()

    # Remove from active sessions
    $script:ActiveSessions.Remove($SessionId)

    Write-Host "Session $SessionId closed" -ForegroundColor Yellow
}

function Get-ChatSession {
    <#
    .SYNOPSIS
        Get information about chat sessions
 
    .PARAMETER SessionId
        Specific session ID
 
    .PARAMETER All
        List all sessions
    #>

    [CmdletBinding()]
    param(
        [Parameter(Position = 0)]
        [string]$SessionId,

        [switch]$All
    )

    if ($All -or (-not $SessionId)) {
        $sessions = $script:ActiveSessions.Values | ForEach-Object {
            $info = $_.Session.GetInfo()
            $info.LocalEndpoint = $_.Transport.GetLocalEndpointString()
            [PSCustomObject]$info
        }
        return $sessions
    }

    $sessionData = $script:ActiveSessions[$SessionId]
    if ($null -eq $sessionData) {
        Write-Warning "Session not found: $SessionId"
        return $null
    }

    $info = $sessionData.Session.GetInfo()
    $info.LocalEndpoint = $sessionData.Transport.GetLocalEndpointString()
    return [PSCustomObject]$info
}

function Get-ConnectionString {
    <#
    .SYNOPSIS
        Get connection string for current session
 
    .PARAMETER SessionId
        Session ID (defaults to most recent)
    #>

    [CmdletBinding()]
    param(
        [string]$SessionId
    )

    if (-not $SessionId) {
        $SessionId = @($script:ActiveSessions.Keys)[-1]
    }

    if (-not $SessionId) {
        throw "No active session"
    }

    $sessionData = $script:ActiveSessions[$SessionId]
    if ($null -eq $sessionData) {
        throw "Session not found: $SessionId"
    }

    $endpoint = $sessionData.Transport.GetLocalEndpointString()
    $publicKey = $script:CurrentIdentity.PublicKey

    return "${endpoint}:$publicKey"
}