"""Parse the raw Synology DSM API discovery dump into a clean inventory doc.

Input:  docs/migration/synology-permission-inventory-raw.md
Output: docs/migration/synology-permission-inventory.md (clean digest + mapping table)
"""
import json
import re
from pathlib import Path

RAW = Path('clients/cascades-tucson/docs/migration/synology-permission-inventory-raw.md')
OUT = Path('clients/cascades-tucson/docs/migration/synology-permission-inventory.md')


def extract_json_after(label, text):
    """Find the first JSON object after '--- <label> ---' marker using bracket-matching."""
    idx = text.find(f'--- {label} ---')
    if idx < 0:
        return None
    start = text.find('{', idx)
    if start < 0:
        return None
    depth = 0
    in_str = False
    esc = False
    for i in range(start, len(text)):
        c = text[i]
        if esc:
            esc = False
            continue
        if c == "\\":
            esc = True
            continue
        if c == '"':
            in_str = not in_str
            continue
        if in_str:
            continue
        if c == '{':
            depth += 1
        elif c == '}':
            depth -= 1
            if depth == 0:
                return text[start:i + 1]
    return None


def find_all_json_after(label_pattern, text):
    """Find every JSON block whose label matches the regex pattern."""
    results = []
    for m in re.finditer(r'--- (' + label_pattern + r') ---\n', text):
        label = m.group(1)
        body = extract_json_after(label, text[m.start():])
        if body is None:
            continue
        try:
            results.append((label, json.loads(body)))
        except json.JSONDecodeError:
            pass
    return results


