"""Tests for the evaluation module."""

import os
import re
import tempfile
from unittest.mock import patch

from texer.eval import evaluate, _generate_header, _get_git_sha, _get_version
from texer import Table, Tabular, Row, PGFPlot, Axis, AddPlot, Coordinates


class TestHeader:
    def test_generate_header_with_git(self):
        """Test that header includes version, timestamp, and git SHA when available."""
        with patch("texer.eval._get_git_sha", return_value="abc1234def5678"):
            header = _generate_header()

        assert "% Generated by texer v" in header
        assert "% Git commit: abc1234def5678" in header
        # Check timestamp format (YYYY-MM-DD HH:MM:SS)
        assert re.search(r"\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}", header)

    def test_generate_header_without_git(self):
        """Test that header works when git is not available."""
        with patch("texer.eval._get_git_sha", return_value=None):
            header = _generate_header()

        assert "% Generated by texer v" in header
        assert "Git commit" not in header

    def test_generate_header_includes_version(self):
        """Test that header includes the texer version."""
        header = _generate_header()
        version = _get_version()
        assert f"v{version}" in header

    def test_get_git_sha_returns_full_sha(self):
        """Test that _get_git_sha returns the full SHA or None."""
        sha = _get_git_sha()
        # In a git repo, should return a full SHA (40 chars)
        # Outside a git repo, returns None
        if sha is not None:
            assert isinstance(sha, str)
            assert len(sha) == 40
            assert re.match(r"^[0-9a-f]+$", sha)

    def test_get_version_returns_string(self):
        """Test that _get_version returns a version string."""
        version = _get_version()
        assert isinstance(version, str)
        assert len(version) > 0

    def test_evaluate_without_header(self):
        """Test that evaluate with header=False returns plain content."""
        result = evaluate("hello", header=False)
        assert result == "hello"
        assert "Generated by texer" not in result

    def test_evaluate_default_includes_header(self):
        """Test that evaluate includes header by default."""
        result = evaluate("hello")
        assert "% Generated by texer v" in result
        assert result.endswith("hello")

    def test_evaluate_with_header(self):
        """Test that evaluate with header flag includes the header."""
        with patch("texer.eval._get_git_sha", return_value="abc1234def5678901234567890abcdef12345678"):
            result = evaluate("hello", header=True)

        assert "% Generated by texer v" in result
        assert "% Git commit: abc1234def5678901234567890abcdef12345678" in result
        assert result.endswith("hello")

    def test_header_is_latex_comment(self):
        """Test that header lines are valid LaTeX comments."""
        with patch("texer.eval._get_git_sha", return_value="abc1234def5678901234567890abcdef12345678"):
            header = _generate_header()

        for line in header.strip().split("\n"):
            assert line.startswith("%"), f"Line should be a LaTeX comment: {line}"


