"""The worked examples: fictional day-to-day Ticket-to-Machine tasks at contoso.example.

build_template.py writes these to examples.csv and to the workbook's Examples
sheet. The KSIs and the response window are filled from catalog_schema, so they
always match the Controls sheet and the finding contract's tempo. Every row
passes every check except EX-P1, EX-P2 and EX-P3, which fail on purpose for practice.
All names, systems and identities are invented.

Microsoft facts in EX-17 to EX-20 are from Microsoft Learn, and the ServiceNow
Otto facts in EX-21 and EX-22 from ServiceNow's own pages, all read November 2,
2026; the guide page footnotes each one.
"""

from __future__ import annotations

import catalog_schema as cs

_FIELDS = ["Task ID", "Task name", "Lane", "Trigger", "Source system", "Target system", "Drift class",
           "Severity", "C1 Origin (who asks)", "C2 Approver role (who approves)", "C3 Action (what runs)",
           "C3 Executing identity (as which identity)", "C4 Re-read check (how closure is proven)",
           "Re-read system", "C5 Evidence kept", "NIST SP 800-53 controls", "Owner", "Status", "Notes"]

_ROWS = [
    ("EX-01", "Joiner: create the account from the HR hire", "Machine, gated", "event",
     "Core HCM (HR)", "Entra ID", "none", "P3",
     "HR hire action for a new worker at contoso.example",
     "HR, through the hire action, under the joiner policy",
     "Inbound provisioning creates the user from the HR record (Graph bulk upload)",
     "svc-hr-inbound (provisioning service identity)",
     "Read the user by employeeId: exists, enabled from the start date, department and manager match HR",
     "entra_user", "HR event ID, provisioning log entry, re-read result and time",
     "AC-2", "Identity Operations", "Approved", ""),
    ("EX-02", "Mover: update attributes and access after a transfer", "Machine, gated", "event",
     "Core HCM (HR)", "Entra ID", "attribute_drift", "P3",
     "HR transfer action: department changes from Finance to Logistics",
     "New manager approves new-role access; old-role access goes to the old manager's review",
     "Identity governance platform updates attributes and groups through its Entra connector",
     "svc-iga-connector, elevated just in time",
     "Re-read department and group membership: new department set, old department groups absent",
     "isc_identity", "HR event ID, approval, connector run log, re-read of attributes and groups",
     "AC-2; AC-6", "Identity Operations", "Approved", ""),
    ("EX-03", "Leaver: disable the account on the last day", "Machine, gated", "event",
     "Core HCM (HR)", "Entra ID and AD DS", "stale_access", "P2",
     "HR separation action with the last day of work",
     "HR, through the separation action",
     "Lifecycle workflow disables the account and removes group memberships at the end of the last day",
     "wf-leaver (lifecycle workflow managed identity)",
     "Re-read: accountEnabled is false and the user has no group memberships",
     "entra_user", "HR event ID, workflow run ID, re-read result and time",
     "AC-2", "Identity Operations", "Approved", ""),
    ("EX-04", "Access request: a project role", "Machine, gated", "request",
     "ITSM catalog", "Entra ID", "none", "P3",
     "Catalog request from the user, with a business reason and an end date",
     "Role owner named in the authority model; never the requester",
     "Access package assignment through Graph",
     "svc-fulfillment, activated just in time for one hour",
     "Re-read the assignment: present, eligible not standing, with the requested end date",
     "entra_user", "Request ID, approver and time, activation ID, re-read of the assignment",
     "AC-2; AC-3; AC-5", "Access Governance", "Approved", ""),
    ("EX-05", "Attribute correction: office location wrong in the directory", "Machine, gated", "event",
     "Drift reconciliation", "Core HCM (HR), then Entra ID", "attribute_drift", "P3",
     "Drift finding: directory office location differs from HR",
     "HR data steward corrects the HR record; the directory follows HR",
     "Correct the value in Core HCM; inbound provisioning carries it to Entra ID",
     "svc-hr-inbound (provisioning service identity)",
     "Re-read the attribute in Entra ID: equals the corrected HR value",
     "entra_user", "Finding ID, HR change record, re-read result",
     "AC-2; CA-7", "HR Data Stewards", "Approved", "Fix the source, never the directory."),
    ("EX-06", "Stale access removal: entitlement unused for 90 days", "Machine, gated", "schedule",
     "Identity governance platform", "Entra ID", "stale_access", "P2",
     "Quarterly access review finding: entitlement unused for 90 days",
     "Entitlement owner decides in the access review",
     "Identity governance platform removes the entitlement through its connector",
     "svc-iga-connector, elevated just in time",
     "Re-read: the entitlement is absent from the account",
     "isc_account", "Review decision, removal run log, re-read result",
     "AC-2; AC-6", "Access Governance", "Approved", ""),
    ("EX-07", "Orphan account: AD account with no HR record", "Human decision", "schedule",
     "Drift reconciliation", "AD DS", "orphan_account", "P2",
     "Drift finding: an AD account with no matching HR record",
     "Application owner of the account confirms it has no owner",
     "Disable the account through the identity governance platform's AD connector",
     "svc-iga-connector, elevated just in time",
     "Re-read the account on its source: disabled is true",
     "isc_account", "Finding ID, owner confirmation, run log, re-read result",
     "AC-2; CA-7", "Identity Operations", "Approved", ""),
    ("EX-08", "Contractor end date reached", "Machine, gated", "schedule",
     "Contractor record (sponsor system)", "Entra ID", "stale_access", "P2",
     "Contract end date reached on the contractor record",
     "Contractor's sponsor; an extension needs the sponsor's approval",
     "Lifecycle workflow disables the account on the end date",
     "wf-contractor-end (lifecycle workflow managed identity)",
     "Re-read: accountEnabled is false, or a new end date approved by the sponsor is recorded",
     "entra_user", "End date, sponsor decision, workflow run ID, re-read result",
     "AC-2", "Identity Operations", "Approved", ""),
    ("EX-09", "Certificate renewal for an internal web server", "Protocol-enforced", "schedule",
     "Certificate manager", "web01.contoso.example", "none", "P4",
     "Issuance policy: the certificate is inside its renewal window",
     "Certificate policy; no approval per renewal",
     "ACME client on the server renews the certificate",
     "ACME client identity on the server",
     "Re-read the endpoint: the new certificate is served and expires later than the old one",
     "other", "Issuance log, inventory entry, endpoint re-read",
     "SC-12; SC-17", "PKI Team", "Approved",
     "Protocol-enforced: no ticket per renewal. Anomalies go to the front door."),
    ("EX-10", "Emergency access account check", "Human decision", "schedule",
     "Sign-in logs", "Entra ID", "credential_drift", "P1",
     "Daily check, or a sign-in alert on an emergency access account",
     "Identity security lead",
     "Read sign-in logs and authentication methods through Graph (read only)",
     "svc-monitor-ro (read-only monitoring identity)",
     "Re-read: no unexplained sign-in; registered methods match the sealed record",
     "entra_user", "Check result, sign-in log extract, the lead's decision",
     "AC-2; SI-4", "Identity Security", "Approved",
     "Raised to P1 from the class default P2: any use of an emergency access account is critical."),
    ("EX-11", "Privileged role activation review", "Human decision", "schedule",
     "Privileged Identity Management", "Entra ID", "unauthorized_write", "P2",
     "Weekly review: each role activation is matched to a change or incident task",
     "Privileged access owner",
     "If no task explains an activation: remove the eligibility through Graph after the owner decides",
     "svc-pim-admin, activated just in time",
     "Re-read: every activation in the week links to a task, or the eligibility is gone",
     "entra_user", "Activation list, matched task IDs, the owner's decision, re-read",
     "AC-6; AU-10; CM-5(1)", "Identity Security", "Approved", ""),
    ("EX-12", "Group owner change", "Machine, gated", "request",
     "ITSM catalog", "Entra ID", "attribute_drift", "P3",
     "Request from the departing owner, or a finding that a group has no owner",
     "Data owner for the group's resource names the new owner",
     "Set the group owner through Graph",
     "svc-fulfillment, activated just in time",
     "Re-read the group's owners: the new owner is present, the old owner is not",
     "other", "Request ID, approval, re-read of owners",
     "AC-2; AC-5", "Access Governance", "Approved", ""),
    ("EX-13", "Service account owner leaving", "Human decision", "event",
     "Core HCM (HR)", "Service account register", "orphan_account", "P2",
     "HR separation of a person who owns three service accounts",
     "Application owner names the new owner",
     "Update the owner in the register and the application owners through Graph",
     "svc-npi-register, activated just in time",
     "Re-read the register and the app's owners: a current employee owns each account",
     "other", "HR event ID, the new owner's acceptance, re-read",
     "AC-2; CM-8", "Identity Operations", "Approved", ""),
    ("EX-14", "Failed provisioning retry", "Human decision", "event",
     "Provisioning logs", "Entra ID", "missing_identity", "P2",
     "Provisioning error: a hire from HR has no account after the run",
     "Identity operations lead reads the error and approves the retry",
     "Fix the cause, then rerun provisioning for the one user",
     "svc-hr-inbound (provisioning service identity)",
     "Re-read the user by employeeId: the account exists with HR's values",
     "entra_user", "Error ID, the lead's decision, rerun log, re-read",
     "AC-2; CA-7", "Identity Operations", "Approved", ""),
    ("EX-15", "Schema drift in the HR feed", "Machine, gated", "event",
     "HR feed validation", "HR-to-Entra middleware", "schema_drift", "P3",
     "Feed validation finding: hire dates arrive in a new format",
     "Pipeline owner approves the transform change",
     "Deploy a versioned transform through the pipeline's change process",
     "svc-pipeline-deploy (deployment identity)",
     "Re-run validation on the next feed: zero format failures",
     "other", "Finding ID, change record, transform version and hash, validation result",
     "CM-3; CM-4(2)", "Integration Team", "Approved", ""),
    ("EX-16", "Unauthorized direct write to a group", "Human decision", "event",
     "Native change detection", "AD DS", "unauthorized_write", "P2",
     "Native change event: a member was added outside the front door",
     "Group's access owner decides keep (with a request) or revert",
     "If revert: the identity governance platform removes the membership",
     "svc-iga-connector, elevated just in time",
     "Re-read the membership: it matches the owner's decision",
     "isc_account", "Change event, the owner's decision, run log, re-read",
     "CM-5(1); CM-6(2); AU-10; SI-4", "Identity Security", "Approved", ""),
    ("EX-17", "Admin consent request triage", "Human decision", "event",
     "Admin consent workflow (Microsoft's notification email)", "Entra ID", "none", "P3",
     "A user's admin consent request; the notification email opens a ticket",
     "Designated reviewer who holds a role that can grant the consent: Cloud Application Administrator "
     "or Application Administrator for delegated permissions; Privileged Role Administrator for "
     "Microsoft Graph application permissions",
     "Check publisher verification, the permissions requested and the business need; approve or deny "
     "in the Entra admin center or through Graph",
     "The reviewer, through a just-in-time role activation",
     "Re-read the grant: GET /oauth2PermissionGrants filtered on the app's clientId (delegated) or "
     "GET /servicePrincipals/{id}/appRoleAssignments (application); after a denial, no grant",
     "other", "Request ID, publisher status, permissions, decision and reason, re-read of the grant",
     "AC-3; AC-6; AU-12", "Application Governance", "Approved",
     "Requests expire after the days set in the admin consent settings; reviewers can get reminders."),
    ("EX-18", "App registration lifecycle request (create, change or delete)", "Machine, gated", "request",
     "ITSM catalog item", "Entra ID", "none", "P3",
     "Catalog request to create, change or delete an application, naming its owner and sponsor",
     "Sponsor of the application, with the identity governance office for new apps",
     "Graph: POST /applications, PATCH or DELETE /applications/{id}; credential is a certificate or "
     "federated credential, never a client secret",
     "svc-app-lifecycle, with the least Graph permission the action needs",
     "Re-read GET /applications/{id} and GET /servicePrincipals filtered on appId: owners set, a "
     "certificate or federated credential present, no password credential; after a delete, the app "
     "is in deleted items",
     "other", "Request ID, approval, Graph request IDs, re-read of the app and service principal",
     "AC-2; AC-6; CM-3; IA-5", "Application Governance", "Approved",
     "Delete is a soft delete: Graph keeps the app restorable for 30 days. Restoring an app doesn't "
     "restore its service principal."),
    ("EX-19", "ImmutableId update to hard-match a cloud account", "Human decision", "request",
     "ITSM catalog", "Entra ID", "missing_identity", "P2",
     "Request to link a cloud-managed account to its AD account before the AD account enters sync scope",
     "Hybrid identity lead; the account owner confirms both accounts are the same person",
     "Update-MgUser -OnPremisesImmutableId with the base64 of the AD account's mS-DS-ConsistencyGuid "
     "(or objectGUID), or a Graph PATCH on the user",
     "A named administrator, through a time-boxed role activation",
     "GET /users/{id}?$select=onPremisesImmutableId equals the approved value; the next sync run "
     "matches the account with no errors",
     "entra_user", "Approval, before and after values, the AD object's GUID, sync run result",
     "AC-2; IA-4; CM-3", "Hybrid Identity Team", "Approved",
     "High impact: human approval required. A wrong value links the wrong AD account, which then "
     "overwrites the cloud account. Only for a cloud-managed, non-privileged user."),
    ("EX-20", "Integration credential issued or rotated through the ITSM", "Machine, gated", "schedule",
     "ITSM catalog and expiry check", "Agency vault", "credential_drift", "P2",
     "Catalog request for an integration credential, or the expiry check 30 days before expiry",
     "System owner of the integration; never the requester",
     "Issuing system creates the credential and writes it straight to the vault",
     "svc-cred-rotation, with rights to one vault path",
     "Re-read: a test call with the new credential works, its expiry is recorded, and a call with "
     "the old credential fails",
     "other", "Request ID, approval, vault version ID, expiry date, revocation result",
     "IA-5; AC-6; AU-12", "Integration Team", "Approved",
     "No credential value ever goes in the ticket."),
    ("EX-21", "A request arrives through ServiceNow Otto or the Virtual Agent", "Machine, gated",
     "request (automated intake)",
     "ServiceNow Otto or Virtual Agent", "Per the request (here: Entra ID)", "none", "P3",
     "A request captured by the conversational intake; the channel is recorded on the task as the trigger",
     "The approver the catalog item names, the same as for a form request",
     "The intake creates the typed catalog task; fulfillment runs the same native action as a form request",
     "Intake channel's integration identity (create only); fulfillment identity unchanged",
     "Re-read as for the underlying task, and confirm the intake record links to the task",
     "other", "Intake record ID, typed task, approval, re-read",
     "AC-2; AC-5; AU-12", "Service Desk", "Approved",
     "The channel is a trigger, never an approver."),
    ("EX-22", "ServiceNow Otto (or an AI agent it routes to) takes an action", "Machine, gated", "request",
     "ServiceNow Otto", "Entra ID", "none", "P3",
     "A catalog request for standard software access, handled by the AI agent",
     "The approver the catalog item names approves before the agent acts",
     "The agent adds the user to the software-access group through the fulfillment API; for admin "
     "consent, app deletion or an ImmutableId change it only prepares the change for a person",
     "ai-fulfillment-agent (a non-person identity with scoped roles, no standing admin rights)",
     "An independent checker re-reads the group membership; the agent's own success message isn't evidence",
     "other", "Request ID, approval, AI Control Tower log of the agent's action, independent re-read",
     "AC-2; AC-6; AC-6(5); AU-2; AU-12; CM-3; CM-8(4)", "Service Desk", "Approved",
     "The agent's actions are logged under its own identity, apart from people's. Confirm availability "
     "in the government cloud with ServiceNow and the FedRAMP Marketplace."),
    ("EX-P1", "Practice: missing account found by nightly reconciliation", "Machine, gated", "schedule",
     "Drift reconciliation", "Entra ID", "missing_identity", "P2",
     "Drift finding: an HR record with no directory account",
     "Identity operations lead",
     "Create the account through Graph",
     "svc-fulfillment",
     "Re-read the user by employeeId",
     "entra_user", "",
     "AC-2", "Identity Operations", "Draft", ""),
    ("EX-P2", "Practice: remove a shared mailbox permission", "Machine, gated", "request",
     "ITSM catalog", "Exchange Online", "stale_access", "P4",
     "Request from the mailbox owner",
     "Mailbox owner",
     "Remove the permission through PowerShell",
     "svc-fulfillment",
     "Re-read the mailbox permissions",
     "other", "Request ID and re-read",
     "", "", "Draft", ""),
    ("EX-P3", "Practice: an AI agent deletes an app registration", "Machine, gated", "request",
     "ServiceNow Otto", "Entra ID", "none", "P3",
     "Catalog request to delete an application",
     "Application sponsor",
     "The agent deletes the application through Graph",
     "ai-fulfillment-agent",
     "The agent reports that the delete succeeded",
     "other", "The agent's log",
     "AC-2; CM-3", "Application Governance", "Draft", ""),
]

