[CmdletBinding()]
param(
    [string]$BaseUrl = 'http://127.0.0.1/CCW',
    [string]$Username = 'admin',
    [string]$Password = 'admin123'
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8

function Get-FlashText {
    param([string]$Html)

    $match = [regex]::Match($Html, '<div class="flash[^"]*">\s*(.*?)\s*</div>', 'IgnoreCase, Singleline')
    if (-not $match.Success) {
        return ''
    }

    $text = [regex]::Replace($match.Groups[1].Value, '<[^>]+>', '')
    return [System.Net.WebUtility]::HtmlDecode($text).Trim()
}

function Get-FirstEditHref {
    param(
        [string]$Html,
        [string]$Fragment
    )

    $links = [regex]::Matches($Html, '<a\b[^>]*href="([^"]+)"[^>]*>', 'IgnoreCase')
    foreach ($link in $links) {
        $href = [System.Net.WebUtility]::HtmlDecode($link.Groups[1].Value)
        if ($href.Contains($Fragment)) {
            return $href
        }
    }

    throw "Edit link not found for fragment: $Fragment"
}

function Get-FormMarkup {
    param(
        [string]$Html,
        [string]$ActionContains
    )

    $forms = [regex]::Matches($Html, '<form\b[^>]*action="([^"]*)"[^>]*>(.*?)</form>', 'IgnoreCase, Singleline')
    foreach ($form in $forms) {
        $action = [System.Net.WebUtility]::HtmlDecode($form.Groups[1].Value)
        if ($action.Contains($ActionContains)) {
            return [System.Net.WebUtility]::HtmlDecode($form.Groups[2].Value)
        }
    }

    throw "Form not found for action: $ActionContains"
}

function Get-HiddenInputs {
    param([string]$FormHtml)

    $state = @{}
    $hiddenInputs = [regex]::Matches($FormHtml, '<input\b[^>]*type="hidden"[^>]*name="([^"]+)"[^>]*value="([^"]*)"', 'IgnoreCase')
    foreach ($input in $hiddenInputs) {
        $name = [System.Net.WebUtility]::HtmlDecode($input.Groups[1].Value)
        $value = [System.Net.WebUtility]::HtmlDecode($input.Groups[2].Value)
        $state[$name] = $value
    }

    return $state
}

function Get-InputValue {
    param(
        [string]$FormHtml,
        [string]$Name
    )

    $pattern = '<input\b[^>]*name="' + [regex]::Escape($Name) + '"[^>]*value="([^"]*)"'
    $match = [regex]::Match($FormHtml, $pattern, 'IgnoreCase')
    if (-not $match.Success) {
        return ''
    }

    return [System.Net.WebUtility]::HtmlDecode($match.Groups[1].Value)
}

function Get-SelectSelectedValue {
    param(
        [string]$FormHtml,
        [string]$Name
    )

    $selectPattern = '<select\b[^>]*name="' + [regex]::Escape($Name) + '"[^>]*>(.*?)</select>'
    $selectMatch = [regex]::Match($FormHtml, $selectPattern, 'IgnoreCase, Singleline')
    if (-not $selectMatch.Success) {
        return ''
    }

    $selectedMatch = [regex]::Match($selectMatch.Groups[1].Value, '<option\b[^>]*value="([^"]*)"[^>]*selected[^>]*>', 'IgnoreCase')
    if ($selectedMatch.Success) {
        return [System.Net.WebUtility]::HtmlDecode($selectedMatch.Groups[1].Value)
    }

    $firstMatch = [regex]::Match($selectMatch.Groups[1].Value, '<option\b[^>]*value="([^"]*)"', 'IgnoreCase')
    if ($firstMatch.Success) {
        return [System.Net.WebUtility]::HtmlDecode($firstMatch.Groups[1].Value)
    }

    return ''
}

function Get-CheckedValues {
    param(
        [string]$FormHtml,
        [string]$Name
    )

    $values = New-Object System.Collections.Generic.List[string]
    $pattern = '<input\b[^>]*name="' + [regex]::Escape($Name) + '"[^>]*value="([^"]*)"[^>]*checked[^>]*>'
    $matches = [regex]::Matches($FormHtml, $pattern, 'IgnoreCase')
    foreach ($match in $matches) {
        $values.Add([System.Net.WebUtility]::HtmlDecode($match.Groups[1].Value)) | Out-Null
    }

    return $values.ToArray()
}

function Is-CheckboxChecked {
    param(
        [string]$FormHtml,
        [string]$Name
    )

    $pattern = '<input\b[^>]*name="' + [regex]::Escape($Name) + '"[^>]*checked[^>]*>'
    return [regex]::IsMatch($FormHtml, $pattern, 'IgnoreCase')
}

function Invoke-FlowRequest {
    param(
        [Microsoft.PowerShell.Commands.WebRequestSession]$Session,
        [string]$Uri,
        [string]$Method = 'GET',
        [hashtable]$Body = @{}
    )

    $params = @{
        Uri = $Uri
        Method = $Method
        WebSession = $Session
        UseBasicParsing = $true
    }

    if ($Method -eq 'POST') {
        $params.Body = $Body
    }

    return Invoke-WebRequest @params
}

function Assert-NotBlank {
    param(
        [string]$Value,
        [string]$Label
    )

    if ([string]::IsNullOrWhiteSpace($Value)) {
        throw "$Label flash was blank."
    }
}

$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
$stamp = Get-Date -Format 'yyyyMMddHHmmss'
$results = [ordered]@{}

$loginPage = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/pages/login.php"
$loginForm = Get-FormMarkup -Html $loginPage.Content -ActionContains '../process/login.php'
$loginHidden = Get-HiddenInputs -FormHtml $loginForm
$null = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/process/login.php" -Method 'POST' -Body @{
    csrf_token = $loginHidden['csrf_token']
    username = $Username
    password = $Password
    display_mode = 'app'
}

