#!/usr/bin/env python3
"""
Procedural Flow Seed Script for ResolutionFlow.

Creates sample procedural flows (step-by-step project templates) with intake forms
for common MSP project work.

Run from the backend directory with: python -m scripts.seed_procedural_flows

Requirements:
- Backend server must be running (uvicorn app.main:app)
"""

import asyncio
import argparse
import httpx
from typing import Any


# API Configuration
API_BASE_URL = "http://localhost:8000/api/v1"

ADMIN_EMAIL = None
ADMIN_PASSWORD = None


# =============================================================================
# PROCEDURAL FLOW DEFINITIONS
# =============================================================================

def get_domain_controller_flow() -> dict[str, Any]:
    """Domain Controller & Active Directory Setup — full build procedure."""
    return {
        "name": "Domain Controller & Active Directory Setup",
        "description": "Complete procedure for building a new domain controller, installing Active Directory Domain Services, configuring DNS, and verifying replication. Includes intake form for server and domain details.",
        "tree_type": "procedural",
        "category": "Projects - Infrastructure",
        "tags": ["active-directory", "dns", "domain-controller", "server-build"],
        "intake_form": [
            {
                "variable_name": "server_name",
                "label": "Server Name",
                "field_type": "text",
                "required": True,
                "placeholder": "e.g. DC01, YOURCOMPANY-DC02",
                "help_text": "NetBIOS name of the new domain controller (max 15 characters)",
                "group_name": "Server Details",
                "display_order": 1,
            },
            {
                "variable_name": "server_ip",
                "label": "Server IP Address",
                "field_type": "ip_address",
                "required": True,
                "placeholder": "e.g. 10.0.1.10",
                "help_text": "Static IP address for this server",
                "group_name": "Server Details",
                "display_order": 2,
            },
            {
                "variable_name": "subnet_mask",
                "label": "Subnet Mask",
                "field_type": "text",
                "required": True,
                "placeholder": "e.g. 255.255.255.0",
                "default_value": "255.255.255.0",
                "group_name": "Server Details",
                "display_order": 3,
            },
            {
                "variable_name": "default_gateway",
                "label": "Default Gateway",
                "field_type": "ip_address",
                "required": True,
                "placeholder": "e.g. 10.0.1.1",
                "group_name": "Server Details",
                "display_order": 4,
            },
            {
                "variable_name": "domain_name",
                "label": "Domain Name (FQDN)",
                "field_type": "text",
                "required": True,
                "placeholder": "e.g. corp.contoso.com",
                "help_text": "Fully qualified domain name",
                "group_name": "Domain Configuration",
                "display_order": 5,
            },
            {
                "variable_name": "netbios_name",
                "label": "NetBIOS Domain Name",
                "field_type": "text",
                "required": True,
                "placeholder": "e.g. CONTOSO",
                "help_text": "Short domain name (auto-derived from FQDN if unsure)",
                "group_name": "Domain Configuration",
                "display_order": 6,
            },
            {
                "variable_name": "is_first_dc",
                "label": "Is this the first DC in the domain?",
                "field_type": "select",
                "required": True,
                "options": ["Yes — new forest/domain", "No — additional DC in existing domain"],
                "help_text": "Determines whether to create a new forest or join an existing one",
                "group_name": "Domain Configuration",
                "display_order": 7,
            },
            {
                "variable_name": "existing_dc_ip",
                "label": "Existing DC IP (if adding to existing domain)",
                "field_type": "ip_address",
                "required": False,
                "placeholder": "e.g. 10.0.1.5",
                "help_text": "IP of an existing domain controller for replication (leave blank for first DC)",
                "group_name": "Domain Configuration",
                "display_order": 8,
            },
            {
                "variable_name": "dsrm_password",
                "label": "DSRM Password",
                "field_type": "password",
                "required": True,
                "help_text": "Directory Services Restore Mode password — store securely in password vault",
                "group_name": "Security",
                "display_order": 9,
            },
            {
                "variable_name": "server_roles",
                "label": "Additional Server Roles",
                "field_type": "multi_select",
                "required": False,
                "options": ["DHCP Server", "Certificate Services (CA)", "DFS Namespaces", "Windows Server Backup", "WSUS"],
                "help_text": "Optional roles to install alongside AD DS",
                "group_name": "Additional Configuration",
                "display_order": 10,
            },
            {
                "variable_name": "client_name",
                "label": "Client / Company Name",
                "field_type": "text",
                "required": True,
                "placeholder": "e.g. Contoso Ltd",
                "group_name": "Project Info",
                "display_order": 11,
            },
            {
                "variable_name": "ticket_number",
                "label": "Ticket / Project Number",
                "field_type": "text",
                "required": False,
                "placeholder": "e.g. PRJ-2024-0042",
                "group_name": "Project Info",
                "display_order": 12,
            },
        ],
        "tree_structure": {
            "steps": [
                {
                    "id": "step_1",
                    "type": "procedure_step",
                    "title": "Verify Prerequisites",
                    "content_type": "verification",
                    "description": "Before beginning, verify the following prerequisites are met for **[VAR:client_name]** project **[VAR:ticket_number]**:\n\n- Windows Server 2022 (or 2019) is installed and activated on **[VAR:server_name]**\n- Server has network connectivity\n- You have local administrator credentials\n- The IP address **[VAR:server_ip]** is not in use (ping test)\n- DNS is planned — if this is a new domain, the DC will be its own DNS server",
                    "verification": {
                        "type": "checkbox",
                        "prompt": "All prerequisites verified?"
                    },
                },
                {
                    "id": "step_2",
                    "type": "procedure_step",
                    "title": "Configure Static IP Address",
                    "content_type": "action",
                    "description": "Set the static IP address on the primary network adapter of **[VAR:server_name]**.",
                    "commands": [
                        {
                            "language": "powershell",
                            "code": "# Get the primary network adapter\n$adapter = Get-NetAdapter | Where-Object { $_.Status -eq 'Up' } | Select-Object -First 1\n\n# Remove existing IP configuration\nRemove-NetIPAddress -InterfaceIndex $adapter.ifIndex -Confirm:$false -ErrorAction SilentlyContinue\nRemove-NetRoute -InterfaceIndex $adapter.ifIndex -Confirm:$false -ErrorAction SilentlyContinue\n\n# Set static IP\nNew-NetIPAddress -InterfaceIndex $adapter.ifIndex `\n  -IPAddress \"[VAR:server_ip]\" `\n  -PrefixLength 24 `\n  -DefaultGateway \"[VAR:default_gateway]\"\n\n# Set DNS — point to self (will be DNS server after AD install)\nSet-DnsClientServerAddress -InterfaceIndex $adapter.ifIndex `\n  -ServerAddresses \"[VAR:server_ip]\",\"[VAR:default_gateway]\"",
                            "label": "Set Static IP via PowerShell"
                        }
                    ],
                    "expected_outcome": "Server should have IP **[VAR:server_ip]** with gateway **[VAR:default_gateway]**. Verify with `ipconfig /all`.",
                },
                {
                    "id": "step_3",
                    "type": "procedure_step",
                    "title": "Rename Server",
                    "content_type": "action",
                    "description": "Rename the server to **[VAR:server_name]** and reboot.",
                    "commands": [
                        {
                            "language": "powershell",
                            "code": "# Rename the computer\nRename-Computer -NewName \"[VAR:server_name]\" -Force -Restart",
                            "label": "Rename and restart"
                        }
                    ],
                    "expected_outcome": "Server reboots with hostname **[VAR:server_name]**. Wait for reboot to complete before proceeding.",
                    "verification": {
                        "type": "checkbox",
                        "prompt": "Server has rebooted and hostname is correct?"
                    },
                },
                {
                    "id": "step_4",
                    "type": "procedure_step",
                    "title": "Install AD DS Role",
                    "content_type": "action",
                    "description": "Install the Active Directory Domain Services role and management tools on **[VAR:server_name]**.",
                    "commands": [
                        {
                            "language": "powershell",
                            "code": "# Install AD DS role with management tools\nInstall-WindowsFeature AD-Domain-Services -IncludeManagementTools -Verbose",
                            "label": "Install AD DS"
                        }
                    ],
                    "expected_outcome": "Feature installation completes successfully. No reboot required at this stage.",
                },
                {
                    "id": "step_5",
                    "type": "procedure_step",
                    "title": "Promote to Domain Controller",
                    "content_type": "action",
                    "description": "Promote **[VAR:server_name]** to a domain controller for **[VAR:domain_name]**.\n\n**Important:** The DSRM password should be stored securely in your password vault immediately after this step.",
                    "commands": [
                        {
                            "language": "powershell",
                            "code": "# For a NEW forest/domain:\nInstall-ADDSForest `\n  -DomainName \"[VAR:domain_name]\" `\n  -DomainNetBIOSName \"[VAR:netbios_name]\" `\n  -ForestMode \"WinThreshold\" `\n  -DomainMode \"WinThreshold\" `\n  -InstallDns:$true `\n  -DatabasePath \"C:\\Windows\\NTDS\" `\n  -LogPath \"C:\\Windows\\NTDS\" `\n  -SysvolPath \"C:\\Windows\\SYSVOL\" `\n  -SafeModeAdministratorPassword (Read-Host -AsSecureString \"DSRM Password\") `\n  -Force:$true",
                            "label": "Promote — New Forest"
                        },
                        {
                            "language": "powershell",
                            "code": "# For ADDITIONAL DC in existing domain:\nInstall-ADDSDomainController `\n  -DomainName \"[VAR:domain_name]\" `\n  -InstallDns:$true `\n  -DatabasePath \"C:\\Windows\\NTDS\" `\n  -LogPath \"C:\\Windows\\NTDS\" `\n  -SysvolPath \"C:\\Windows\\SYSVOL\" `\n  -SafeModeAdministratorPassword (Read-Host -AsSecureString \"DSRM Password\") `\n  -Force:$true",
                            "label": "Promote — Additional DC"
                        }
                    ],
                    "expected_outcome": "Server reboots automatically after promotion. This may take 5-10 minutes. After reboot, log in with **[VAR:netbios_name]\\Administrator**.",
                },
                {
                    "id": "step_6",
                    "type": "procedure_step",
                    "title": "Verify AD DS Installation",
                    "content_type": "verification",
                    "description": "After reboot, verify Active Directory and DNS are functioning correctly on **[VAR:server_name]**.",
                    "commands": [
                        {
                            "language": "powershell",
                            "code": "# Verify AD DS\nGet-ADDomainController -Filter *\nGet-ADDomain\nGet-ADForest\n\n# Verify DNS\nGet-DnsServerZone\nResolve-DnsName [VAR:domain_name]\n\n# Check SYSVOL and NETLOGON shares\nGet-SmbShare | Where-Object { $_.Name -in 'SYSVOL','NETLOGON' }\n\n# Check DCDIAG\ndcdiag /s:[VAR:server_name]",
                            "label": "AD & DNS verification"
                        }
                    ],
                    "expected_outcome": "All DCDIAG tests should pass. SYSVOL and NETLOGON shares should be present. DNS zone for **[VAR:domain_name]** should exist.",
                    "verification": {
                        "type": "checkbox",
                        "prompt": "DCDIAG passed, SYSVOL/NETLOGON shares present, DNS zone exists?"
                    },
                },
                {
                    "id": "step_7",
                    "type": "procedure_step",
                    "title": "Configure DNS Forwarders",
                    "content_type": "action",
                    "description": "Configure DNS forwarders so **[VAR:server_name]** can resolve external domains.",
                    "commands": [
                        {
                            "language": "powershell",
                            "code": "# Set DNS forwarders (Cloudflare + Google as fallback)\nSet-DnsServerForwarder -IPAddress \"1.1.1.1\",\"8.8.8.8\" -PassThru\n\n# Verify\nGet-DnsServerForwarder\n\n# Test external resolution\nResolve-DnsName google.com",
                            "label": "Configure forwarders"
                        }
                    ],
                    "expected_outcome": "External DNS resolution works. `Resolve-DnsName google.com` should return an IP address.",
                },
                {
                    "id": "step_8",
                    "type": "procedure_step",
                    "title": "Create OU Structure",
                    "content_type": "action",
                    "description": "Create the organizational unit structure for **[VAR:client_name]**.",
                    "commands": [
                        {
                            "language": "powershell",
                            "code": "$domain = (Get-ADDomain).DistinguishedName\n\n# Create top-level OUs\n$ous = @(\n  \"OU=Company Users,$domain\",\n  \"OU=Company Computers,$domain\",\n  \"OU=Company Groups,$domain\",\n  \"OU=Company Servers,$domain\",\n  \"OU=Service Accounts,$domain\",\n  \"OU=Disabled Accounts,$domain\"\n)\n\nforeach ($ou in $ous) {\n  $name = ($ou -split ',')[0] -replace 'OU=',''\n  try {\n    New-ADOrganizationalUnit -Name $name -Path $domain -ProtectedFromAccidentalDeletion $true\n    Write-Host \"Created: $name\" -ForegroundColor Green\n  } catch {\n    Write-Host \"Already exists: $name\" -ForegroundColor Yellow\n  }\n}",
                            "label": "Create OUs"
                        }
                    ],
                    "expected_outcome": "OU structure visible in Active Directory Users and Computers.",
                },
                {
                    "id": "step_9",
                    "type": "procedure_step",
                    "title": "Configure Group Policy Defaults",
                    "content_type": "action",
                    "description": "Configure essential Group Policy settings for the new domain.",
                    "commands": [
                        {
                            "language": "powershell",
                            "code": "# Set password policy\nSet-ADDefaultDomainPasswordPolicy -Identity \"[VAR:domain_name]\" `\n  -LockoutThreshold 5 `\n  -LockoutDuration \"00:30:00\" `\n  -LockoutObservationWindow \"00:30:00\" `\n  -MaxPasswordAge \"90.00:00:00\" `\n  -MinPasswordAge \"1.00:00:00\" `\n  -MinPasswordLength 12 `\n  -PasswordHistoryCount 10 `\n  -ComplexityEnabled $true\n\n# Verify\nGet-ADDefaultDomainPasswordPolicy",
                            "label": "Set password policy"
                        }
                    ],
                    "expected_outcome": "Password policy configured: 12 char minimum, complexity enabled, 5 lockout threshold, 90-day max age.",
                },
                {
                    "id": "step_10",
                    "type": "procedure_step",
                    "title": "Document and Update Password Vault",
                    "content_type": "informational",
                    "description": "Document the following in the client's IT documentation and password vault:\n\n| Item | Value |\n|------|-------|\n| Server Name | **[VAR:server_name]** |\n| IP Address | **[VAR:server_ip]** |\n| Domain | **[VAR:domain_name]** |\n| NetBIOS | **[VAR:netbios_name]** |\n| DSRM Password | *(stored in vault)* |\n| DNS Forwarders | 1.1.1.1, 8.8.8.8 |\n\nUpdate the client's network diagram and runbook with the new DC information.",
                    "verification": {
                        "type": "checkbox",
                        "prompt": "Documentation and password vault updated?"
                    },
                },
                {
                    "id": "step_end",
                    "type": "procedure_end",
                    "title": "Domain Controller Setup Complete",
                    "description": "**[VAR:server_name]** has been configured as a domain controller for **[VAR:domain_name]** at **[VAR:client_name]**.\n\n**Next steps to consider:**\n- Configure DHCP if selected as an additional role\n- Set up Azure AD Connect for hybrid identity\n- Configure backup solution for System State\n- Schedule a replication health check in 24 hours",
                },
            ]
        },
    }


