from copy import deepcopy

FORBIDDEN_EVIDENCE_KEYS = {"secret_value","plaintext","password","token","private_key","credential_value"}
REQUIRED_RECORD_FIELDS = [
    "secret_object","owning_service","workload_identity","required_capability","allowed_access",
    "human_access","retrieval_path","rotation_revocation_evidence","audit_fields","residual_risk"
]

def evidence_row_complete(row):
    if not all(k in row and row[k] not in (None, "", []) for k in REQUIRED_RECORD_FIELDS):
        return False
    if any(k in row for k in FORBIDDEN_EVIDENCE_KEYS):
        return False
    audit_fields=row.get("audit_fields",[])
    if isinstance(audit_fields,(list,tuple,set)) and any(str(k) in FORBIDDEN_EVIDENCE_KEYS for k in audit_fields):
        return False
    return True

def evaluate_access(request, policy):
    identity=request.get("identity_id","")
    secret_id=request.get("secret_id","")
    capability=request.get("capability","")
    if secret_id in {"*","secret://*"} and policy.get("wildcard_secret_access_forbidden",True):
        return {"decision":"DENY_WILDCARD_SCOPE","identity_id":identity,"secret_id":secret_id,"capability":capability}
    if secret_id in set(policy.get("revoked_secret_ids",[])):
        return {"decision":"DENY_REVOKED_SECRET","identity_id":identity,"secret_id":secret_id,"capability":capability}
    objects=policy.get("secret_objects",{})
    if secret_id not in objects or not objects[secret_id].get("active",False):
        return {"decision":"DENY_UNKNOWN_OR_INACTIVE_SECRET","identity_id":identity,"secret_id":secret_id,"capability":capability}
    allowed=policy.get("identity_access",{}).get(identity,{}).get(secret_id,[])
    if capability not in allowed:
        return {"decision":"DENY_SCOPE_OR_CAPABILITY","identity_id":identity,"secret_id":secret_id,"capability":capability}
    return {"decision":"ALLOW_SCOPED_ACCESS","identity_id":identity,"secret_id":secret_id,"capability":capability}

def evaluate_rotation(event, policy):
    old_id=event.get("old_secret_id","")
    new_id=event.get("new_secret_id","")
    if not old_id or not new_id or old_id==new_id:
        return {"decision":"DENY_ROTATION_INVALID_IDS","old_secret_id":old_id,"new_secret_id":new_id}
    objects=policy.get("secret_objects",{})
    if old_id not in objects or new_id not in objects:
        return {"decision":"DENY_ROTATION_UNKNOWN_SECRET","old_secret_id":old_id,"new_secret_id":new_id}
    required=policy.get("rotation_requires",{})
    for key in ("new_active","old_revoked","consumers_updated","audit_event_present"):
        if required.get(key,True) and event.get(key) is not True:
            return {"decision":"DENY_ROTATION_INCOMPLETE","missing":key,"old_secret_id":old_id,"new_secret_id":new_id}
    return {"decision":"ROTATION_COMPLETE","old_secret_id":old_id,"new_secret_id":new_id}

def build_audit_event(observation, lifecycle_event="ACCESS_DECISION"):
    return {
      "identity_id":observation.get("identity_id","SYSTEM"),
      "secret_id":observation.get("secret_id") or observation.get("new_secret_id") or "NONE",
      "decision":observation.get("decision","UNKNOWN"),
      "lifecycle_event":lifecycle_event,
      "timestamp":"2026-09-16T00:00:00Z"
    }

def build_record(secret_object, owner, identity, capability, access_result, rotation_result):
    row={
      "secret_object":secret_object,
      "owning_service":owner,
      "workload_identity":identity,
      "required_capability":capability,
      "allowed_access":access_result.get("decision"),
      "human_access":"NO_DIRECT_READ",
      "retrieval_path":f"identity://{identity} -> {secret_object}",
      "rotation_revocation_evidence":rotation_result.get("decision"),
      "audit_fields":["identity_id","secret_id","decision","lifecycle_event","timestamp"],
      "residual_risk":"Synthetic local evidence only; no production assurance."
    }
    return row

def classify_transfer(kind):
    if kind=="batch_worker":
        return {"status":"REBUILD_SCOPE","lifetime":"short_job","human_access":"NO_DIRECT_READ","scope":"ONE_JOB_OBJECT_ONLY"}
    if kind=="interactive_api":
        return {"status":"REBUILD_SCOPE","lifetime":"long_running","human_access":"NO_DIRECT_READ","scope":"OWN_SERVICE_OBJECTS_ONLY"}
    return {"status":"UNKNOWN_CONTEXT","lifetime":None,"human_access":None,"scope":None}
