import json
import os
import subprocess
import sys


REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
sys.path.insert(0, REPO)

from tools.tradingview_update import (
    PineParseError,
    build_parser,
    copy_to_clipboard,
    latest_file,
    parse_pine,
    run_preflight,
    slugify,
    verify_text_content,
    write_source_copy,
)


def test_parse_strategy_title_named_arg():
    info = parse_pine(os.path.join(REPO, "strategies/strategy_mlp_scores.pine"))

    assert info.kind == "strategy"
    assert info.title == "MLPScores"
    assert info.version == 6
    assert info.line_count > 100
    assert len(info.sha256) == 64


def test_parse_indicator_title_positional_arg():
    info = parse_pine(os.path.join(REPO, "indicators/IndicatorActivationScores.pine"))

    assert info.kind == "indicator"
    assert info.title == "Indicator: Activation Scores"
    assert info.version == 6


def test_parse_multiline_named_title(tmp_path):
    pine = tmp_path / "custom.pine"
    pine.write_text(
        """//@version=6
strategy(
    title="My Multi Line Strategy",
    overlay=false
)
plot(close)
""",
        encoding="utf-8",
    )

    info = parse_pine(pine)

    assert info.kind == "strategy"
    assert info.title == "My Multi Line Strategy"


def test_parse_requires_strategy_or_indicator(tmp_path):
    pine = tmp_path / "library.pine"
    pine.write_text("//@version=6\nlibrary(\"Nope\")\n", encoding="utf-8")

    try:
        parse_pine(pine)
    except PineParseError as exc:
        assert "strategy" in str(exc)
    else:
        raise AssertionError("Expected PineParseError")


def test_inspect_command_outputs_json():
    from tools.tradingview_update import main
    from io import StringIO

    old_stdout = sys.stdout
    sys.stdout = StringIO()
    try:
        rc = main(["inspect", "strategies/strategy_mlp_scores.pine"])
        output = sys.stdout.getvalue()
    finally:
        sys.stdout = old_stdout

    payload = json.loads(output)
    assert rc == 0
    assert payload["title"] == "MLPScores"


def test_preflight_runs_duplicate_check_for_any_pine():
    checks = run_preflight(os.path.join(REPO, "strategies/strategy_mlp_scores.pine"))

    assert len(checks) == 1
    assert checks[0].command[-2:] == ["tools/check_pine_duplicates.py", "strategies/strategy_mlp_scores.pine"]


def test_copy_to_clipboard_returns_false_when_pbcopy_fails(monkeypatch):
    monkeypatch.setattr("tools.tradingview_update.shutil.which", lambda name: "/usr/bin/pbcopy")

    def fail(*args, **kwargs):
        raise subprocess.CalledProcessError(1, ["pbcopy"])

    monkeypatch.setattr("tools.tradingview_update.subprocess.run", fail)

    assert copy_to_clipboard("code") is False


def test_slugify_produces_filesystem_label():
    assert slugify("https://www.tradingview.com/chart/?symbol=BTCUSD") == "https_www.tradingview.com_chart_symbol_BTCUSD"
    assert slugify("   ") == "capture"


def test_capture_parser_defaults_to_tradingview_chart():
    parser = build_parser()
    args = parser.parse_args(["capture"])

    assert args.url == "https://www.tradingview.com/chart/"
    assert args.headed is False
    assert args.save_text is False


def test_write_source_copy_creates_timestamped_and_latest_files(tmp_path):
    info = parse_pine(os.path.join(REPO, "indicators/IndicatorActivationScores.pine"))
    source_path = write_source_copy(tmp_path, info, "// pine source\n")

    assert source_path.exists()
    assert source_path.read_text(encoding="utf-8") == "// pine source\n"
    latest = tmp_path / "source" / "latest_Indicator_Activation_Scores.pine"
    assert latest.exists()
    assert latest.read_text(encoding="utf-8") == "// pine source\n"


def test_verify_text_content_requires_expected_and_rejects_errors():
    result = verify_text_content(
        "Pine Editor\nMLPScores\nNo errors",
        expected=["MLPScores"],
        rejected=["Compilation error", "Undeclared identifier"],
    )

    assert result["ok"] is True
    assert result["missing_expected"] == []
    assert result["found_rejected"] == []


def test_verify_text_content_reports_missing_and_rejected():
    result = verify_text_content(
        "Pine Editor\nCompilation error\n",
        expected=["MLPScores"],
        rejected=["Compilation error"],
    )

    assert result["ok"] is False
    assert result["missing_expected"] == ["MLPScores"]
    assert result["found_rejected"] == ["Compilation error"]


def test_verify_text_parser_collects_repeated_expectations():
    parser = build_parser()
    args = parser.parse_args(["verify-text", "--text", "screen.txt", "--expect", "MLPScores", "--expect", "Pine Editor"])

    assert args.text == "screen.txt"
    assert args.expect == ["MLPScores", "Pine Editor"]


def test_manual_update_parser_defaults_to_script_title_verification_later():
    parser = build_parser()
    args = parser.parse_args(["manual-update", "strategies/strategy_mlp_scores.pine"])

    assert args.pine == "strategies/strategy_mlp_scores.pine"
    assert args.url == "https://www.tradingview.com/chart/"
    assert args.expect == []
    assert args.reject == []
    assert args.wait_ms == 1000


def test_doctor_parser_defaults_to_static_checks():
    parser = build_parser()
    args = parser.parse_args(["doctor"])

    assert args.pine == "strategies/strategy_mlp_scores.pine"
    assert args.browser is False
    assert args.tradingview is False
    assert args.wait_ms == 3000


def test_latest_file_returns_newest_path(tmp_path):
    old = tmp_path / "old.json"
    new = tmp_path / "new.json"
    old.write_text("{}", encoding="utf-8")
    new.write_text("{}", encoding="utf-8")
    os.utime(old, (1, 1))
    os.utime(new, (2, 2))

    assert latest_file([old, new]) == new
    assert latest_file([tmp_path / "missing.json"]) is None


def test_status_parser_defaults_to_artifact_dir():
    parser = build_parser()
    args = parser.parse_args(["status"])

    assert args.out_dir == ".tradingview_e2e"


def test_profile_status_parser_defaults_to_persistent_headless_check():
    parser = build_parser()
    args = parser.parse_args(["profile-status"])

    assert args.url == "https://www.tradingview.com/chart/"
    assert args.headed is False
    assert args.wait_for_user is False
    assert args.wait_ms == 3000
