# JUSTSALE Cloud Bridge Print Service
# This script polls the JUSTSALE Cloud API for pending print jobs.

$configPath = Join-Path $PSScriptRoot "cloud_printer_config.txt"
$printerConfigPath = Join-Path $PSScriptRoot "printer_config.txt"
$tempDir = Join-Path $PSScriptRoot "temp_receipts"

if (!(Test-Path $tempDir)) { New-Item -ItemType Directory -Path $tempDir | Out-Null }

# Add Raw Printing Support via Win32 API
$RawPrinterCode = @"
using System;
using System.Runtime.InteropServices;
using System.IO;

public class RawPrinterHelper {
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public class DOCINFOA {
        [MarshalAs(UnmanagedType.LPStr)] public string pDocName;
        [MarshalAs(UnmanagedType.LPStr)] public string pOutputFile;
        [MarshalAs(UnmanagedType.LPStr)] public string pDataType;
    }
    [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);
    [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool ClosePrinter(IntPtr hPrinter);
    [DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);
    [DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool EndDocPrinter(IntPtr hPrinter);
    [DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool StartPagePrinter(IntPtr hPrinter);
    [DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool EndPagePrinter(IntPtr hPrinter);
    [DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);

    public static bool SendBytesToPrinter(string szPrinterName, byte[] pBytes, Int32 dwCount, string szDocName) {
        IntPtr hPrinter = new IntPtr(0);
        DOCINFOA di = new DOCINFOA();
        bool bSuccess = false;
        di.pDocName = szDocName;
        di.pDataType = "RAW";
        if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero)) {
            if (StartDocPrinter(hPrinter, 1, di)) {
                if (StartPagePrinter(hPrinter)) {
                    IntPtr pUnmanagedBytes = Marshal.AllocCoTaskMem(dwCount);
                    Marshal.Copy(pBytes, 0, pUnmanagedBytes, dwCount);
                    Int32 dwWritten = 0;
                    bSuccess = WritePrinter(hPrinter, pUnmanagedBytes, dwCount, out dwWritten);
                    EndPagePrinter(hPrinter);
                    Marshal.FreeCoTaskMem(pUnmanagedBytes);
                }
                EndDocPrinter(hPrinter);
            }
            ClosePrinter(hPrinter);
        }
        return bSuccess;
    }
}
"@

Add-Type -TypeDefinition $RawPrinterCode -ErrorAction SilentlyContinue

function Get-Config {
    if (Test-Path $configPath) {
        $cfg = @{}
        Get-Content $configPath | ForEach-Object {
            if ($_ -match "(.+): (.+)") { $cfg[$matches[1].Trim()] = $matches[2].Trim() }
        }
        return $cfg
    }
    return $null
}

Write-Host "--------------------------------------------------"
Write-Host "     JUSTSALE CLOUD BRIDGE PRINT SERVICE"
Write-Host "--------------------------------------------------"

while($true) {
    $cfg = Get-Config
    if ($null -eq $cfg) {
        Write-Host "Waiting for cloud_printer_config.txt..." -ForegroundColor Yellow
        Start-Sleep -Seconds 5
        continue
    }

    $baseUrl = $cfg["URL"]
    $userParam = if ($cfg["User"]) { "user=$($cfg['User'])" } else { "user_id=$($cfg['UserID'])" }
    $terminal = $cfg["Terminal"]
    $interval = [int]($cfg["Interval"] -or 2)
    $printer = "Xlife D82"
    if (Test-Path $printerConfigPath) { $printer = (Get-Content $printerConfigPath -Raw).Trim() }

    try {
        $url = "$($baseUrl)/api/print.php?action=get_pending&$userParam&terminal=$terminal"
        $response = Invoke-RestMethod -Uri $url -Method Get -ErrorAction Stop

        if ($response.success) {
            $jobId = $response.job_id
            $fileName = $response.filename
            $ext = $response.extension
            $contentBytes = [System.Convert]::FromBase64String($response.content)
            
            Write-Host "[$(Get-Date -Format 'HH:mm:ss')] New Print Job: $fileName" -ForegroundColor Green
            
            if ($ext -eq "txt") {
                [RawPrinterHelper]::SendBytesToPrinter($printer, $contentBytes, $contentBytes.Length, "Receipt #$($response.sale_id)")
            } else {
                $tmpPath = Join-Path $tempDir $fileName
                [System.IO.File]::WriteAllBytes($tmpPath, $contentBytes)
                Start-Process -FilePath $tmpPath -Verb Print -WindowStyle Hidden
                Start-Sleep -Seconds 3
            }

            # Mark as done
            $doneUrl = "$($baseUrl)/api/print.php?action=mark_printed&id=$jobId&$userParam"
            Invoke-RestMethod -Uri $doneUrl -Method Get | Out-Null
            Write-Host "Job #$jobId marked as printed."
        }
    } catch {
        Write-Host "Error polling cloud: $($_.Exception.Message)" -ForegroundColor Red
    }

    Start-Sleep -Seconds $interval
}
