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

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

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

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

    $form = Get-FormState -Html $Html -ActionContains '../process/save_service_group.php'
    $selectPattern = '<select\b[^>]*name="group_main_service_id"[^>]*>(.*?)</select>'
    $selectMatch = [regex]::Match($form.Html, $selectPattern, 'IgnoreCase, Singleline')
    if (-not $selectMatch.Success) {
        throw 'Main service select not found.'
    }

    $optionMatches = [regex]::Matches($selectMatch.Groups[1].Value, '<option\b([^>]*)value="([^"]*)"([^>]*)>', 'IgnoreCase')
    foreach ($option in $optionMatches) {
        $value = [System.Net.WebUtility]::HtmlDecode($option.Groups[2].Value).Trim()
        if ($value -eq '') {
            continue
        }

        $attributes = ($option.Groups[1].Value + ' ' + $option.Groups[3].Value)
        $categoryMatch = [regex]::Match($attributes, 'data-category-id="([^"]+)"', 'IgnoreCase')
        if (-not $categoryMatch.Success) {
            continue
        }

        return @{
            MainServiceId = $value
            CategoryId = [System.Net.WebUtility]::HtmlDecode($categoryMatch.Groups[1].Value).Trim()
        }
    }

    throw 'No selectable main service option found.'
}

function Find-CategoryRow {
    param(
        [string]$Html,
        [string]$CategoryName
    )

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

        $editMatch = [regex]::Match($rowHtml, '<a\b[^>]*href="([^"]*service_categories\.php\?category_id=\d+[^"]*)"', 'IgnoreCase')
        $deleteMatch = [regex]::Match($rowHtml, '<form\b[^>]*action="\.\./process/delete_service_category\.php"[^>]*>(.*?)</form>', 'IgnoreCase, Singleline')
        if (-not $editMatch.Success -or -not $deleteMatch.Success) {
            continue
        }

        $deleteForm = $deleteMatch.Groups[1].Value
        $categoryIdMatch = [regex]::Match($deleteForm, 'name="category_id"\s+value="(\d+)"', 'IgnoreCase')
        $returnToMatch = [regex]::Match($deleteForm, 'name="return_to"\s+value="([^"]+)"', 'IgnoreCase')
        if (-not $categoryIdMatch.Success -or -not $returnToMatch.Success) {
            continue
        }

        return @{
            EditHref = [System.Net.WebUtility]::HtmlDecode($editMatch.Groups[1].Value)
            CategoryId = $categoryIdMatch.Groups[1].Value
            ReturnTo = [System.Net.WebUtility]::HtmlDecode($returnToMatch.Groups[1].Value)
        }
    }

    throw "Category row not found: $CategoryName"
}

function Find-ServiceGroupRow {
    param(
        [string]$Html,
        [string]$GroupName
    )

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

        $editMatch = [regex]::Match($rowHtml, '<a\b[^>]*href="([^"]*service_groups\.php\?service_group_id=\d+[^"]*)"', 'IgnoreCase')
        $deleteMatch = [regex]::Match($rowHtml, '<form\b[^>]*action="\.\./process/delete_service_group\.php"[^>]*>(.*?)</form>', 'IgnoreCase, Singleline')
        if (-not $editMatch.Success -or -not $deleteMatch.Success) {
            continue
        }

        $deleteForm = $deleteMatch.Groups[1].Value
        $groupIdMatch = [regex]::Match($deleteForm, 'name="service_group_id"\s+value="(\d+)"', 'IgnoreCase')
        $returnToMatch = [regex]::Match($deleteForm, 'name="return_to"\s+value="([^"]+)"', 'IgnoreCase')
        if (-not $groupIdMatch.Success -or -not $returnToMatch.Success) {
            continue
        }

        return @{
            EditHref = [System.Net.WebUtility]::HtmlDecode($editMatch.Groups[1].Value)
            ServiceGroupId = $groupIdMatch.Groups[1].Value
            ReturnTo = [System.Net.WebUtility]::HtmlDecode($returnToMatch.Groups[1].Value)
        }
    }

    throw "Service group row not found: $GroupName"
}

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."
    }
}

function Assert-ContainsText {
    param(
        [string]$Html,
        [string]$ExpectedText,
        [string]$Label
    )

    if ($Html -notmatch [regex]::Escape($ExpectedText)) {
        throw "$Label did not contain expected text: $ExpectedText"
    }
}