def main():
    content = RAW.read_text(encoding='utf-8')
    content = re.sub(r'\n{2,}', '\n', content)

    # USERS
    users_obj = json.loads(extract_json_after('SYNO.Core.User list', content))
    users = users_obj.get('data', {}).get('users', [])

    # GROUPS
    groups_obj = json.loads(extract_json_after('SYNO.Core.Group list', content))
    groups = groups_obj.get('data', {}).get('groups', [])

    # SHARES
    shares_obj = json.loads(extract_json_after('SYNO.Core.Share list', content))
    shares = shares_obj.get('data', {}).get('shares', [])

    # PER-SHARE PERMISSIONS
    perms = find_all_json_after(r"share '[^']+' - local (?:user|group) permissions", content)

    # Build a lookup: share_name -> {'users': {name: flags}, 'groups': {name: flags}}
    share_perms = {}
    for label, obj in perms:
        m = re.match(r"share '([^']+)' - local (user|group) permissions", label)
        if not m:
            continue
        sname, scope = m.group(1), m.group(2)
        share_perms.setdefault(sname, {'user': {}, 'group': {}})
        for item in obj.get('data', {}).get('items', []):
            nm = item.get('name', '?')
            share_perms[sname][scope][nm] = {
                'admin': item.get('is_admin', False),
                'deny': item.get('is_deny', False),
                'ro': item.get('is_readonly', False),
                'rw': item.get('is_writable', False),
                'custom': item.get('is_custom', False),
            }

    # Effective write-access table: for each share, who can actually write (writable OR admin) AND not denied
    def effective(flags):
        if flags['deny']:
            return 'DENY'
        if flags['rw']:
            return 'RW'
        if flags['admin']:
            return 'Admin(full)'
        if flags['ro']:
            return 'RO'
        return '-'

    # Write the digest
    out_lines = []
    out_lines.append('# Synology cascadesDS — Permission Inventory (2026-04-22 via DSM API)')
    out_lines.append('')
    out_lines.append('**Source:** `docs/migration/synology-permission-inventory-raw.md`')
    out_lines.append('**Method:** DSM HTTP API v7 via CS-SERVER GuruRMM agent (SSH not enabled on Synology as of 2026-04-22)')
    out_lines.append('')
    out_lines.append('## Summary counts')
    out_lines.append('')
    out_lines.append(f'- Synology local users: **{len(users)}**')
    out_lines.append(f'- Synology local groups: **{len(groups)}**')
    out_lines.append(f'- Shares: **{len(shares)}**')
    out_lines.append('')

    # Users
    out_lines.append('## Users')
    out_lines.append('')
    out_lines.append('| Name | UID | Expired | Email | Description | Groups |')
    out_lines.append('|---|---:|---|---|---|---|')
    for u in sorted(users, key=lambda x: x.get('name', '').lower()):
        name = u.get('name', '?')
        uid = u.get('uid', '-')
        expired = 'yes' if u.get('expired') in ('now', 'expired', True) else 'no'
        email = u.get('email', '') or ''
        desc = (u.get('description', '') or '').replace('|', r'\|')
        groups_list = u.get('additional', {}).get('groups', []) if isinstance(u.get('additional'), dict) else []
        groups_str = ', '.join(groups_list) if groups_list else '-'
        out_lines.append(f'| `{name}` | {uid} | {expired} | {email} | {desc} | {groups_str} |')
    out_lines.append('')

    # Groups
    out_lines.append('## Groups')
    out_lines.append('')
    out_lines.append('| Name | GID | Type | Description | Members |')
    out_lines.append('|---|---:|---|---|---|')
    for g in groups:
        name = g.get('name', '?')
        gid = g.get('gid', '-')
        gtype = g.get('type', '-') or '-'
        desc = g.get('description', '') or ''
        members = g.get('additional', {}).get('members', []) if isinstance(g.get('additional'), dict) else []
        member_names = ', '.join([m.get('name', '?') if isinstance(m, dict) else str(m) for m in members]) or '(unable to enumerate via API — error 3201)'
        out_lines.append(f'| `{name}` | {gid} | {gtype} | {desc} | {member_names} |')
    out_lines.append('')
    out_lines.append('> Note: DSM API `SYNO.Core.Group.Member` returned error 3201 for all groups with this API path; membership was only partially available via the `members` additional field on the group list call. Full membership requires DSM web UI or CLI `synogroup --get <name>` via SSH.')
    out_lines.append('')

    # Shares + permissions
    out_lines.append('## Shares')
    out_lines.append('')
    out_lines.append('| Share | Volume Path | Hidden | Description |')
    out_lines.append('|---|---|---|---|')
    for s in shares:
        name = s.get('name', '?')
        vol = s.get('vol_path', '') or s.get('additional', {}).get('vol_path', '') or ''
        hidden = s.get('additional', {}).get('hidden', False) if isinstance(s.get('additional'), dict) else False
        desc = (s.get('additional', {}).get('desc', '') or '') if isinstance(s.get('additional'), dict) else ''
        out_lines.append(f'| `{name}` | {vol} | {hidden} | {desc} |')
    out_lines.append('')

    # Effective permissions per share (write-capable or explicit-deny users/groups)
    out_lines.append('## Effective share permissions')
    out_lines.append('')
    out_lines.append('"Admin(full)" means the account inherits full control via administrators-group membership. "RW" means explicitly writable. "DENY" means explicitly denied (wins over everything else in Synology ACL order). Blank / absent means no explicit permission (effectively no access beyond what the users group grants).')
    out_lines.append('')

    # Identity sets to call out
    known_current_employees = {
        'Ashley Jensen', 'CasAdmin201', 'ChristinaDupras', 'Crystal Rodriguez',
        'Crystal Suszek', 'Dining Manager', 'JD Martin', 'John Trozzi',
        'Karen Rossini', 'Lois Lane', 'Lupe Sanchez', 'Megan Hiatt',
        'meredith kuhn', 'Shelby Trozzi', 'Stephanie Devin', 'Susan Hicks',
        'Veronica'
    }
    known_departed = {
        'Amber M Lee', 'Ann Dery', 'Anna Pitzlin', 'Britney Thompson',
        'Haris Durut', 'Monica RamirezRossette', 'Nela Durut-Azizi',
        'Tamra Johnson'
    }
    role_accounts = {
        'Accounting', 'Dining Manager', 'Front Desk', 'Memcare Receptionist',
        'mcnurse', 'memcarenurse', 'Nurse Tower'
    }
    service_or_admin = {'admin', 'guest', 'guru', 'VPNClient'}

    for sname in sorted(share_perms.keys()):
        out_lines.append(f'### Share: `{sname}`')
        out_lines.append('')

        # Groups first
        g_perms = share_perms[sname]['group']
        out_lines.append('**Groups:**')
        out_lines.append('')
        out_lines.append('| Group | Effective |')
        out_lines.append('|---|---|')
        for gname in sorted(g_perms.keys()):
            out_lines.append(f'| `{gname}` | {effective(g_perms[gname])} |')
        out_lines.append('')

        # Users with non-default effective permission
        u_perms = share_perms[sname]['user']
        nondefault = {n: f for n, f in u_perms.items()
                      if f['rw'] or f['ro'] or f['deny'] or f['admin']}
        if nondefault:
            out_lines.append('**Users with explicit permission:**')
            out_lines.append('')
            out_lines.append('| User | Effective | Status |')
            out_lines.append('|---|---|---|')
            for n in sorted(nondefault.keys(), key=lambda x: x.lower()):
                eff = effective(nondefault[n])
                if n in known_departed:
                    status = 'DEPARTED'
                elif n in role_accounts:
                    status = 'ROLE (HIPAA concern)'
                elif n in service_or_admin:
                    status = 'service/admin'
                elif n in known_current_employees:
                    status = 'current'
                else:
                    status = '?'
                out_lines.append(f'| `{n}` | {eff} | {status} |')
            out_lines.append('')
        else:
            out_lines.append('*(No user-level overrides — share access is group-based only.)*')
            out_lines.append('')

    OUT.write_text('\n'.join(out_lines), encoding='utf-8')
    print(f'Wrote {OUT}')
    print(f'Bytes: {OUT.stat().st_size}')


if __name__ == '__main__':
    main()
