PowerNotes.psm1

enum Priority {
    Low = 1
    Medium = 2
    High = 3
}

class Note {
    [string]     $ID
    [string]     $Body
    [string]     $Topic
    [datetime]   $Date
    [Priority]   $Priority

    Note([string] $Body, [string] $Topic, [Priority] $Priority) {
        $this.ID = [guid]::NewGuid().ToString()
        $this.Body = $Body
        $this.Topic = $Topic
        $this.Priority = $Priority
        $this.Date = (Get-Date)
    }

    Note([string] $Body, [string] $Topic) {
        $this.ID = [guid]::NewGuid().ToString()
        $this.Body = $Body
        $this.Topic = $Topic
        $this.Priority = [Priority]::Low
        $this.Date = (Get-Date)
    }

    Note([string] $Body) {
        $this.ID = [guid]::NewGuid().ToString()
        $this.Body = $Body
        $this.Topic = 'DailyNote'
        $this.Priority = [Priority]::Low
        $this.Date = (Get-Date)
    }

    Note([hashtable] $NoteParams) {
        $this.ID = [guid]::NewGuid().ToString()
        $this.Body = $NoteParams.Body
        $this.Topic = if ($NoteParams.ContainsKey('Topic') -and $NoteParams.Topic) { $NoteParams.Topic } else { 'DailyNote' }
        $this.Priority = if ($NoteParams.ContainsKey('Priority') -and $NoteParams.Priority) { $NoteParams.Priority } else { [Priority]::Low }
        $this.Date = (Get-Date)
    }

    [void] Print() {
        # Format: "MMM dd HH:mm:ss" e.g. "Sep 12 13:02:05"
        $timestamp = $this.Date.ToString('MMM-dd HH:mm:ss')
        $color = $null
        switch ($this.Priority) {
            'Medium' { $color = 'Yellow' }
            'High' { $color = 'Red' }
            default { $color = $null }
        }

        # Use colon as separator between topic and body
        Write-Host ('[{0}] {1}: ' -f $timestamp, $this.Topic) -NoNewline

        if ($color) {
            Write-Host $this.Body -ForegroundColor $color
        } else {
            Write-Host $this.Body
        }
    }
}

function Find-Note {
    [Alias('fpn')]
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Text,
        [int] $Year = (Get-Date).Year,
        [int]$MaxCount
    )

    $filePath = Get-NotesFile -Year $Year
    $ResultOut = [System.Collections.ArrayList]::new() 

    if (-not (Test-Path $filePath)) {
        return $ResultOut
        # Quit, No file found
    }

    $rawData = Get-Content -Path $filePath
    if ([string]::IsNullOrWhiteSpace($rawData)) {
        return $ResultOut
        # Quit, No entires in the JSON
    }
    $rawData | ForEach-Object {
        $obj = $_ | ConvertFrom-Json
        $entry = [Note]::new(@{
                Body     = $obj.Body
                Topic    = $obj.Topic
                Priority = [Priority]$($obj.Priority)
            })
        $entry.ID = $obj.ID
        $entry.Date = [datetime]$obj.Date
        [void]$ResultOut.Add($entry) 
    }
    $ResultOut = $ResultOut | Where-Object { $_.Body -like "*$Text*" }
    
    if ($MaxCount) { $ResultOut = $ResultOut | Select-Object -Last $MaxCount }

    return $ResultOut
}
function Get-Note {
    [Alias('gpn')]
    [CmdletBinding()]
    param(
        [int] $Year = (Get-Date).Year,
        [int] $Count = 10,
        [switch]$All,
        #TODO Yet to Implement LAST
        [ValidateSet('Week', 'Fortnight', 'Month', 'Quarter')]
        [string]$Last,
        $Topic
    )

    $filePath = Get-NotesFile -Year $Year
    $ResultOut = [System.Collections.ArrayList]::new() 

    if (-not (Test-Path $filePath)) {
        return $ResultOut
        # Quit, No file found
    }

    $rawData = Get-Content -Path $filePath
    if ([string]::IsNullOrWhiteSpace($rawData)) {
        return $ResultOut
        # Quit, No entires in the JSON
    }
    $rawData | ForEach-Object {
        $obj = $_ | ConvertFrom-Json
        $entry = [Note]::new(@{
                Body     = $obj.Body
                Topic    = $obj.Topic
                Priority = [Priority]$($obj.Priority)
            })
        $entry.ID = $obj.ID
        $entry.Date = [datetime]$obj.Date
        [void]$ResultOut.Add($entry) 
    }
    
    if ($Last) { Write-Warning 'Feature not implemented : Last' }

    if ($Topic) {
        $ResultOut = $ResultOut | Where-Object { $_.Topic -like "*$topic*" }
    }

    if (-not $All) { 
        $ResultOut = $ResultOut | Select-Object -Last $Count 
    }
    return $ResultOut
}
function Get-NotesFile {
    param (
        [int] $Year = (Get-Date).Year,
        [switch]$Create
    )
    $baseDir = Get-NotesDir
    $fileName = "Notes-$Year.jsonl"
    $FullFilePath = Join-Path $baseDir $fileName
    if ($Create -and (-not (Test-Path $FullFilePath))) {
        New-Item $FullFilePath | Out-Null
    }
    return Join-Path $baseDir $fileName
}
function New-Note {
    [Alias('npn')]
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $true)]
        [string] $Body,
        [string] $Topic = $null,
        [priority]$Priority = [priority]::Low
    )
    $NoteParams = @{
        Body = $Body
    }
    if ($null -ne $Topic) { $NoteParams['Topic'] = $Topic }
    $NoteParams['Priority'] = $Priority
    $NoteEntry = [Note]::new($NoteParams)
    if (-not $PSCmdlet.ShouldProcess($NoteEntry, 'Will write the note')) { return }
    Add-NoteEntryToFile -Note $NoteEntry
    return $NoteEntry
}

function Add-NoteEntryToFile {
    param(
        [Note] $Note,
        $Year = (Get-Date).Year
    )
    $filePath = Get-NotesFile -Year $Year -Create
    $NoteValue = $Note | ConvertTo-Json -Compress
    $NoteValue = $NoteValue + "`n"
    try {
        [System.IO.File]::AppendAllText($filePath, $NoteValue, [Text.UTF8Encoding]::new($false))
    } catch {
        Write-Error $_
    }
}
function Get-NotesDir {

    if ($env:PowerNotesDir) {
        if (Test-Path -PathType Container -Path $env:PowerNotesDir) {
            $dir = $env:PowerNotesDir
        }
    } else {
        ## If environment is not set, assign default path based on OS
        if ($IsWindows) {
            $base = $env:APPDATA
        } elseif ($IsLinux -or $IsMacOS) {
            $base = $env:XDG_DATA_HOME
            if (-not $base) {
                if ($IsMacOS) { $base = Join-Path $HOME 'Library/Application Support' }
                else { $base = Join-Path $HOME '.local/share' }
            }
        } else { throw 'Unsupported OS' }
        $dir = Join-Path $base 'PowerNotes'
    }
    if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
    return $dir
}