# -------------------------------------------------------------------
# task.check – Redis configuration validator
# -------------------------------------------------------------------
# Soft checks scan for specific config keywords and hand over captured
# values to Python generators that print assert: conditions.
# -------------------------------------------------------------------

# -------------------------------------------------------------------
# 1. bind directive
# -------------------------------------------------------------------
note: bind – the server should listen on loopback only
regexp: ^^ \s* bind \s+ (.+) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
addrs = []
for c in captures():
    addrs.extend(c[0].split())
state = get_state()
state['bind_found'] = True
update_state(state)
# loopback-only check
loopback_ok = all(a in ('127.0.0.1', '::1', '-::1') for a in addrs)
print(f"assert: {1 if loopback_ok else 0} bind should be loopback (127.0.0.1 ::1)")
CODE

# -------------------------------------------------------------------
# 2. port directive
# -------------------------------------------------------------------
note: port – must be a valid TCP port (1-65535)
regexp: ^^ \s* port \s+ (\d+) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    port = int(c[0])
    ok = 1 <= port <= 65535
    print(f"assert: {1 if ok else 0} port {port} in range 1-65535")
CODE

# -------------------------------------------------------------------
# 3. protected-mode
# -------------------------------------------------------------------
note: protected-mode – should be enabled (yes)
regexp: ^^ \s* protected\-mode \s+ (yes|no) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    val = c[0].strip().lower()
    ok = (val == 'yes')
    print(f"assert: {1 if ok else 0} protected-mode should be yes, got {val}")
CODE

# -------------------------------------------------------------------
# 4. daemonize
# -------------------------------------------------------------------
note: daemonize – valid values are yes/no
regexp: ^^ \s* daemonize \s+ (yes|no) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    val = c[0].strip().lower()
    ok = val in ('yes', 'no')
    print(f"assert: {1 if ok else 0} daemonize valid (yes/no), got {val}")
CODE

# -------------------------------------------------------------------
# 5. requirepass
# -------------------------------------------------------------------
note: requirepass – a password should be set
regexp: ^^ \s* requirepass \s+ (.+) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    pwd = c[0].strip().strip('"').strip("'")
    ok = len(pwd) >= 8
    state = get_state()
    state['requirepass_set'] = True
    update_state(state)
    print(f"assert: {1 if ok else 0} requirepass length >= 8, got {len(pwd)} chars")
CODE

# -------------------------------------------------------------------
# 6. dir (working directory)
# -------------------------------------------------------------------
note: dir – working directory must be specified
regexp: ^^ \s* dir \s+ (.+) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    path = c[0].strip()
    ok = len(path) > 0
    print(f"assert: {1 if ok else 0} dir must be non-empty, got '{path}'")
CODE

# -------------------------------------------------------------------
# 7. dbfilename
# -------------------------------------------------------------------
note: dbfilename – RDB filename should be set
regexp: ^^ \s* dbfilename \s+ (.+) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    name = c[0].strip()
    ok = len(name) > 0
    print(f"assert: {1 if ok else 0} dbfilename must be non-empty, got '{name}'")
CODE

# -------------------------------------------------------------------
# 8. save (RDB snapshot policy)
# -------------------------------------------------------------------
note: save – RDB save policy should be defined
regexp: ^^ \s* save \s+ (.+) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    args = c[0].strip()
    ok = len(args) > 0
    print(f"assert: {1 if ok else 0} save policy defined, got '{args}'")
CODE

# -------------------------------------------------------------------
# 9. maxmemory
# -------------------------------------------------------------------
note: maxmemory – a memory limit should be set
regexp: ^^ \s* maxmemory \s+ (.+) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    val = c[0].strip()
    ok = len(val) > 0
    print(f"assert: {1 if ok else 0} maxmemory set, got '{val}'")
CODE

# -------------------------------------------------------------------
# 10. maxmemory-policy
# -------------------------------------------------------------------
note: maxmemory-policy – must be a recognised eviction policy
regexp: ^^ \s* maxmemory\-policy \s+ ([\w\-]+) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
known = {'noeviction','allkeys-lru','volatile-lru','allkeys-random',
         'volatile-random','volatile-ttl','allkeys-lfu','volatile-lfu'}
for c in captures():
    policy = c[0].strip().lower()
    ok = policy in known
    state = get_state()
    state['maxmemory_policy'] = policy
    update_state(state)
    print(f"assert: {1 if ok else 0} maxmemory-policy valid, got {policy}")