class TestEvaluateOutputFile:
    def test_save_table_with_preamble(self):
        """Test saving a table with preamble."""
        table = Table(
            Tabular(
                columns="lc",
                header=Row("Name", "Value"),
                rows=[Row("A", "1")],
            ),
            caption="Test Table",
        )

        with tempfile.TemporaryDirectory() as tmpdir:
            file_path = os.path.join(tmpdir, "test_table.tex")
            evaluate(table, output_file=file_path, with_preamble=True, header=False)

            with open(file_path, "r") as f:
                content = f.read()

            assert "\\documentclass{standalone}" in content
            assert "\\usepackage{booktabs}" in content
            assert "\\begin{document}" in content
            assert "\\begin{table}" in content
            assert "\\end{document}" in content

    def test_save_table_without_preamble(self):
        """Test saving a table without preamble."""
        table = Table(
            Tabular(
                columns="lc",
                header=Row("Name", "Value"),
                rows=[Row("A", "1")],
            ),
        )

        with tempfile.TemporaryDirectory() as tmpdir:
            file_path = os.path.join(tmpdir, "test_table.tex")
            evaluate(table, output_file=file_path, with_preamble=False, header=False)

            with open(file_path, "r") as f:
                content = f.read()

            assert "\\documentclass" not in content
            assert "\\begin{table}" in content

    def test_save_with_header(self):
        """Test saving with header comment."""
        table = Table(
            Tabular(columns="l", rows=[Row("test")]),
        )

        with tempfile.TemporaryDirectory() as tmpdir:
            file_path = os.path.join(tmpdir, "test.tex")
            evaluate(table, output_file=file_path, header=True)

            with open(file_path, "r") as f:
                content = f.read()

            assert "% Generated by texer" in content

    def test_save_without_header(self):
        """Test saving without header comment."""
        table = Table(
            Tabular(columns="l", rows=[Row("test")]),
        )

        with tempfile.TemporaryDirectory() as tmpdir:
            file_path = os.path.join(tmpdir, "test.tex")
            evaluate(table, output_file=file_path, header=False)

            with open(file_path, "r") as f:
                content = f.read()

            assert "% Generated by texer" not in content

    def test_save_plot_with_preamble(self):
        """Test saving a plot with preamble."""
        plot = PGFPlot(
            Axis(
                xlabel="X",
                ylabel="Y",
                plots=[AddPlot(coords=Coordinates([(0, 0), (1, 1)]))],
            )
        )

        with tempfile.TemporaryDirectory() as tmpdir:
            file_path = os.path.join(tmpdir, "test_plot.tex")
            evaluate(plot, output_file=file_path, with_preamble=True, header=False)

            with open(file_path, "r") as f:
                content = f.read()

            assert "\\documentclass{standalone}" in content
            assert "\\usepackage{pgfplots}" in content
            assert "\\begin{tikzpicture}" in content
            assert "\\end{document}" in content

    def test_save_tabular_with_preamble(self):
        """Test saving a Tabular (not Table) with preamble."""
        tabular = Tabular(
            columns="cc",
            rows=[Row("A", "B")],
        )

        with tempfile.TemporaryDirectory() as tmpdir:
            file_path = os.path.join(tmpdir, "test_tabular.tex")
            evaluate(tabular, output_file=file_path, with_preamble=True, header=False)

            with open(file_path, "r") as f:
                content = f.read()

            assert "\\documentclass{standalone}" in content
            assert "\\usepackage{booktabs}" in content
            assert "\\begin{tabular}" in content

    def test_save_with_data(self):
        """Test saving with data context."""
        from texer import Ref

        table = Table(
            Tabular(
                columns="l",
                rows=[Row(Ref("value"))],
            ),
        )

        with tempfile.TemporaryDirectory() as tmpdir:
            file_path = os.path.join(tmpdir, "test.tex")
            evaluate(table, data={"value": "Hello"}, output_file=file_path, header=False)

            with open(file_path, "r") as f:
                content = f.read()

            assert "Hello" in content

    def test_evaluate_returns_content_when_saving(self):
        """Test that evaluate returns the content even when saving to file."""
        table = Table(
            Tabular(columns="l", rows=[Row("test")]),
        )

        with tempfile.TemporaryDirectory() as tmpdir:
            file_path = os.path.join(tmpdir, "test.tex")
            result = evaluate(table, output_file=file_path, header=False)

            assert "\\begin{table}" in result

            with open(file_path, "r") as f:
                content = f.read()

            assert result == content


class TestCompile:
    def test_compile_no_pdflatex(self):
        """Test that compile=True raises error when pdflatex is not available."""
        table = Table(
            Tabular(columns="l", rows=[Row("test")]),
        )

        with tempfile.TemporaryDirectory() as tmpdir:
            file_path = os.path.join(tmpdir, "test.tex")
            with patch("shutil.which", return_value=None):
                try:
                    evaluate(table, output_file=file_path, compile=True)
                    assert False, "Should have raised RuntimeError"
                except RuntimeError as e:
                    assert "pdflatex not found" in str(e)

    def test_compile_requires_output_file(self):
        """Test that compile=True requires output_file."""
        table = Table(
            Tabular(columns="l", rows=[Row("test")]),
        )

        try:
            evaluate(table, compile=True)
            assert False, "Should have raised ValueError"
        except ValueError as e:
            assert "output_file is required" in str(e)

    def test_compile_auto_enables_preamble(self):
        """Test that compile=True automatically enables with_preamble."""
        table = Table(
            Tabular(columns="l", rows=[Row("test")]),
        )

        with tempfile.TemporaryDirectory() as tmpdir:
            file_path = os.path.join(tmpdir, "test.tex")
            # Mock pdflatex to avoid actual compilation
            with patch("shutil.which", return_value="/usr/bin/pdflatex"):
                with patch("subprocess.run") as mock_run:
                    mock_run.return_value.returncode = 0
                    try:
                        evaluate(table, output_file=file_path, compile=True, header=False)
                    except Exception:
                        pass  # May fail due to mock, that's ok

            # Check file was created with preamble
            with open(file_path, "r") as f:
                content = f.read()

            assert "\\documentclass{standalone}" in content
            assert "\\begin{document}" in content
