import io, re


def load(p):
    raw = io.open(p, encoding='utf-8', newline='').read()
    return raw.replace('\r\n', '\n'), '\r\n' in raw


def save(p, s, crlf):
    if crlf:
        s = s.replace('\n', '\r\n')
    io.open(p, 'w', encoding='utf-8', newline='').write(s)


def cut(s, start, end_incl):
    i = s.index(start)
    j = s.index(end_incl, i) + len(end_incl)
    return s[:i] + s[j:], s[i:j]


# ---- the new file: CRLF like its neighbours
p = 'crates/spt-net/src/net/siblings.rs'
s, _ = load(p)
save(p, s, True)

# ---- register the module
p = 'crates/spt-net/src/net.rs'
s, c = load(p)
assert s.count('pub mod shelllink;\n') == 1
s = s.replace('pub mod shelllink;\n', 'pub mod shelllink;\npub mod siblings;\n')
save(p, s, c)

# ---- wansend: take out node_qualifier + label_of, and the pure sibling items
p = 'crates/spt/src/wansend.rs'
s, c = load(p)
s, nq = cut(s, '/// The best `@node` qualifier for `node` among `peers`: its advertised label\n',
            '    None\n}\n\n')
assert 'fn label_of' in nq and 'fn node_qualifier' in nq
s, _ = cut(s, "/// Another node's row for an id: a SIBLING of the local instance (releases#349).\n",
           '        SendHead::Shell(shell_ref) => (format!("shell {shell_ref}"), false),\n    };\n    if allowed {\n        return None;\n    }\n')
# the remainder of dormant_send_refusal's body up to its end
s, rest = cut(s, '    let here = own_label.map_or_else(|| key_prefix(own_hex), str::to_string);\n',
              '             input, then send again. Nothing was sent."\n        ),\n    })\n}\n\n')
s, hp = cut(s, "/// What a `--handoff` send does before any byte moves (REQ-INSTANCE-HANDOFF).\n",
            "        _ => refuse(format!(\"'@{q}' matches several nodes: {}.\", listing(&named))),\n    }\n}\n\n")
assert 'fn plan_handoff' in hp
# re-export so the CLI's paths stay `crate::wansend::…`
s = s.replace('''use spt_net::net::wanmsg::WanMessage;
''', '''use spt_net::net::wanmsg::WanMessage;
pub(crate) use spt_net::net::registry::node_qualifier;
pub use spt_net::net::siblings::{
    dormant_send_refusal, plan_handoff, sibling_rows, HandoffPlan, SendHead, SiblingRow,
};
''', 1)
# tests: drop the moved tests and their helpers, keep handoff_report
s, _ = cut(s, '    const THIRD: &str = "abab', '''            .expect("Q5: the restriction holds during a vacancy");
        assert!(why.contains("no instance of it is active"), "{why}");
        assert!(why.contains("`spt endpoint wake ling@here`"), "{why}");
    }

''')
s, _ = cut(s, '''    // [unit->REQ-INSTANCE-HANDOFF] the preconditions refuse BEFORE any byte
    // moves''', '''        refused(plan(Some("ling"), Some(Active), "ling@nowhere"), "no sibling of ling on 'nowhere'");
    }

''')
save(p, s, c)

# ---- registry.rs: node_qualifier (pub) + label_of after key_prefix
p = 'crates/spt-net/src/net/registry.rs'
s, c = load(p)
m = re.search(r'pub fn key_prefix\(node: &str\) -> String \{\n(.*?\n)\}\n', s, re.S)
assert m
nq = nq.replace('pub(crate) fn node_qualifier(', 'pub fn node_qualifier(')
s = s[:m.end()] + '\n' + nq.rstrip('\n') + '\n' + s[m.end():]
save(p, s, c)
print('refactor ok')
