"""Step-2 adjacent-tire compatibility experiment, extended to larger
shared cycle lengths k = 9 and k = 12.  Uses the numpy-optimized
fiber distribution from tire_fiber_chords_fast.py.

Same convention as tire_fiber_step2.py: for each (T1, T2) pair, we
compute T1's D-projection and T2's U-projection on the shared cycle γ
and report intersection sizes (forward + reverse).
"""
from __future__ import annotations

import time

from tire_fiber_chords import d_positions_for, u_positions_for
from tire_fiber_chords_fast import (
    fiber_distribution_fast,
    spoke_only_fiber_distribution_fast,
    projection_support_fast,
)


def project_T1_D(gamma_len: int, m_1: int, chords_1, model: str) -> set:
    if model == 'SR':
        n = m_1 + gamma_len
        fibers = spoke_only_fiber_distribution_fast(n)
    else:
        fibers, _, _ = fiber_distribution_fast(m_1, gamma_len, chords_1)
    d_pos = d_positions_for(m_1, gamma_len)
    return projection_support_fast(fibers, d_pos)


def project_T2_U(gamma_len: int, k_2: int, chords_2, model: str) -> set:
    if model == 'SR':
        n = gamma_len + k_2
        fibers = spoke_only_fiber_distribution_fast(n)
    else:
        fibers, _, _ = fiber_distribution_fast(gamma_len, k_2, chords_2)
    u_pos = u_positions_for(gamma_len, k_2)
    return projection_support_fast(fibers, u_pos)


def intersect_with_reflection(S1: set, S2: set) -> tuple[set, set]:
    forward = S1 & S2
    S2_rev = {s[::-1] for s in S2}
    reverse = S1 & S2_rev
    return forward, reverse


# Chord sets that make each k-cycle SP-feasible (all faces ≤ 3 B_in edges).
# --- Chord configurations validated to produce all-faces ≤ 3 B_in edges ---
# k=9: 3-chord matching giving faces of sizes (2,2,2,3).
K9_CHORDS_3 = [(0, 2), (3, 5), (6, 8)]
# k=9 alternate: a 3-chord nested config also giving (2,2,2,3).
K9_CHORDS_NESTED = [(0, 2), (4, 6), (3, 7)]

# k=12: 3-chord matching giving 4 faces of size 3 (symmetric "thirds").
K12_CHORDS_3 = [(0, 3), (4, 7), (8, 11)]
# k=12 nested: chord (0,3), then chord (4,11) for the bigger arc, then
# (5,7) and (8,10) inside (4,11).  Faces: {0,1,2}(3), {5,6}(2), {8,9}(2),
# {4,7,10}(3), {3,11}(2).
K12_CHORDS_NESTED = [(0, 3), (4, 11), (5, 7), (8, 10)]


def validate_chord_set(k: int, chords: list[tuple[int, int]]) -> tuple[bool, str]:
    """Check that no O-face has > 3 B_in edges (the SP feasibility
    constraint at 3 colors)."""
    from tire_fiber_chords import compute_faces_from_chords
    faces = compute_faces_from_chords(k, chords)
    sizes = sorted(len(f) for f in faces)
    ok = max(sizes) <= 3
    return ok, f"face sizes {sizes}"


# Curated pairs.  Format: (γ, T1_config, T2_config).
# T1_config = (m_1, chords_1, model1); T2_config = (k_2, chords_2, model2).
CASES_K9 = [
    # k = 9: both sides need at least 3 chords for SP feasibility.
    (9, (9, K9_CHORDS_3, 'SP'), (9, K9_CHORDS_3, 'SP')),
    (9, (9, K9_CHORDS_3, 'SP'), (9, K9_CHORDS_NESTED, 'SP')),
    (9, (9, K9_CHORDS_NESTED, 'SP'), (9, K9_CHORDS_NESTED, 'SP')),
    (9, (12, K9_CHORDS_3, 'SP'), (9, K9_CHORDS_3, 'SP')),
    (9, (9, K9_CHORDS_3, 'SP'), (12, K12_CHORDS_3, 'SP')),  # fixed: chord set matches k_2=12
    (9, (9, K9_CHORDS_3, 'SP'), (12, K12_CHORDS_NESTED, 'SP')),
    (9, (9, K9_CHORDS_3, 'SR'), (9, K9_CHORDS_3, 'SP')),
    (9, (9, K9_CHORDS_3, 'SP'), (3, [], 'SR')),
    (9, (9, K9_CHORDS_3, 'SP'), (4, [(0, 2)], 'SP')),
    (9, (9, K9_CHORDS_3, 'SP'), (6, [(0, 3)], 'SP')),
    (9, (9, K9_CHORDS_3, 'SP'), (6, [(0, 2), (3, 5)], 'SP')),
    (9, (9, K9_CHORDS_NESTED, 'SP'), (6, [(0, 3)], 'SP')),
    (9, (9, K9_CHORDS_NESTED, 'SP'), (12, K12_CHORDS_NESTED, 'SP')),
]

