[CmdletBinding()]
param(
    [string]$BaseUrl = 'http://127.0.0.1/CCW',
    [string]$Username = 'admin',
    [string]$Password = 'admin123',
    [string]$ProbeUsername = 'branch_probe',
    [string]$ProbePassword = 'branchprobe123',
    [string]$ProbeName = 'Branch Scope Probe',
    [string]$ProbePhone = '',
    [string[]]$AllowedBranches = @(),
    [string]$PreferredPosition = 'Administrator',
    [string]$DefaultStartPage = ''
)

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-FormState {
    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 (-not $action.Contains($ActionContains)) {
            continue
        }

        $state = @{}
        $hiddenInputs = [regex]::Matches($form.Groups[2].Value, '<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 @{
            Action = $action
            Html = $form.Groups[2].Value
            State = $state
        }
    }

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

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-SelectOptions {
    param(
        [string]$Html,
        [string]$Name
    )

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

    $options = New-Object System.Collections.Generic.List[object]
    $optionMatches = [regex]::Matches($selectMatch.Groups[1].Value, '<option\b[^>]*value="([^"]*)"[^>]*>(.*?)</option>', 'IgnoreCase, Singleline')
    foreach ($option in $optionMatches) {
        $value = [System.Net.WebUtility]::HtmlDecode($option.Groups[1].Value).Trim()
        $label = [regex]::Replace($option.Groups[2].Value, '<[^>]+>', '')
        $label = [System.Net.WebUtility]::HtmlDecode($label).Trim()
        if ($value -eq '') {
            continue
        }

        $options.Add([pscustomobject]@{
            Value = $value
            Label = $label
        }) | Out-Null
    }

    return $options.ToArray()
}

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)
    }

    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 Find-EmployeeEditHref {
    param(
        [string]$Html,
        [string]$Username
    )

    $rows = [regex]::Matches($Html, '<tr>(.*?)</tr>', 'IgnoreCase, Singleline')
    foreach ($row in $rows) {
        $rowHtml = $row.Groups[1].Value
        if ($rowHtml -notmatch [regex]::Escape($Username)) {
            continue
        }

        $editMatch = [regex]::Match($rowHtml, '<a\b[^>]*href="([^"]*employees\.php\?employee_id=\d+[^"]*)"', 'IgnoreCase')
        if ($editMatch.Success) {
            return [System.Net.WebUtility]::HtmlDecode($editMatch.Groups[1].Value)
        }
    }

    return ''
}

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 Login-Session {
    param(
        [Microsoft.PowerShell.Commands.WebRequestSession]$Session,
        [string]$BaseUrl,
        [string]$Username,
        [string]$Password
    )

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

if ($AllowedBranches.Count -eq 0) {
    throw 'AllowedBranches is required.'
}

$normalizedAllowedBranches = @($AllowedBranches | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object { $_.Trim() } | Sort-Object -Unique)
if ($normalizedAllowedBranches.Count -eq 0) {
    throw 'AllowedBranches is required.'
}

if ([string]::IsNullOrWhiteSpace($ProbePhone)) {
    $ProbePhone = '086' + (Get-Date -Format 'HHmmss')
}

$preferredLabels = @($PreferredPosition, 'Administrator', 'Branch Manager', 'Service Advisor') |
    Where-Object { -not [string]::IsNullOrWhiteSpace($_) } |
    ForEach-Object { $_.Trim() } |
    Select-Object -Unique

$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
$results = [ordered]@{}

Login-Session -Session $session -BaseUrl $BaseUrl -Username $Username -Password $Password

$employeesPage = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/pages/employees.php?show_form=1"
$employeesHtml = [string]$employeesPage.Content
$positionOptions = @(Get-SelectOptions -Html $employeesHtml -Name 'position_id')
$selectedPosition = $null
foreach ($preferredLabel in $preferredLabels) {
    $selectedPosition = $positionOptions | Where-Object { $_.Label -eq $preferredLabel } | Select-Object -First 1
    if ($null -ne $selectedPosition) {
        break
    }
}
if ($null -eq $selectedPosition) {
    throw 'No suitable position found for probe account.'
}

$existingEditHref = Find-EmployeeEditHref -Html $employeesHtml -Username $ProbeUsername
$formState = $null
$employeeId = 0
$editing = $false
$currentFormHtml = ''

if ($existingEditHref -ne '') {
    $editing = $true
    $editPage = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/pages/$existingEditHref"
    $formState = Get-FormState -Html ([string]$editPage.Content) -ActionContains '../process/save_employee.php'
    $currentFormHtml = [string]$formState.Html
    $employeeId = [int]$formState.State['employee_id']
} else {
    $formState = Get-FormState -Html $employeesHtml -ActionContains '../process/save_employee.php'
    $currentFormHtml = [string]$formState.Html
}

$body = @{
    filter_position_id = $formState.State['filter_position_id']
    name = $ProbeName
    phone = $ProbePhone
    username = $ProbeUsername
    password = $ProbePassword
    position_id = [string]$selectedPosition.Value
    branch_scope = 'assigned'
    default_start_page = $DefaultStartPage
    is_active = '1'
}

if ($normalizedAllowedBranches.Count -eq 1) {
    $body['branch_names[]'] = $normalizedAllowedBranches[0]
} else {
    $body['branch_names[]'] = $normalizedAllowedBranches
}

if ($editing -and $employeeId -gt 0) {
    $body['employee_id'] = [string]$employeeId
}

$saveResult = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/process/save_employee.php" -Method 'POST' -Body $body
$flash = Get-FlashText -Html ([string]$saveResult.Content)
if ([string]::IsNullOrWhiteSpace($flash)) {
    throw 'Probe account save flash was blank.'
}

$savedHtml = [string]$saveResult.Content
$savedEditHref = Find-EmployeeEditHref -Html $savedHtml -Username $ProbeUsername
if ($savedEditHref -eq '') {
    throw 'Probe account edit link could not be resolved after save.'
}

$savedEditPage = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/pages/$savedEditHref"
$savedFormState = Get-FormState -Html ([string]$savedEditPage.Content) -ActionContains '../process/save_employee.php'
$savedFormHtml = [string]$savedFormState.Html
$savedEmployeeId = [int]$savedFormState.State['employee_id']
$savedBranches = @(Get-CheckedValues -FormHtml $savedFormHtml -Name 'branch_names[]')
$savedScope = Get-SelectSelectedValue -FormHtml $savedFormHtml -Name 'branch_scope'
$savedStartPage = Get-SelectSelectedValue -FormHtml $savedFormHtml -Name 'default_start_page'

$results['action'] = if ($editing) { 'updated' } else { 'created' }
$results['flash'] = $flash
$results['employee_id'] = $savedEmployeeId
$results['username'] = $ProbeUsername
$results['position'] = [string]$selectedPosition.Label
$results['branch_scope'] = $savedScope
$results['allowed_branches'] = ($savedBranches -join ', ')
$results['default_start_page'] = if ([string]::IsNullOrWhiteSpace($savedStartPage)) { '[auto]' } else { $savedStartPage }

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