def get_m365_user_onboarding_flow() -> dict[str, Any]:
    """Microsoft 365 User Onboarding — new hire setup procedure."""
    return {
        "name": "Microsoft 365 User Onboarding",
        "description": "Step-by-step procedure for creating a new Microsoft 365 user account, assigning licenses, configuring email, adding to security groups, and setting up MFA. Includes intake form for new user details.",
        "tree_type": "procedural",
        "category": "Projects - Microsoft 365",
        "tags": ["m365", "onboarding", "user-setup", "exchange-online"],
        "intake_form": [
            {
                "variable_name": "first_name",
                "label": "First Name",
                "field_type": "text",
                "required": True,
                "placeholder": "e.g. Jane",
                "group_name": "User Information",
                "display_order": 1,
            },
            {
                "variable_name": "last_name",
                "label": "Last Name",
                "field_type": "text",
                "required": True,
                "placeholder": "e.g. Smith",
                "group_name": "User Information",
                "display_order": 2,
            },
            {
                "variable_name": "display_name",
                "label": "Display Name",
                "field_type": "text",
                "required": True,
                "placeholder": "e.g. Jane Smith",
                "group_name": "User Information",
                "display_order": 3,
            },
            {
                "variable_name": "job_title",
                "label": "Job Title",
                "field_type": "text",
                "required": True,
                "placeholder": "e.g. Marketing Manager",
                "group_name": "User Information",
                "display_order": 4,
            },
            {
                "variable_name": "department",
                "label": "Department",
                "field_type": "text",
                "required": True,
                "placeholder": "e.g. Marketing",
                "group_name": "User Information",
                "display_order": 5,
            },
            {
                "variable_name": "manager_email",
                "label": "Manager's Email",
                "field_type": "email",
                "required": False,
                "placeholder": "e.g. john.doe@company.com",
                "group_name": "User Information",
                "display_order": 6,
            },
            {
                "variable_name": "email_address",
                "label": "Email Address",
                "field_type": "email",
                "required": True,
                "placeholder": "e.g. jane.smith@company.com",
                "group_name": "Account Setup",
                "display_order": 7,
            },
            {
                "variable_name": "license_type",
                "label": "License Type",
                "field_type": "select",
                "required": True,
                "options": [
                    "Microsoft 365 Business Basic",
                    "Microsoft 365 Business Standard",
                    "Microsoft 365 Business Premium",
                    "Microsoft 365 E3",
                    "Microsoft 365 E5",
                    "Exchange Online Plan 1",
                    "Exchange Online Plan 2"
                ],
                "help_text": "Select the license to assign to this user",
                "group_name": "Account Setup",
                "display_order": 8,
            },
            {
                "variable_name": "security_groups",
                "label": "Security Groups",
                "field_type": "textarea",
                "required": False,
                "placeholder": "One group per line, e.g.:\nMarketing Team\nAll Staff\nVPN Users",
                "help_text": "List the security/distribution groups this user should be added to",
                "group_name": "Access & Groups",
                "display_order": 9,
            },
            {
                "variable_name": "shared_mailboxes",
                "label": "Shared Mailboxes (Full Access)",
                "field_type": "textarea",
                "required": False,
                "placeholder": "One mailbox per line, e.g.:\ninfo@company.com\nsales@company.com",
                "help_text": "Shared mailboxes this user needs access to",
                "group_name": "Access & Groups",
                "display_order": 10,
            },
            {
                "variable_name": "start_date",
                "label": "Start Date",
                "field_type": "text",
                "required": True,
                "placeholder": "e.g. 2025-03-15",
                "help_text": "When the user needs access (YYYY-MM-DD)",
                "group_name": "Project Info",
                "display_order": 11,
            },
            {
                "variable_name": "client_name",
                "label": "Client / Company Name",
                "field_type": "text",
                "required": True,
                "placeholder": "e.g. Contoso Ltd",
                "group_name": "Project Info",
                "display_order": 12,
            },
            {
                "variable_name": "ticket_number",
                "label": "Ticket Number",
                "field_type": "text",
                "required": False,
                "placeholder": "e.g. TKT-2024-1234",
                "group_name": "Project Info",
                "display_order": 13,
            },
        ],
        "tree_structure": {
            "steps": [
                {
                    "id": "step_1",
                    "type": "procedure_step",
                    "title": "Verify Prerequisites & Licensing",
                    "content_type": "verification",
                    "description": "Before creating the account for **[VAR:display_name]** at **[VAR:client_name]**:\n\n- Confirm **[VAR:license_type]** license is available in the tenant\n- Verify the domain for **[VAR:email_address]** is configured in M365\n- Confirm the email address is not already in use\n- Verify start date: **[VAR:start_date]**\n- Ticket reference: **[VAR:ticket_number]**",
                    "commands": [
                        {
                            "language": "powershell",
                            "code": "# Connect to Microsoft Graph (if not already connected)\nConnect-MgGraph -Scopes \"User.ReadWrite.All\",\"Group.ReadWrite.All\",\"Directory.ReadWrite.All\"\n\n# Check available licenses\nGet-MgSubscribedSku | Select-Object SkuPartNumber, ConsumedUnits, @{N='Available';E={$_.PrepaidUnits.Enabled - $_.ConsumedUnits}} | Format-Table\n\n# Check if email is already taken\nGet-MgUser -Filter \"mail eq '[VAR:email_address]'\" -ErrorAction SilentlyContinue",
                            "label": "Check licenses & email availability"
                        }
                    ],
                    "verification": {
                        "type": "checkbox",
                        "prompt": "License available and email address is free?"
                    },
                },
                {
                    "id": "step_2",
                    "type": "procedure_step",
                    "title": "Create User Account",
                    "content_type": "action",
                    "description": "Create the Microsoft 365 user account for **[VAR:display_name]**.",
                    "commands": [
                        {
                            "language": "powershell",
                            "code": "# Generate a temporary password\n$tempPassword = -join ((65..90) + (97..122) + (48..57) + (33,35,36,37) | Get-Random -Count 16 | ForEach-Object { [char]$_ })\n\n# Create the user\n$params = @{\n  AccountEnabled = $true\n  DisplayName = \"[VAR:display_name]\"\n  GivenName = \"[VAR:first_name]\"\n  Surname = \"[VAR:last_name]\"\n  MailNickname = \"[VAR:first_name].[VAR:last_name]\".ToLower()\n  UserPrincipalName = \"[VAR:email_address]\"\n  JobTitle = \"[VAR:job_title]\"\n  Department = \"[VAR:department]\"\n  PasswordProfile = @{\n    ForceChangePasswordNextSignIn = $true\n    Password = $tempPassword\n  }\n  UsageLocation = \"US\"\n}\n\n$newUser = New-MgUser @params\nWrite-Host \"User created: $($newUser.UserPrincipalName)\" -ForegroundColor Green\nWrite-Host \"Temp password: $tempPassword\" -ForegroundColor Yellow",
                            "label": "Create M365 user"
                        }
                    ],
                    "expected_outcome": "User account created. **Save the temporary password securely** — you'll need it for the welcome email.",
                },
                {
                    "id": "step_3",
                    "type": "procedure_step",
                    "title": "Assign License",
                    "content_type": "action",
                    "description": "Assign the **[VAR:license_type]** license to **[VAR:display_name]**.",
                    "commands": [
                        {
                            "language": "powershell",
                            "code": "# Get the user\n$user = Get-MgUser -Filter \"userPrincipalName eq '[VAR:email_address]'\"\n\n# Get the SKU for the selected license\n# Common SKU mappings:\n# Business Basic = O365_BUSINESS_ESSENTIALS or SPB\n# Business Standard = O365_BUSINESS_PREMIUM\n# Business Premium = SPB\n# E3 = SPE_E3\n# E5 = SPE_E5\n$skus = Get-MgSubscribedSku | Where-Object { $_.PrepaidUnits.Enabled - $_.ConsumedUnits -gt 0 }\n$skus | Select-Object SkuPartNumber, SkuId | Format-Table\n\n# Assign license (replace SKU_ID with actual value from above)\n# Set-MgUserLicense -UserId $user.Id -AddLicenses @(@{SkuId = \"SKU_ID_HERE\"}) -RemoveLicenses @()",
                            "label": "Assign license via PowerShell"
                        }
                    ],
                    "expected_outcome": "License assigned. Mailbox provisioning may take 5-15 minutes.",
                    "verification": {
                        "type": "checkbox",
                        "prompt": "License successfully assigned?"
                    },
                },
                {
                    "id": "step_4",
                    "type": "procedure_step",
                    "title": "Wait for Mailbox Provisioning",
                    "content_type": "informational",
                    "description": "Exchange Online mailbox provisioning typically takes 5-15 minutes after license assignment.\n\nWhile waiting, you can proceed to configure groups in the next step.\n\nTo check mailbox status:",
                    "commands": [
                        {
                            "language": "powershell",
                            "code": "# Check if mailbox exists yet\nConnect-ExchangeOnline\nGet-EXOMailbox -Identity \"[VAR:email_address]\" -ErrorAction SilentlyContinue | Select-Object DisplayName, PrimarySmtpAddress, RecipientTypeDetails",
                            "label": "Check mailbox provisioning"
                        }
                    ],
                    "expected_outcome": "Mailbox appears with `RecipientTypeDetails` = `UserMailbox`.",
                },
                {
                    "id": "step_5",
                    "type": "procedure_step",
                    "title": "Add to Security Groups",
                    "content_type": "action",
                    "description": "Add **[VAR:display_name]** to the requested security and distribution groups.\n\n**Requested groups:**\n[VAR:security_groups]",
                    "commands": [
                        {
                            "language": "powershell",
                            "code": "# Get the user\n$user = Get-MgUser -Filter \"userPrincipalName eq '[VAR:email_address]'\"\n\n# List available groups (search by name)\n# Get-MgGroup -Filter \"displayName eq 'GROUP_NAME'\" | Select-Object DisplayName, Id\n\n# Add user to a group\n# New-MgGroupMember -GroupId \"GROUP_ID\" -DirectoryObjectId $user.Id",
                            "label": "Add to groups"
                        }
                    ],
                    "expected_outcome": "User appears as a member of all requested groups.",
                    "verification": {
                        "type": "checkbox",
                        "prompt": "User added to all requested groups?"
                    },
                },
                {
                    "id": "step_6",
                    "type": "procedure_step",
                    "title": "Configure Shared Mailbox Access",
                    "content_type": "action",
                    "description": "Grant **[VAR:display_name]** Full Access and Send As permissions on requested shared mailboxes.\n\n**Requested shared mailboxes:**\n[VAR:shared_mailboxes]",
                    "commands": [
                        {
                            "language": "powershell",
                            "code": "# Grant Full Access to shared mailbox\n# Add-MailboxPermission -Identity \"SHARED_MAILBOX@company.com\" -User \"[VAR:email_address]\" -AccessRights FullAccess -AutoMapping $true\n\n# Grant Send As permission\n# Add-RecipientPermission -Identity \"SHARED_MAILBOX@company.com\" -Trustee \"[VAR:email_address]\" -AccessRights SendAs -Confirm:$false",
                            "label": "Configure shared mailbox access"
                        }
                    ],
                    "expected_outcome": "Shared mailboxes auto-map in Outlook within 30-60 minutes.",
                },
                {
                    "id": "step_7",
                    "type": "procedure_step",
                    "title": "Set Manager (Optional)",
                    "content_type": "action",
                    "description": "Set the manager for **[VAR:display_name]** if provided.\n\nManager: **[VAR:manager_email]**",
                    "commands": [
                        {
                            "language": "powershell",
                            "code": "# Set manager\n$user = Get-MgUser -Filter \"userPrincipalName eq '[VAR:email_address]'\"\n$manager = Get-MgUser -Filter \"userPrincipalName eq '[VAR:manager_email]'\"\n\nif ($manager) {\n  Set-MgUserManagerByRef -UserId $user.Id -BodyParameter @{ \"@odata.id\" = \"https://graph.microsoft.com/v1.0/users/$($manager.Id)\" }\n  Write-Host \"Manager set successfully\" -ForegroundColor Green\n}",
                            "label": "Set manager"
                        }
                    ],
                },
                {
                    "id": "step_8",
                    "type": "procedure_step",
                    "title": "Configure MFA",
                    "content_type": "action",
                    "description": "Ensure MFA is configured for **[VAR:display_name]**.\n\nIf the tenant uses **Security Defaults** or **Conditional Access**, MFA will be prompted on first login.\n\nIf using per-user MFA, enable it manually:\n1. Go to Microsoft Entra admin center > Users > Per-user MFA\n2. Find **[VAR:email_address]**\n3. Set MFA status to **Enabled**\n\nThe user will be prompted to set up the Microsoft Authenticator app on first login.",
                    "verification": {
                        "type": "checkbox",
                        "prompt": "MFA configured or will be prompted on first sign-in?"
                    },
                },
                {
                    "id": "step_9",
                    "type": "procedure_step",
                    "title": "Send Welcome Email & Credentials",
                    "content_type": "informational",
                    "description": "Send the new user their login credentials securely.\n\n**Account details to communicate:**\n- Email: **[VAR:email_address]**\n- Temporary password: *(from step 2)*\n- Sign-in URL: https://portal.office.com\n- They will be prompted to change their password on first login\n- They will be prompted to set up MFA (Microsoft Authenticator app)\n\n**Best practice:** Send the username and password via separate channels (e.g., email the username to the manager, text the password to the user's personal phone).",
                    "verification": {
                        "type": "checkbox",
                        "prompt": "Welcome email/credentials sent securely?"
                    },
                },
                {
                    "id": "step_10",
                    "type": "procedure_step",
                    "title": "Document & Update Ticket",
                    "content_type": "informational",
                    "description": "Update the documentation and close the ticket.\n\n**Ticket: [VAR:ticket_number]**\n\n| Item | Value |\n|------|-------|\n| User | **[VAR:display_name]** |\n| Email | **[VAR:email_address]** |\n| License | **[VAR:license_type]** |\n| Department | **[VAR:department]** |\n| Title | **[VAR:job_title]** |\n| Start Date | **[VAR:start_date]** |\n| Groups | [VAR:security_groups] |\n| Shared Mailboxes | [VAR:shared_mailboxes] |",
                    "verification": {
                        "type": "checkbox",
                        "prompt": "Ticket updated and documented?"
                    },
                },
                {
                    "id": "step_end",
                    "type": "procedure_end",
                    "title": "User Onboarding Complete",
                    "description": "**[VAR:display_name]** ([VAR:email_address]) has been onboarded to Microsoft 365 for **[VAR:client_name]**.\n\n**Summary:**\n- Account created with **[VAR:license_type]**\n- Added to security groups\n- Shared mailbox access configured\n- MFA will be prompted on first sign-in\n\n**Follow-up in 24 hours:**\n- Verify user has logged in successfully\n- Confirm MFA is registered\n- Check shared mailboxes auto-mapped in Outlook",
                },
            ]
        },
    }


