"""
検証関数の定義
"""

import sys
from pathlib import Path

# プロジェクトのルートディレクトリをPythonパスに追加
project_root = Path(__file__).resolve().parent.parent
if str(project_root) not in sys.path:
    sys.path.insert(0, str(project_root))

import numpy as np
import networkx as nx
from typing import Dict, List
from genice3.genice import GenIce3
from genice3.molecule import Molecule


def validate_basic_execution(genice: GenIce3, exporter, output: str) -> bool:
    """
    レベル1: 基本的な動作確認

    Args:
        genice: GenIce3インスタンス
        exporter: Exporterインスタンス
        output: 出力文字列

    Returns:
        bool: 検証が成功した場合True
    """
    # 出力が生成される
    assert output is not None, "Output should not be None"
    assert len(output) > 0, "Output should not be empty"

    return True


def validate_output_format(exporter_name: str, output: str) -> bool:
    """
    レベル2: 出力形式の検証

    Args:
        exporter_name: exporter名
        output: 出力文字列

    Returns:
        bool: 検証が成功した場合True
    """
    if exporter_name == "gromacs":
        lines = output.split("\n")
        # ヘッダー行が存在
        assert (
            "Generated by GenIce" in output
        ), "Gromacs header should contain 'Generated by GenIce'"
        # 最低限の行数がある
        assert (
            len(lines) >= 3
        ), f"Gromacs output should have at least 3 lines, got {len(lines)}"
        # 原子数行が存在し、整数としてパースできる
        try:
            atom_count = int(lines[1].strip())
            assert atom_count > 0, f"Atom count should be positive, got {atom_count}"
        except (ValueError, IndexError) as e:
            raise AssertionError(f"Failed to parse atom count from line 2: {e}")

    elif exporter_name == "cif":
        # CIF形式の必須キーワード
        assert "data_genice" in output, "CIF output should contain 'data_genice'"
        assert "_cell_length_a" in output, "CIF output should contain '_cell_length_a'"
        assert "loop_" in output, "CIF output should contain 'loop_'"
        assert (
            "_atom_site_label" in output
        ), "CIF output should contain '_atom_site_label'"

    return True


def validate_ice_rules(genice: GenIce3) -> bool:
    """
    レベル3: アイスルールの検証

    各水分子から2本の水素結合が出て、2本が入ることを確認

    Args:
        genice: GenIce3インスタンス

    Returns:
        bool: 検証が成功した場合True
    """
    digraph = genice.digraph

    # 各ノード（水分子）から2本の水素結合が出る
    for node in digraph.nodes():
        out_degree = digraph.out_degree(node)
        in_degree = digraph.in_degree(node)

        assert (
            out_degree == 2
        ), f"Node {node} has {out_degree} outgoing edges, expected 2"
        assert in_degree == 2, f"Node {node} has {in_degree} incoming edges, expected 2"

    return True


def validate_atom_count(genice: GenIce3, water_model: Molecule) -> bool:
    """
    レベル3: 原子数の検証

    Args:
        genice: GenIce3インスタンス
        water_model: 水分子モデル

    Returns:
        bool: 検証が成功した場合True
    """
    waters = genice.water_molecules(water_model=water_model)

    # 水分子数 × 分子あたりの原子数
    expected_atoms = len(waters) * len(water_model.sites)

    # 実際の原子数をカウント
    total_atoms = sum(len(mol.sites) for mol in waters.values())

    assert (
        total_atoms == expected_atoms
    ), f"Expected {expected_atoms} atoms, got {total_atoms}"

    return True


def validate_coordinates_in_cell(genice: GenIce3, water_model: Molecule) -> bool:
    """
    レベル3: 座標がセル内に収まっていることの検証

    Args:
        genice: GenIce3インスタンス
        water_model: 水分子モデル

    Returns:
        bool: 検証が成功した場合True
    """
    cell = genice.cell
    cell_inv = np.linalg.inv(cell)

    waters = genice.water_molecules(water_model=water_model)

    for mol in waters.values():
        for site in mol.sites:
            # 絶対座標を相対座標に変換
            rel_pos = site @ cell_inv
            # 0-1の範囲に正規化
            rel_pos = rel_pos - np.floor(rel_pos)

            # すべての成分が0-1の範囲内（少し余裕を持たせる）
            assert np.all(rel_pos >= -1e-10) and np.all(
                rel_pos <= 1 + 1e-10
            ), f"Coordinate {site} is outside cell (relative: {rel_pos})"

    return True


def validate_graph_structure(genice: GenIce3) -> bool:
    """
    レベル5: グラフ構造の検証

    Args:
        genice: GenIce3インスタンス

    Returns:
        bool: 検証が成功した場合True
    """
    graph = genice.graph
    digraph = genice.digraph

    # 無向グラフと有向グラフのノード数が一致
    assert (
        graph.number_of_nodes() == digraph.number_of_nodes()
    ), f"Graph node count mismatch: {graph.number_of_nodes()} vs {digraph.number_of_nodes()}"

    # 有向グラフが生成されている
    assert digraph.number_of_edges() > 0, "Digraph should have edges"

    # グラフが連結している（無向グラフとして）
    if isinstance(graph, nx.Graph):
        assert nx.is_connected(graph), "Graph should be connected"
    elif isinstance(graph, nx.DiGraph):
        # 有向グラフの場合は無向グラフに変換して確認
        undirected = graph.to_undirected()
        assert nx.is_connected(undirected), "Graph should be connected"

    return True


def validate_reproducibility(unitcell_name: str, seed: int = 1) -> bool:
    """
    レベル4: 再現性の検証

    同じseedで同じ結果が得られることを確認

    Args:
        unitcell_name: 単位胞名
        seed: 乱数シード

    Returns:
        bool: 検証が成功した場合True
    """
    from genice3.plugin import UnitCell, Exporter
    from io import StringIO

    # 1回目の実行
    genice1 = GenIce3(seed=seed)
    genice1.unitcell = UnitCell(unitcell_name)
    output_file1 = StringIO()
    Exporter("gromacs").dump(genice1, output_file1, water_model="4site")
    output1 = output_file1.getvalue()

    # 2回目の実行
    run2 = GenIce3(seed=seed)
    run2.unitcell = UnitCell(unitcell_name)
    output_file2 = StringIO()
    Exporter("gromacs").dump(run2, output_file2, water_model="4site")
    output2 = output_file2.getvalue()

    # 出力が完全に一致
    assert output1 == output2, "Results should be identical with same seed"

    return True


def validate_comprehensive(
    genice: GenIce3, exporter_name: str, output: str, water_model: Molecule
) -> bool:
    """
    総合的な検証

    Args:
        genice: GenIce3インスタンス
        exporter_name: exporter名
        output: 出力文字列
        water_model: 水分子モデル

    Returns:
        bool: すべての検証が成功した場合True
    """
    # レベル1: 基本動作
    validate_basic_execution(genice, None, output)

    # レベル2: 出力形式
    validate_output_format(exporter_name, output)

    # レベル3: 物理的整合性
    validate_ice_rules(genice)
    validate_atom_count(genice, water_model)
    validate_coordinates_in_cell(genice, water_model)

    # レベル5: 構造的整合性
    validate_graph_structure(genice)

    return True