# Per row: identity type, identity sponsor, high impact, human approval before action, re-read by.
_SVC, _MI, _AI, _PERSON, _PROTO = "Service identity", "Managed or workload identity", "AI agent",     "Person (time-boxed role)", "Protocol client"
_IND, _SELF = "Independent checker", "The identity that acted"
_SAFEGUARDS = {
    "EX-01": (_SVC, "Identity Operations manager", "no", "no", _IND),
    "EX-02": (_SVC, "Access Governance manager", "no", "yes", _IND),
    "EX-03": (_MI, "Identity Operations manager", "no", "no", _IND),
    "EX-04": (_SVC, "Access Governance manager", "no", "yes", _IND),
    "EX-05": (_SVC, "Identity Operations manager", "no", "yes", _IND),
    "EX-06": (_SVC, "Access Governance manager", "no", "yes", _IND),
    "EX-07": (_SVC, "Access Governance manager", "no", "yes", _IND),
    "EX-08": (_MI, "Identity Operations manager", "no", "no", _IND),
    "EX-09": (_PROTO, "PKI Team lead", "no", "no", _IND),
    "EX-10": (_SVC, "Identity Security lead", "no", "yes", _IND),
    "EX-11": (_SVC, "Identity Security lead", "no", "yes", _IND),
    "EX-12": (_SVC, "Access Governance manager", "no", "yes", _IND),
    "EX-13": (_SVC, "Identity Operations manager", "no", "yes", _IND),
    "EX-14": (_SVC, "Identity Operations manager", "no", "yes", _IND),
    "EX-15": (_SVC, "Integration Team lead", "no", "yes", _IND),
    "EX-16": (_SVC, "Identity Security lead", "no", "yes", _IND),
    "EX-17": (_PERSON, "", "yes", "yes", _IND),
    "EX-18": (_SVC, "Application Governance lead", "yes", "yes", _IND),
    "EX-19": (_PERSON, "", "yes", "yes", _IND),
    "EX-20": (_SVC, "Integration Team lead", "no", "yes", _IND),
    "EX-21": (_SVC, "Service Desk manager", "no", "yes", _IND),
    "EX-22": (_AI, "Service Desk manager", "no", "yes", _IND),
    "EX-P1": (_SVC, "Identity Operations manager", "no", "no", _IND),
    "EX-P2": (_SVC, "", "no", "no", _IND),
    "EX-P3": (_AI, "", "yes", "no", _SELF),
}

PRACTICE_IDS = {"EX-P1", "EX-P2", "EX-P3"}


def rows() -> list[dict]:
    """Each example as a dict keyed by the catalog's headers, with KSIs and window filled in."""
    out = []
    for values in _ROWS:
        row = dict(zip(_FIELDS, values))
        row.update(zip(["Identity type", "Identity sponsor", "High impact", "Human approval before action",
                        "Re-read by"], _SAFEGUARDS[row["Task ID"]]))
        row["KSIs"] = "; ".join(cs.ksis_for(cs.split_list(row["NIST SP 800-53 controls"])))
        row["Response window"] = cs.window_text(row["Severity"])
        out.append({name: row.get(name, "") for name in cs.HEADERS})
    return out