def get_vpn_gateway_flow() -> dict[str, Any]:
    """Site-to-Site VPN Configuration — firewall/network setup procedure."""
    return {
        "name": "Site-to-Site VPN Configuration",
        "description": "Step-by-step procedure for configuring a site-to-site IPSec VPN tunnel between two locations. Covers network planning, firewall configuration, tunnel setup, and verification.",
        "tree_type": "procedural",
        "category": "Projects - Networking",
        "tags": ["vpn", "ipsec", "networking", "firewall"],
        "intake_form": [
            {
                "variable_name": "site_a_name",
                "label": "Site A Name (Primary)",
                "field_type": "text",
                "required": True,
                "placeholder": "e.g. HQ Office",
                "group_name": "Site A — Primary",
                "display_order": 1,
            },
            {
                "variable_name": "site_a_public_ip",
                "label": "Site A Public IP",
                "field_type": "ip_address",
                "required": True,
                "placeholder": "e.g. 203.0.113.10",
                "group_name": "Site A — Primary",
                "display_order": 2,
            },
            {
                "variable_name": "site_a_lan_subnet",
                "label": "Site A LAN Subnet",
                "field_type": "text",
                "required": True,
                "placeholder": "e.g. 10.0.1.0/24",
                "help_text": "Local network behind Site A's firewall",
                "group_name": "Site A — Primary",
                "display_order": 3,
            },
            {
                "variable_name": "site_a_firewall",
                "label": "Site A Firewall Model",
                "field_type": "select",
                "required": True,
                "options": ["SonicWall", "Fortinet FortiGate", "Meraki MX", "pfSense/OPNsense", "Ubiquiti USG/UDM", "Other"],
                "group_name": "Site A — Primary",
                "display_order": 4,
            },
            {
                "variable_name": "site_b_name",
                "label": "Site B Name (Remote)",
                "field_type": "text",
                "required": True,
                "placeholder": "e.g. Branch Office",
                "group_name": "Site B — Remote",
                "display_order": 5,
            },
            {
                "variable_name": "site_b_public_ip",
                "label": "Site B Public IP",
                "field_type": "ip_address",
                "required": True,
                "placeholder": "e.g. 198.51.100.20",
                "group_name": "Site B — Remote",
                "display_order": 6,
            },
            {
                "variable_name": "site_b_lan_subnet",
                "label": "Site B LAN Subnet",
                "field_type": "text",
                "required": True,
                "placeholder": "e.g. 10.0.2.0/24",
                "help_text": "Local network behind Site B's firewall",
                "group_name": "Site B — Remote",
                "display_order": 7,
            },
            {
                "variable_name": "site_b_firewall",
                "label": "Site B Firewall Model",
                "field_type": "select",
                "required": True,
                "options": ["SonicWall", "Fortinet FortiGate", "Meraki MX", "pfSense/OPNsense", "Ubiquiti USG/UDM", "Other"],
                "group_name": "Site B — Remote",
                "display_order": 8,
            },
            {
                "variable_name": "psk",
                "label": "Pre-Shared Key",
                "field_type": "password",
                "required": True,
                "help_text": "IKE pre-shared key — minimum 20 characters recommended. Store in password vault.",
                "group_name": "Tunnel Configuration",
                "display_order": 9,
            },
            {
                "variable_name": "encryption",
                "label": "Encryption Algorithm",
                "field_type": "select",
                "required": True,
                "options": ["AES-256-GCM (recommended)", "AES-256-CBC", "AES-128-GCM", "AES-128-CBC"],
                "default_value": "AES-256-GCM (recommended)",
                "group_name": "Tunnel Configuration",
                "display_order": 10,
            },
            {
                "variable_name": "ike_version",
                "label": "IKE Version",
                "field_type": "select",
                "required": True,
                "options": ["IKEv2 (recommended)", "IKEv1"],
                "default_value": "IKEv2 (recommended)",
                "group_name": "Tunnel Configuration",
                "display_order": 11,
            },
            {
                "variable_name": "client_name",
                "label": "Client Name",
                "field_type": "text",
                "required": True,
                "placeholder": "e.g. Contoso Ltd",
                "group_name": "Project Info",
                "display_order": 12,
            },
            {
                "variable_name": "ticket_number",
                "label": "Ticket / Project Number",
                "field_type": "text",
                "required": False,
                "placeholder": "e.g. PRJ-2024-0099",
                "group_name": "Project Info",
                "display_order": 13,
            },
        ],
        "tree_structure": {
            "steps": [
                {
                    "id": "step_1",
                    "type": "procedure_step",
                    "title": "Verify Network Prerequisites",
                    "content_type": "verification",
                    "description": "Verify the following before configuring the VPN for **[VAR:client_name]** (ticket: **[VAR:ticket_number]**):\n\n**Site A — [VAR:site_a_name]:**\n- Public IP: **[VAR:site_a_public_ip]** (static, not behind NAT if possible)\n- LAN Subnet: **[VAR:site_a_lan_subnet]**\n- Firewall: **[VAR:site_a_firewall]**\n\n**Site B — [VAR:site_b_name]:**\n- Public IP: **[VAR:site_b_public_ip]** (static, not behind NAT if possible)\n- LAN Subnet: **[VAR:site_b_lan_subnet]**\n- Firewall: **[VAR:site_b_firewall]**\n\n**Critical checks:**\n- LAN subnets must NOT overlap\n- Both firewalls support IPSec VPN\n- ISPs are not blocking UDP 500 and 4500\n- Both sites have admin access to their firewall",
                    "verification": {
                        "type": "checkbox",
                        "prompt": "All prerequisites verified — no subnet overlap, admin access confirmed?"
                    },
                },
                {
                    "id": "step_2",
                    "type": "procedure_step",
                    "title": "Document the Tunnel Configuration",
                    "content_type": "informational",
                    "description": "Record the agreed tunnel parameters before configuring either side:\n\n| Parameter | Value |\n|-----------|-------|\n| IKE Version | **[VAR:ike_version]** |\n| Encryption | **[VAR:encryption]** |\n| Hash | SHA-256 |\n| DH Group | 14 (2048-bit) |\n| SA Lifetime | Phase 1: 28800s / Phase 2: 3600s |\n| PFS | Enabled (DH Group 14) |\n| Site A Peer | **[VAR:site_a_public_ip]** |\n| Site B Peer | **[VAR:site_b_public_ip]** |\n| Site A Network | **[VAR:site_a_lan_subnet]** |\n| Site B Network | **[VAR:site_b_lan_subnet]** |\n\n**Both sides MUST match exactly.** Save this table — you'll reference it during configuration.",
                },
                {
                    "id": "step_3",
                    "type": "procedure_step",
                    "title": "Configure Site A Firewall",
                    "content_type": "action",
                    "description": "Configure the VPN tunnel on **[VAR:site_a_name]** (**[VAR:site_a_firewall]**).\n\nLog into the firewall at **[VAR:site_a_name]** and create a new site-to-site VPN policy:\n\n1. **Phase 1 (IKE SA):**\n   - Remote gateway: **[VAR:site_b_public_ip]**\n   - Authentication: Pre-shared key\n   - IKE version: **[VAR:ike_version]**\n   - Encryption: **[VAR:encryption]**\n   - Hash: SHA-256\n   - DH Group: 14\n   - Lifetime: 28800 seconds\n\n2. **Phase 2 (IPSec SA):**\n   - Local network: **[VAR:site_a_lan_subnet]**\n   - Remote network: **[VAR:site_b_lan_subnet]**\n   - Encryption: **[VAR:encryption]**\n   - Hash: SHA-256\n   - PFS: Group 14\n   - Lifetime: 3600 seconds\n\n3. **Firewall rules:**\n   - Allow traffic from **[VAR:site_a_lan_subnet]** to **[VAR:site_b_lan_subnet]** via VPN zone\n   - Allow traffic from **[VAR:site_b_lan_subnet]** to **[VAR:site_a_lan_subnet]** via VPN zone",
                    "verification": {
                        "type": "checkbox",
                        "prompt": "Site A VPN policy and firewall rules configured?"
                    },
                },
                {
                    "id": "step_4",
                    "type": "procedure_step",
                    "title": "Configure Site B Firewall",
                    "content_type": "action",
                    "description": "Configure the matching VPN tunnel on **[VAR:site_b_name]** (**[VAR:site_b_firewall]**).\n\nRepeat the same configuration, but mirror the local/remote settings:\n\n1. **Phase 1 (IKE SA):**\n   - Remote gateway: **[VAR:site_a_public_ip]**\n   - All other settings IDENTICAL to Site A\n\n2. **Phase 2 (IPSec SA):**\n   - Local network: **[VAR:site_b_lan_subnet]**\n   - Remote network: **[VAR:site_a_lan_subnet]**\n   - All other settings IDENTICAL to Site A\n\n3. **Firewall rules:**\n   - Allow traffic from **[VAR:site_b_lan_subnet]** to **[VAR:site_a_lan_subnet]** via VPN zone\n   - Allow traffic from **[VAR:site_a_lan_subnet]** to **[VAR:site_b_lan_subnet]** via VPN zone\n\n**Double check:** Encryption, hash, DH group, PFS, and lifetimes must be identical on both sides.",
                    "verification": {
                        "type": "checkbox",
                        "prompt": "Site B VPN policy and firewall rules configured (matching Site A)?"
                    },
                },
                {
                    "id": "step_5",
                    "type": "procedure_step",
                    "title": "Initiate Tunnel & Verify Phase 1",
                    "content_type": "verification",
                    "description": "Bring up the VPN tunnel and verify Phase 1 (IKE) negotiation succeeds.\n\nOn most firewalls, you can force the tunnel to initiate by:\n- Sending a ping from one LAN to the other\n- Using the firewall's \"Connect\" or \"Bring Up\" button in the VPN status page\n\n**Check the VPN status page on both firewalls:**\n- Phase 1 should show **Established** or **Connected**\n- If Phase 1 fails, check: pre-shared key match, IKE version match, encryption/hash match, public IPs correct\n\n**Common Phase 1 failures:**\n- Mismatched PSK (most common)\n- ISP blocking UDP 500/4500\n- NAT-T issues (enable NAT traversal if behind NAT)\n- DH group mismatch",
                    "verification": {
                        "type": "checkbox",
                        "prompt": "Phase 1 (IKE) established on both firewalls?"
                    },
                },
                {
                    "id": "step_6",
                    "type": "procedure_step",
                    "title": "Verify Phase 2 & Traffic Flow",
                    "content_type": "verification",
                    "description": "Verify Phase 2 (IPSec) is established and traffic flows between sites.\n\n**From a device at [VAR:site_a_name] ([VAR:site_a_lan_subnet]):**\n- Ping a device at [VAR:site_b_name] ([VAR:site_b_lan_subnet])\n- Try accessing a file share or RDP session across the tunnel\n\n**From a device at [VAR:site_b_name] ([VAR:site_b_lan_subnet]):**\n- Ping a device at [VAR:site_a_name] ([VAR:site_a_lan_subnet])\n\n**Common Phase 2 failures:**\n- Mismatched encryption/hash/PFS settings\n- Incorrect local/remote network definitions (proxy IDs)\n- Firewall rules not allowing VPN zone traffic\n- Asymmetric routing (traffic going out default route instead of tunnel)",
                    "verification": {
                        "type": "checkbox",
                        "prompt": "Bidirectional traffic confirmed between sites?"
                    },
                },
                {
                    "id": "step_7",
                    "type": "procedure_step",
                    "title": "Test Critical Services",
                    "content_type": "verification",
                    "description": "Test the specific services that need to work across the VPN:\n\n- **File shares:** Access shared folders from remote site\n- **RDP:** Remote desktop to servers across the tunnel\n- **Active Directory:** If domain-joined, verify domain auth works from remote site\n- **DNS:** Resolve internal hostnames across the tunnel\n- **Printing:** If printers need cross-site access\n\nTest from both directions (A→B and B→A).",
                    "verification": {
                        "type": "checkbox",
                        "prompt": "All required services working bidirectionally?"
                    },
                },
                {
                    "id": "step_8",
                    "type": "procedure_step",
                    "title": "Configure Monitoring & Alerts",
                    "content_type": "action",
                    "description": "Set up monitoring to detect VPN tunnel drops:\n\n1. **On the firewalls:** Enable VPN tunnel status alerting (email/SNMP)\n2. **In your RMM/monitoring tool:** Add a ping monitor from each site to a host on the other site\n3. **Recommended thresholds:**\n   - Alert if tunnel is down for > 5 minutes\n   - Alert if latency exceeds 100ms\n   - Alert if packet loss exceeds 1%\n\n**DPD (Dead Peer Detection):**\nEnsure DPD is enabled on both firewalls with a 10-second interval. This allows automatic tunnel renegotiation after brief outages.",
                    "verification": {
                        "type": "checkbox",
                        "prompt": "Monitoring and DPD configured?"
                    },
                },
                {
                    "id": "step_9",
                    "type": "procedure_step",
                    "title": "Document & Update Password Vault",
                    "content_type": "informational",
                    "description": "Document the complete VPN configuration:\n\n| Parameter | Site A | Site B |\n|-----------|--------|--------|\n| Name | **[VAR:site_a_name]** | **[VAR:site_b_name]** |\n| Public IP | **[VAR:site_a_public_ip]** | **[VAR:site_b_public_ip]** |\n| LAN Subnet | **[VAR:site_a_lan_subnet]** | **[VAR:site_b_lan_subnet]** |\n| Firewall | **[VAR:site_a_firewall]** | **[VAR:site_b_firewall]** |\n| IKE Version | **[VAR:ike_version]** | **[VAR:ike_version]** |\n| Encryption | **[VAR:encryption]** | **[VAR:encryption]** |\n\n**Store in password vault:**\n- Pre-shared key\n- Firewall admin credentials for both sites\n\n**Update:**\n- Client network diagram with VPN tunnel\n- Firewall configuration backup on both sites",
                    "verification": {
                        "type": "checkbox",
                        "prompt": "Documentation complete and PSK stored in vault?"
                    },
                },
                {
                    "id": "step_end",
                    "type": "procedure_end",
                    "title": "VPN Tunnel Active",
                    "description": "Site-to-site VPN tunnel is established between **[VAR:site_a_name]** and **[VAR:site_b_name]** for **[VAR:client_name]**.\n\n**Summary:**\n- Tunnel: **[VAR:site_a_public_ip]** ↔ **[VAR:site_b_public_ip]**\n- Networks: **[VAR:site_a_lan_subnet]** ↔ **[VAR:site_b_lan_subnet]**\n- Encryption: **[VAR:encryption]** / **[VAR:ike_version]**\n\n**Follow-up:**\n- Verify tunnel stability over 24-48 hours\n- Confirm monitoring alerts fire correctly (test with a brief tunnel disconnect)\n- Schedule quarterly PSK rotation",
                },
            ]
        },
    }