$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
$stamp = Get-Date -Format 'yyyyMMddHHmmss'
$categoryName = "Smoke Category $stamp"
$categoryUpdatedName = "$categoryName Updated"
$groupName = "Smoke Group $stamp"
$groupUpdatedName = "$groupName Updated"
$results = [ordered]@{}

$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'
}

$categoryPage = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/pages/service_categories.php"
$categoryForm = Get-FormState -Html $categoryPage.Content -ActionContains '../process/save_service_category.php'
$categoryCreateResult = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/process/save_service_category.php" -Method 'POST' -Body @{
    return_to = $categoryForm.State['return_to']
    category_name = $categoryName
}
$results['category_create_flash'] = Get-FlashText -Html $categoryCreateResult.Content
Assert-NotBlank -Value $results['category_create_flash'] -Label 'Category create'

$createdCategoryRow = Find-CategoryRow -Html $categoryCreateResult.Content -CategoryName $categoryName
$results['category_id'] = $createdCategoryRow.CategoryId

$categoryEditPage = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/pages/$($createdCategoryRow.EditHref)"
$categoryEditForm = Get-FormState -Html $categoryEditPage.Content -ActionContains '../process/save_service_category.php'
$categoryUpdateResult = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/process/save_service_category.php" -Method 'POST' -Body @{
    return_to = $categoryEditForm.State['return_to']
    category_id = $categoryEditForm.State['category_id']
    category_name = $categoryUpdatedName
}
$results['category_update_flash'] = Get-FlashText -Html $categoryUpdateResult.Content
Assert-NotBlank -Value $results['category_update_flash'] -Label 'Category update'

$updatedCategoryRow = Find-CategoryRow -Html $categoryUpdateResult.Content -CategoryName $categoryUpdatedName
$categoryDeleteResult = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/process/delete_service_category.php" -Method 'POST' -Body @{
    category_id = $updatedCategoryRow.CategoryId
    return_to = $updatedCategoryRow.ReturnTo
}
$results['category_delete_flash'] = Get-FlashText -Html $categoryDeleteResult.Content
Assert-NotBlank -Value $results['category_delete_flash'] -Label 'Category delete'

$groupPage = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/pages/service_groups.php"
$groupForm = Get-FormState -Html $groupPage.Content -ActionContains '../process/save_service_group.php'
$mainServiceOption = Get-FirstMainServiceOption -Html $groupPage.Content

$groupCreateBody = @{
    return_to = $groupForm.State['return_to']
    group_category_id = $mainServiceOption.CategoryId
    group_main_service_id = $mainServiceOption.MainServiceId
    group_name = $groupName
    show_window_film_details = '1'
}
$groupCreateResult = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/process/save_service_group.php" -Method 'POST' -Body $groupCreateBody
$results['group_create_flash'] = Get-FlashText -Html $groupCreateResult.Content
Assert-NotBlank -Value $results['group_create_flash'] -Label 'Service group create'

$createdGroupRow = Find-ServiceGroupRow -Html $groupCreateResult.Content -GroupName $groupName
$results['service_group_id'] = $createdGroupRow.ServiceGroupId

$groupEditPage = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/pages/$($createdGroupRow.EditHref)"
$groupEditForm = Get-FormState -Html $groupEditPage.Content -ActionContains '../process/save_service_group.php'
$groupUpdateBody = @{
    return_to = $groupEditForm.State['return_to']
    service_group_id = $groupEditForm.State['service_group_id']
    group_category_id = $mainServiceOption.CategoryId
    group_main_service_id = $mainServiceOption.MainServiceId
    group_name = $groupUpdatedName
}
$groupUpdateResult = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/process/save_service_group.php" -Method 'POST' -Body $groupUpdateBody
$results['group_update_flash'] = Get-FlashText -Html $groupUpdateResult.Content
Assert-NotBlank -Value $results['group_update_flash'] -Label 'Service group update'
Assert-ContainsText -Html $groupUpdateResult.Content -ExpectedText $groupUpdatedName -Label 'Service group update page'

$groupDeleteResult = Invoke-FlowRequest -Session $session -Uri "$BaseUrl/process/delete_service_group.php" -Method 'POST' -Body @{
    service_group_id = $groupEditForm.State['service_group_id']
    return_to = 'service_groups.php'
}
$results['group_delete_flash'] = Get-FlashText -Html $groupDeleteResult.Content
Assert-NotBlank -Value $results['group_delete_flash'] -Label 'Service group delete'

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