CODE

# -------------------------------------------------------------------
# 11. logfile
# -------------------------------------------------------------------
note: logfile – log file path must be provided
regexp: ^^ \s* logfile \s+ (.+) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    path = c[0].strip().strip('"').strip("'")
    ok = len(path) > 0
    print(f"assert: {1 if ok else 0} logfile must be non-empty, got '{path}'")
CODE

# -------------------------------------------------------------------
# 12. tcp-keepalive
# -------------------------------------------------------------------
note: tcp-keepalive – should be a positive number
regexp: ^^ \s* tcp\-keepalive \s+ (\d+) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    val = int(c[0])
    ok = val > 0
    print(f"assert: {1 if ok else 0} tcp-keepalive > 0, got {val}")
CODE

# -------------------------------------------------------------------
# 13. include directive
# -------------------------------------------------------------------
note: include – optional, but paths must be non-empty
regexp: ^^ \s* include \s+ (.+) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    path = c[0].strip().strip('"').strip("'")
    ok = len(path) > 0
    print(f"assert: {1 if ok else 0} include path valid, got '{path}'")
CODE

# -------------------------------------------------------------------
# 14. loadmodule directive
# -------------------------------------------------------------------
note: loadmodule – optional, but path must be non-empty
regexp: ^^ \s* loadmodule \s+ (.+) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    path = c[0].strip().strip('"').strip("'")
    ok = len(path) > 0
    print(f"assert: {1 if ok else 0} loadmodule path valid, got '{path}'")
CODE

# -------------------------------------------------------------------
# 15. replicaof directive
# -------------------------------------------------------------------
note: replicaof – if present, must supply master host and port
regexp: ^^ \s* replicaof \s+ (.+) \s+ (\d+) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    host = c[0].strip()
    port = int(c[1])
    ok = len(host) > 0 and 1 <= port <= 65535
    print(f"assert: {1 if ok else 0} replicaof {host}:{port} valid")
CODE

# -------------------------------------------------------------------
# 16. masterauth directive
# -------------------------------------------------------------------
note: masterauth – if replicaof is used, masterauth should be set
regexp: ^^ \s* masterauth \s+ (.+) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    auth = c[0].strip().strip('"').strip("'")
    ok = len(auth) > 0
    print(f"assert: {1 if ok else 0} masterauth set, got {len(auth)} chars")
CODE

# -------------------------------------------------------------------
# 17. ACL file directive
# -------------------------------------------------------------------
note: aclfile – if ACLs are enabled, path must be valid
regexp: ^^ \s* aclfile \s+ (.+) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    path = c[0].strip().strip('"').strip("'")
    ok = len(path) > 0
    print(f"assert: {1 if ok else 0} aclfile path valid, got '{path}'")
CODE

# -------------------------------------------------------------------
# 18. ACL user definitions (simplified check)
# -------------------------------------------------------------------
note: user – ACL user definitions must have a valid status (on|off)
regexp: ^^ \s* user \s+ \S+ \s+ (on|off) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    status = c[0].strip().lower()
    ok = status in ('on', 'off')
    print(f"assert: {1 if ok else 0} user status is on/off, got {status}")
CODE

# -------------------------------------------------------------------
# 19. databases directive
# -------------------------------------------------------------------
note: databases – number of databases must be a positive integer
regexp: ^^ \s* databases \s+ (\d+) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    num = int(c[0])
    ok = num > 0
    print(f"assert: {1 if ok else 0} databases > 0, got {num}")
CODE

# -------------------------------------------------------------------
# 20. timeout directive
# -------------------------------------------------------------------
note: timeout – client idle timeout should be a non-negative integer
regexp: ^^ \s* timeout \s+ (\d+) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    val = int(c[0])
    ok = val >= 0
    print(f"assert: {1 if ok else 0} timeout >= 0, got {val}")
CODE

# -------------------------------------------------------------------
# 21. stop-writes-on-bgsave-error
# -------------------------------------------------------------------
note: stop-writes-on-bgsave-error – should normally be yes
regexp: ^^ \s* stop\-writes\-on\-bgsave\-error \s+ (yes|no) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    val = c[0].strip().lower()
    ok = val == 'yes'
    print(f"assert: {1 if ok else 0} stop-writes-on-bgsave-error is yes, got {val}")