# =============================================================================
# SEEDING FUNCTIONS
# =============================================================================

async def get_admin_token(client: httpx.AsyncClient) -> str:
    """Authenticate and get admin token."""
    response = await client.post(
        f"{API_BASE_URL}/auth/login",
        data={"username": ADMIN_EMAIL, "password": ADMIN_PASSWORD},
    )
    if response.status_code != 200:
        raise Exception(f"Login failed ({response.status_code}): {response.text}")
    return response.json()["access_token"]


async def create_procedural_flow(client: httpx.AsyncClient, token: str, flow_data: dict) -> dict | None:
    """Create a procedural flow via the API. Returns None if it already exists."""
    headers = {"Authorization": f"Bearer {token}"}

    # Mark as default/system flow (public and visible to all)
    flow_data["is_default"] = True
    flow_data["is_public"] = True
    flow_data["status"] = "draft"  # Skip validation for seed data

    # Check if flow with same name exists
    list_response = await client.get(f"{API_BASE_URL}/trees", headers=headers, params={"tree_type": "procedural"})
    if list_response.status_code == 200:
        existing = list_response.json()
        for tree in existing:
            if tree["name"] == flow_data["name"]:
                if not tree.get("is_public") or not tree.get("is_default"):
                    patch_response = await client.put(
                        f"{API_BASE_URL}/trees/{tree['id']}",
                        json={"is_public": True, "is_default": True},
                        headers=headers,
                    )
                    if patch_response.status_code == 200:
                        print(f"  [UPDATE] Flow '{flow_data['name']}' visibility updated (ID: {tree['id']})")
                        return None
                print(f"  [SKIP] Flow '{flow_data['name']}' already exists (ID: {tree['id']})")
                return None

    # Create the flow
    response = await client.post(
        f"{API_BASE_URL}/trees",
        json=flow_data,
        headers=headers,
    )

    if response.status_code not in (200, 201):
        raise Exception(f"Failed to create flow '{flow_data['name']}': {response.text}")

    tree = response.json()
    print(f"  [OK] Created flow '{flow_data['name']}' (ID: {tree['id']})")
    return tree


