"""Oscillation Loop — passive EMF tail.

Tails ghojualamanchu-v3/data/medulla.log, and on every new breath line
emits the four wemeanyounoharm EMF loop files into v3/data/signals/.

The flux codec calls aggregate_emf() against v3, so the output is a
deterministic function of the live v3 beat (not just the log line).

Stops cleanly on SIGINT / SIGTERM.
"""

from __future__ import annotations

import json
import os
import re
import signal
import sys
import time
from pathlib import Path

V3_BASE = Path("/home/workspace/ghojualamanchu-v3")
V1_BASE = Path("/home/workspace/ghojualamanchu")
LOG_FILE = V3_BASE / "data" / "medulla.log"
STATE_FILE = V3_BASE / "data" / "state.json"
SIGNAL_LOOP_DIR = V3_BASE / "data" / "signals"
PID_FILE = V1_BASE / "data" / "oscillation_loop.pid"

sys.path.insert(0, str(V1_BASE))
from integrations.oscillation.flux_codec import (  # noqa: E402
    build_resonance_report,
    derive_oscillation_target,
    diff_target_vs_telemetry,
    write_all,
    Telemetry,
)

BREATH_RE = re.compile(r"Breath:\s+(INHALE|EXHALE|HOLD)\s+\(beat\s+(\d+)\)")
TRITS = {"INHALE": 0, "EXHALE": 1, "HOLD": 2}

_running = True


def _stop(*_a):
    global _running
    _running = False


def _read_state_beat() -> int:
    if not STATE_FILE.exists():
        return 0
    try:
        return int(json.loads(STATE_FILE.read_text()).get("current_beat", 0))
    except Exception:
        return 0


def _write_pid():
    PID_FILE.parent.mkdir(parents=True, exist_ok=True)
    PID_FILE.write_text(str(os.getpid()))


def _clear_pid():
    if PID_FILE.exists():
        PID_FILE.unlink()


def main():
    signal.signal(signal.SIGINT, _stop)
    signal.signal(signal.SIGTERM, _stop)
    _write_pid()
    print(f"[oscillation_loop] pid={os.getpid()} watching {LOG_FILE}", flush=True)

    last_offset = 0
    if LOG_FILE.exists():
        last_offset = LOG_FILE.stat().st_size

    try:
        while _running:
            if not LOG_FILE.exists():
                time.sleep(0.5)
                continue

            size = LOG_FILE.stat().st_size
            if size < last_offset:
                # rotated/truncated — resync
                last_offset = 0
            if size > last_offset:
                with LOG_FILE.open("r") as f:
                    f.seek(last_offset)
                    new_data = f.read()
                last_offset = size

                for phase, beat_str in BREATH_RE.findall(new_data):
                    beat = int(beat_str)
                    rr = build_resonance_report(beat)
                    ot = derive_oscillation_target(beat)
                    tlm = Telemetry(
                        beat=beat,
                        timestamp=rr.timestamp,
                        agent_id="oscillation-loop",
                        action_taken=ot.target_prescription,
                        self_trit=ot.trit,
                        notes=f"breath={phase}",
                    )
                    fb = diff_target_vs_telemetry(ot, tlm)
                    write_all(rr, ot, tlm, fb)
                    print(
                        f"[oscillation_loop] beat={beat} breath={phase} "
                        f"field={rr.field_state} -> {ot.target_prescription} "
                        f"trit={ot.trit} mag={ot.magnitude:.2f} "
                        f"align={fb.alignment_score:.2f}",
                        flush=True,
                    )

            time.sleep(0.25)
    finally:
        _clear_pid()
        print("[oscillation_loop] stopped", flush=True)


if __name__ == "__main__":
    main()