$queuePage = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/pages/queue.php"
$plateMatch = [regex]::Match($queuePage.Content, '<span class="plate">([^<]+)</span>', 'IgnoreCase')
if (-not $plateMatch.Success) {
    throw 'Plate number was not found on queue page for member smoke lookup.'
}
$memberLookupPlate = [System.Net.WebUtility]::HtmlDecode($plateMatch.Groups[1].Value).Trim()
$results['member_lookup_plate'] = $memberLookupPlate

$memberListPage = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/pages/member.php?view=list&q=$([uri]::EscapeDataString($memberLookupPlate))"
$memberEditHref = Get-FirstEditHref -Html $memberListPage.Content -Fragment '#member-edit-form'
$memberEditPage = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/pages/$memberEditHref"
$memberForm = Get-FormMarkup -Html $memberEditPage.Content -ActionContains '../process/save_member.php'
$memberHidden = Get-HiddenInputs -FormHtml $memberForm
$memberOriginal = @{
    name = Get-InputValue -FormHtml $memberForm -Name 'name'
    phone = Get-InputValue -FormHtml $memberForm -Name 'phone'
    email = Get-InputValue -FormHtml $memberForm -Name 'email'
    customer_group = Get-SelectSelectedValue -FormHtml $memberForm -Name 'customer_group'
}
$memberUpdatedPhone = if ([string]::IsNullOrWhiteSpace($memberOriginal.phone)) { '099' + $stamp.Substring($stamp.Length - 7) } else { $memberOriginal.phone + '9' }

$memberUpdateBody = @{
    customer_id = $memberHidden['customer_id']
    view = $memberHidden['view']
    q = $memberHidden['q']
    group = $memberHidden['group']
    name = $memberOriginal.name
    phone = $memberUpdatedPhone
    email = $memberOriginal.email
    customer_group = $memberOriginal.customer_group
}

$memberUpdateResult = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/process/save_member.php" -Method 'POST' -Body $memberUpdateBody
$results['member_update_flash'] = Get-FlashText -Html $memberUpdateResult.Content
Assert-NotBlank -Value $results['member_update_flash'] -Label 'Member update'