CODE

# -------------------------------------------------------------------
# 22. rdbcompression
# -------------------------------------------------------------------
note: rdbcompression – should normally be yes
regexp: ^^ \s* rdbcompression \s+ (yes|no) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    val = c[0].strip().lower()
    ok = val == 'yes'
    print(f"assert: {1 if ok else 0} rdbcompression is yes, got {val}")
CODE

# -------------------------------------------------------------------
# 23. rdbchecksum
# -------------------------------------------------------------------
note: rdbchecksum – should normally be yes
regexp: ^^ \s* rdbchecksum \s+ (yes|no) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    val = c[0].strip().lower()
    ok = val == 'yes'
    print(f"assert: {1 if ok else 0} rdbchecksum is yes, got {val}")
CODE

# -------------------------------------------------------------------
# 24. appendonly
# -------------------------------------------------------------------
note: appendonly – AOF persistence flag
regexp: ^^ \s* appendonly \s+ (yes|no) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    val = c[0].strip().lower()
    ok = val in ('yes', 'no')
    print(f"assert: {1 if ok else 0} appendonly valid (yes/no), got {val}")
CODE

# -------------------------------------------------------------------
# 25. appendfsync
# -------------------------------------------------------------------
note: appendfsync – must be one of always, everysec, no
regexp: ^^ \s* appendfsync \s+ (always|everysec|no) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    policy = c[0].strip().lower()
    ok = policy in ('always', 'everysec', 'no')
    print(f"assert: {1 if ok else 0} appendfsync valid, got {policy}")
CODE

# -------------------------------------------------------------------
# 26. supervised
# -------------------------------------------------------------------
note: supervised – must be one of no, upstart, systemd, auto
regexp: ^^ \s* supervised \s+ (no|upstart|systemd|auto) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    mode = c[0].strip().lower()
    ok = mode in ('no', 'upstart', 'systemd', 'auto')
    print(f"assert: {1 if ok else 0} supervised valid, got {mode}")
CODE

# -------------------------------------------------------------------
# 27. syslog-enabled
# -------------------------------------------------------------------
note: syslog-enabled – must be yes or no
regexp: ^^ \s* syslog\-enabled \s+ (yes|no) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    val = c[0].strip().lower()
    ok = val in ('yes', 'no')
    print(f"assert: {1 if ok else 0} syslog-enabled valid (yes/no), got {val}")
CODE

# -------------------------------------------------------------------
# 28. loglevel
# -------------------------------------------------------------------
note: loglevel – must be one of debug, verbose, notice, warning
regexp: ^^ \s* loglevel \s+ (debug|verbose|notice|warning) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    level = c[0].strip().lower()
    ok = level in ('debug', 'verbose', 'notice', 'warning')
    print(f"assert: {1 if ok else 0} loglevel valid, got {level}")
CODE

# -------------------------------------------------------------------
# 29. always-show-logo
# -------------------------------------------------------------------
note: always-show-logo – should normally be no
regexp: ^^ \s* always\-show\-logo \s+ (yes|no) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    val = c[0].strip().lower()
    ok = val == 'no'
    print(f"assert: {1 if ok else 0} always-show-logo is no, got {val}")
CODE

# -------------------------------------------------------------------
# 30. set-proc-title
# -------------------------------------------------------------------
note: set-proc-title – should normally be yes
regexp: ^^ \s* set\-proc\-title \s+ (yes|no) \s* $$
generator: <<CODE
!python
from sparrow6lib import *
for c in captures():
    val = c[0].strip().lower()
    ok = val == 'yes'
    print(f"assert: {1 if ok else 0} set-proc-title is yes, got {val}")
CODE

# -------------------------------------------------------------------
# Final cross-checks using accumulated state
# -------------------------------------------------------------------
code: <<CODE
!python
from sparrow6lib import *
state = get_state()
# If requirepass is not set, protected-mode must be yes (checked elsewhere)
if not state.get('requirepass_set'):
    print("assert: 0 requirepass is not set – authentication is disabled")
# If maxmemory is set, a valid policy should be configured
if 'maxmemory_policy' in state and state['maxmemory_policy'] == 'noeviction':
    print("assert: 0 maxmemory-policy is noeviction – consider a different policy")
print("assert: 1 configuration file validation completed")
CODE