# Use the existing connected PyFluent variable solver.
import json
import math

task_directory = r"C:\Users\13977\AppData\Local\FluentAGI\data\runs\20261018-223401-e88a83"
with_directory = task_directory + r"\with_target"
without_directory = task_directory + r"\without_target"
end_time = 2.2
time_step = 0.005


def unpack(value):
    if isinstance(value, str):
        try:
            return json.loads(value)
        except (ValueError, TypeError):
            return value
    return value


def emit(label, value):
    value = unpack(value)
    print(label)
    print(json.dumps(value, ensure_ascii=False, default=str))
    return value


def walk(value):
    value = unpack(value)
    if isinstance(value, dict):
        yield value
        for child in value.values():
            yield from walk(child)
    elif isinstance(value, (list, tuple)):
        for child in value:
            yield from walk(child)


def verify(label, value):
    result = emit(label, value)
    found = False
    for node in walk(result):
        found = True
        if node.get("needs_review") is True:
            raise RuntimeError(label + ": needs_review")
        for key in ("status", "state"):
            status = node.get(key)
            if isinstance(status, str):
                if status.lower() in (
                    "needs_review",
                    "failed",
                    "error",
                    "aborted",
                    "timeout",
                    "timed_out",
                ):
                    raise RuntimeError(label + ": " + status)
        if node.get("success") is False or node.get("ok") is False:
            raise RuntimeError(label + ": unsuccessful")
        for key in ("error", "errors"):
            if node.get(key):
                raise RuntimeError(label + ": " + str(node[key]))
    if not found:
        raise RuntimeError(label + ": structured feedback unavailable")
    return result


def actual_time(value):
    for key in ("flow_time", "current_time", "final_time"):
        values = []
        for node in walk(value):
            item = node.get(key)
            if isinstance(item, bool):
                continue
            if isinstance(item, (int, float, str)):
                try:
                    number = float(item)
                except (TypeError, ValueError):
                    continue
                if math.isfinite(number) and number >= 0:
                    values.append(number)
        if values:
            return max(values)
    return None


cases = [
    ("with_target", with_directory, True),
    ("without_target", without_directory, False),
]

for index, (case_name, case_directory, include_target) in enumerate(cases):
    options = dict(
        directory=case_directory,
        include_target=include_target,
        body_diameter=0.06,
        target_diameter=0.04,
        speed=0.1,
        ramp_time=0.4,
        end_time=end_time,
        time_step=time_step,
        size=0.012,
    )

    if index > 0:
        backup_file = with_directory + r"\before_case_switch.cas.h5"
        backup_result = solver.settings.file.write_case_data(
            file_name=backup_file
        )
        emit("backup_before_case_switch", backup_result)
        options["replace_loaded_model"] = True

    verify(
        case_name + ": prepare",
        fluentagi.prepare_moving_circle(**options),
    )

    verify(
        case_name + ": initialize_once",
        fluentagi.initialize_moving_body(directory=case_directory),
    )

    completed = False

    for chunk in range(10):
        state = verify(
            case_name + ": read_before_chunk_" + str(chunk + 1),
            fluentagi.read_moving_body(directory=case_directory),
        )
        before = actual_time(state)

        if before is None:
            raise RuntimeError(
                case_name
                + ": actual flow time unavailable; inspect feedback before continuing"
            )

        if before >= end_time - 1e-9:
            completed = True
            break

        feedback = verify(
            case_name + ": advance_chunk_" + str(chunk + 1),
            fluentagi.advance_moving_body(
                directory=case_directory,
                steps=50,
                until_time=end_time,
                max_iter_per_step=100,
            ),
        )

        state = verify(
            case_name + ": read_after_chunk_" + str(chunk + 1),
            fluentagi.read_moving_body(directory=case_directory),
        )
        after = actual_time(state)

        if after is None:
            raise RuntimeError(
                case_name
                + ": actual flow time unavailable after advancement; do not replay"
            )

        print(
            json.dumps(
                {
                    "case": case_name,
                    "chunk": chunk + 1,
                    "time_before": before,
                    "time_after": after,
                    "end_time": end_time,
                },
                ensure_ascii=False,
            )
        )

        if after > end_time + 1e-8:
            raise RuntimeError(case_name + ": physical end time exceeded")

        if after <= before + 1e-12:
            raise RuntimeError(
                case_name + ": no confirmed time advancement; do not replay"
            )

        if after >= end_time - 1e-9:
            completed = True
            break

    if not completed:
        raise RuntimeError(
            case_name + ": target time not confirmed; retain current solution"
        )

    verify(
        case_name + ": final_state",
        fluentagi.read_moving_body(directory=case_directory),
    )

verify(
    "compare_three_moving_wall_pressure_patches",
    fluentagi.compare_moving_pressure(
        directory=task_directory,
        with_target_directory=with_directory,
        without_target_directory=without_directory,
    ),
)