$memberRestoreBody = @{
    customer_id = $memberHidden['customer_id']
    view = $memberHidden['view']
    q = $memberHidden['q']
    group = $memberHidden['group']
    name = $memberOriginal.name
    phone = $memberOriginal.phone
    email = $memberOriginal.email
    customer_group = $memberOriginal.customer_group
}

$memberRestoreResult = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/process/save_member.php" -Method 'POST' -Body $memberRestoreBody
$results['member_restore_flash'] = Get-FlashText -Html $memberRestoreResult.Content
Assert-NotBlank -Value $results['member_restore_flash'] -Label 'Member restore'

$employeePage = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/pages/employees.php"
$employeeEditHref = Get-FirstEditHref -Html $employeePage.Content -Fragment '#employee-form-panel'
$employeeEditPage = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/pages/$employeeEditHref"
$employeeForm = Get-FormMarkup -Html $employeeEditPage.Content -ActionContains '../process/save_employee.php'
$employeeHidden = Get-HiddenInputs -FormHtml $employeeForm
$employeeOriginal = @{
    name = Get-InputValue -FormHtml $employeeForm -Name 'name'
    phone = Get-InputValue -FormHtml $employeeForm -Name 'phone'
    username = Get-InputValue -FormHtml $employeeForm -Name 'username'
    position_id = Get-SelectSelectedValue -FormHtml $employeeForm -Name 'position_id'
    branch_scope = Get-SelectSelectedValue -FormHtml $employeeForm -Name 'branch_scope'
    default_start_page = Get-SelectSelectedValue -FormHtml $employeeForm -Name 'default_start_page'
    branch_names = Get-CheckedValues -FormHtml $employeeForm -Name 'branch_names[]'
    is_active = Is-CheckboxChecked -FormHtml $employeeForm -Name 'is_active'
}
$employeeBranchNames = @($employeeOriginal.branch_names)
$employeeUpdatedPhone = if ([string]::IsNullOrWhiteSpace($employeeOriginal.phone)) { '088' + $stamp.Substring($stamp.Length - 7) } else { $employeeOriginal.phone + '7' }

$employeeUpdateBody = @{
    employee_id = $employeeHidden['employee_id']
    filter_position_id = $employeeHidden['filter_position_id']
    name = $employeeOriginal.name
    phone = $employeeUpdatedPhone
    username = $employeeOriginal.username
    password = ''
    position_id = $employeeOriginal.position_id
    branch_scope = $employeeOriginal.branch_scope
    default_start_page = $employeeOriginal.default_start_page
}
if ($employeeOriginal.is_active) {
    $employeeUpdateBody['is_active'] = '1'
}
if ($employeeBranchNames.Count -gt 0) {
    $employeeUpdateBody['branch_names[]'] = $employeeBranchNames
}

$employeeUpdateResult = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/process/save_employee.php" -Method 'POST' -Body $employeeUpdateBody
$results['employee_update_flash'] = Get-FlashText -Html $employeeUpdateResult.Content
Assert-NotBlank -Value $results['employee_update_flash'] -Label 'Employee update'

$employeeRestoreBody = @{
    employee_id = $employeeHidden['employee_id']
    filter_position_id = $employeeHidden['filter_position_id']
    name = $employeeOriginal.name
    phone = $employeeOriginal.phone
    username = $employeeOriginal.username
    password = ''
    position_id = $employeeOriginal.position_id
    branch_scope = $employeeOriginal.branch_scope
    default_start_page = $employeeOriginal.default_start_page
}
if ($employeeOriginal.is_active) {
    $employeeRestoreBody['is_active'] = '1'
}
if ($employeeBranchNames.Count -gt 0) {
    $employeeRestoreBody['branch_names[]'] = $employeeBranchNames
}

$employeeRestoreResult = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/process/save_employee.php" -Method 'POST' -Body $employeeRestoreBody
$results['employee_restore_flash'] = Get-FlashText -Html $employeeRestoreResult.Content
Assert-NotBlank -Value $results['employee_restore_flash'] -Label 'Employee restore'

$results.GetEnumerator() | ForEach-Object {
    "{0}: {1}" -f $_.Key, $_.Value
}