async def seed_procedural_flows():
    """Main seeding function."""
    print("\n" + "=" * 60)
    print("  PATHERLY - Procedural Flow Templates Seeder")
    print("=" * 60)

    async with httpx.AsyncClient(timeout=60.0) as client:
        # Health check
        try:
            health_check = await client.get(f"{API_BASE_URL.replace('/api/v1', '')}/health")
            if health_check.status_code != 200:
                print(f"\n[ERROR] API health check failed: {health_check.status_code}")
                return False
        except httpx.ConnectError:
            print("\n[ERROR] Cannot connect to API server")
            print(f"        Make sure the server is running at {API_BASE_URL}")
            return False

        # Authenticate
        print("\n[1/3] Authenticating...")
        try:
            token = await get_admin_token(client)
            print(f"       Logged in as {ADMIN_EMAIL}")
        except Exception as e:
            print(f"       [ERROR] Failed to authenticate: {e}")
            return False

        # Get flow definitions
        print("\n[2/3] Preparing procedural flows...")
        flows_to_create = [
            ("Projects - Infrastructure", get_domain_controller_flow()),
            ("Projects - Microsoft 365", get_m365_user_onboarding_flow()),
            ("Projects - Networking", get_vpn_gateway_flow()),
        ]
        print(f"       Found {len(flows_to_create)} flows to seed\n")

        # Create flows
        print("[3/3] Creating procedural flows...")
        created_count = 0
        skipped_count = 0

        current_category = None
        for category, flow_data in flows_to_create:
            if category != current_category:
                print(f"\n  {category}:")
                current_category = category

            try:
                result = await create_procedural_flow(client, token, flow_data)
                if result:
                    created_count += 1
                else:
                    skipped_count += 1
            except Exception as e:
                print(f"  [FAIL] Failed to create '{flow_data['name']}': {e}")

        # Summary
        print("\n" + "=" * 60)
        print("  SEEDING COMPLETE")
        print("=" * 60)
        print(f"  Flows created: {created_count}")
        print(f"  Flows skipped (already exist): {skipped_count}")
        print()

        return True


def main():
    global ADMIN_EMAIL, ADMIN_PASSWORD, API_BASE_URL

    parser = argparse.ArgumentParser(description="Seed procedural flow templates")
    parser.add_argument("--email", required=True, help="Admin email for authentication")
    parser.add_argument("--password", required=True, help="Admin password")
    parser.add_argument("--api-url", default=API_BASE_URL, help="API base URL (default: http://localhost:8000/api/v1)")
    args = parser.parse_args()

    ADMIN_EMAIL = args.email
    ADMIN_PASSWORD = args.password
    API_BASE_URL = args.api_url

    asyncio.run(seed_procedural_flows())


if __name__ == "__main__":
    main()