CASES_K12 = [
    (12, (12, K12_CHORDS_3, 'SP'), (12, K12_CHORDS_3, 'SP')),
    (12, (12, K12_CHORDS_NESTED, 'SP'), (12, K12_CHORDS_NESTED, 'SP')),
    (12, (12, K12_CHORDS_3, 'SP'), (12, K12_CHORDS_NESTED, 'SP')),
    (12, (12, K12_CHORDS_3, 'SP'), (3, [], 'SR')),
    (12, (12, K12_CHORDS_3, 'SP'), (4, [(0, 2)], 'SP')),
    (12, (12, K12_CHORDS_3, 'SP'), (6, [(0, 3)], 'SP')),
    (12, (12, K12_CHORDS_3, 'SP'), (6, [(0, 2), (3, 5)], 'SP')),
    (12, (12, K12_CHORDS_3, 'SP'), (9, K9_CHORDS_3, 'SP')),
    (12, (12, K12_CHORDS_NESTED, 'SP'), (9, K9_CHORDS_3, 'SP')),
    (12, (12, K12_CHORDS_3, 'SR'), (12, K12_CHORDS_3, 'SP')),
]


def fmt_cfg(m_or_k: int, chords, model: str) -> str:
    ch_str = str(chords) if chords else "—"
    if len(ch_str) > 24:
        ch_str = ch_str[:21] + "..."
    return f"({m_or_k}, {ch_str}, {model})"


def run_cases(label: str, cases: list, time_each: bool = False) -> int:
    print(f"\n### {label}\n")
    print(f"{'γ':>2}  {'T1 (m_1, chords_1, model)':<38s}  {'T2 (k_2, chords_2, model)':<38s}  "
          f"{'|S1|':>5s}  {'|S2|':>5s}  {'3^γ':>6s}  {'fwd':>5s}  {'rev':>5s}  {'compat?':>7s}"
          f"{'  time' if time_each else ''}")
    print("-" * (135 + (8 if time_each else 0)))
    nyes = ntotal = 0
    for gamma, t1, t2 in cases:
        m_1, ch1, model1 = t1
        k_2, ch2, model2 = t2
        t0 = time.time()
        S1 = project_T1_D(gamma, m_1, ch1, model1)
        S2 = project_T2_U(gamma, k_2, ch2, model2)
        dt = time.time() - t0
        forward, reverse = intersect_with_reflection(S1, S2)
        compat = "YES" if (forward or reverse) else "NO"
        ntotal += 1
        if compat == "YES":
            nyes += 1
        suffix = f"  {dt:5.1f}s" if time_each else ""
        print(
            f"{gamma:>2}  {fmt_cfg(m_1, ch1, model1):<38s}  {fmt_cfg(k_2, ch2, model2):<38s}  "
            f"{len(S1):>5d}  {len(S2):>5d}  {3**gamma:>6d}  "
            f"{len(forward):>5d}  {len(reverse):>5d}  {compat:>7s}{suffix}"
        )
    print(f"\n{nyes}/{ntotal} compatible at this k.")
    return nyes


def main():
    # Validate chord sets up front.
    print("Chord set validation:")
    for label, ch_set, k in [
        ("K9_CHORDS_3",      K9_CHORDS_3,      9),
        ("K9_CHORDS_NESTED", K9_CHORDS_NESTED, 9),
        ("K12_CHORDS_3",     K12_CHORDS_3,     12),
        ("K12_CHORDS_NESTED",K12_CHORDS_NESTED,12),
    ]:
        ok, info = validate_chord_set(k, ch_set)
        print(f"  {label} (k={k}): {info}  {'OK' if ok else 'FAIL'}")

    n_k9 = run_cases("Step 2 at k = 9", CASES_K9, time_each=True)
    n_k12 = run_cases("Step 2 at k = 12", CASES_K12, time_each=True)
    total = len(CASES_K9) + len(CASES_K12)
    print(f"\nTotal: {n_k9 + n_k12}/{total} compatible across k=9 and k=12.")


if __name__ == '__main__':
    main()
