from __future__ import annotations

from pathlib import Path

import pytest

from jarvis_finance.quality.git_safety import assert_safe, scan_path


def test_git_safety_blocks_sqlite_database(tmp_path: Path) -> None:
    (tmp_path / "finance.sqlite3").write_text("not real")
    findings = scan_path(tmp_path)
    assert any("forbidden_file_type" in f.reason for f in findings)


def test_git_safety_blocks_env_and_credentials(tmp_path: Path) -> None:
    (tmp_path / ".env").write_text("TOKEN=demo")
    (tmp_path / "client_secret.json").write_text("{}")
    findings = scan_path(tmp_path)
    assert len(findings) >= 2
    with pytest.raises(ValueError):
        assert_safe(tmp_path)


def test_git_safety_allows_synthetic_examples(tmp_path: Path) -> None:
    examples = tmp_path / "examples" / "synthetic"
    examples.mkdir(parents=True)
    (examples / "accounts.csv").write_text("platform_name\nDemo\n")
    assert scan_path(tmp_path) == []


def test_git_safety_allows_tests_fixtures_and_config_example_csv_json(tmp_path: Path) -> None:
    fixtures = tmp_path / "tests" / "fixtures"
    fixtures.mkdir(parents=True)
    (fixtures / "synthetic_accounts.csv").write_text("platform_name\nDemo\n")
    (fixtures / "synthetic_payload.json").write_text('{"kind":"synthetic"}')
    config_example = tmp_path / "config.example"
    config_example.mkdir()
    (config_example / "settings.json").write_text('{"token":"replace-me"}')
    assert scan_path(tmp_path) == []


def test_git_safety_allows_only_the_canonical_product_concept_docx(tmp_path: Path) -> None:
    canonical = (
        tmp_path
        / "docs"
        / "finance-manager-2.0"
        / "FinanceManager_Umsetzungs_und_Zielkonzept_v2.2_2026-08-01.docx"
    )
    canonical.parent.mkdir(parents=True)
    canonical.write_bytes(b"canonical concept placeholder")
    blocked = tmp_path / "docs" / "other.docx"
    blocked.write_bytes(b"must stay blocked")

    findings = scan_path(tmp_path)

    assert not any(f.path == canonical.relative_to(tmp_path).as_posix() for f in findings)
    assert any(f.path == "docs/other.docx" and f.reason == "forbidden_file_type:.docx" for f in findings)


def test_git_safety_does_not_false_positive_normal_source_files(tmp_path: Path) -> None:
    src = tmp_path / "src" / "jarvis_finance" / "imports"
    src.mkdir(parents=True)
    (src / "normal_module.py").write_text(
        "from __future__ import annotations\n\n"
        "def parse_token_name(token_name: str) -> str:\n"
        "    return token_name.strip().lower()\n"
    )
    assert scan_path(tmp_path) == []


def test_git_safety_blocks_runtime_dirs_db_json_and_token_names(tmp_path: Path) -> None:
    (tmp_path / "data").mkdir()
    (tmp_path / "data" / "portfolio.csv").write_text("real-ish")
    (tmp_path / "Github_token_secret.txt").write_text("placeholder")
    (tmp_path / "runtime").mkdir()
    (tmp_path / "runtime" / "cache.json").write_text("{}")
    findings = scan_path(tmp_path)
    reasons = [f.reason for f in findings]
    assert any("runtime_or_real_data_directory_blocked" in r for r in reasons)
    assert any("forbidden_secret_like_filename" in r for r in reasons)
    assert any("csv_json_only_allowed" in r for r in reasons)


def test_git_safety_blocks_github_pat_patterns(tmp_path: Path) -> None:
    (tmp_path / "notes.txt").write_text("token=github_pat_" + "A" * 32)
    findings = scan_path(tmp_path)
    assert any(f.reason == "secret_pattern:github_pat" for f in findings)


def test_git_safety_blocks_github_ghp_patterns(tmp_path: Path) -> None:
    (tmp_path / "notes.txt").write_text("token=ghp_" + "B" * 36)
    findings = scan_path(tmp_path)
    assert any(f.reason == "secret_pattern:github_ghp" for f in findings)


@pytest.mark.parametrize(
    ("label", "content"),
    [
        ("bearer_token", "Authorization: Bearer " + "A" * 32),
        ("openai_key", "OPENAI_API_KEY=sk-" + "B" * 32),
        ("slack_token", "SLACK_TOKEN=xoxb-" + "C" * 32),
    ],
)
def test_git_safety_blocks_additional_cleartext_secret_patterns(
    tmp_path: Path, label: str, content: str
) -> None:
    (tmp_path / "notes.txt").write_text(content)
    findings = scan_path(tmp_path)
    assert any(f.reason == f"secret_pattern:{label}" for f in findings)


def test_git_safety_blocks_private_keys(tmp_path: Path) -> None:
    (tmp_path / "id_rsa.txt").write_text(
        "-----BEGIN OPENSSH PRIVATE KEY-----\n"
        "synthetic-not-a-real-key\n"
        "-----END OPENSSH PRIVATE KEY-----\n"
    )
    findings = scan_path(tmp_path)
    assert any(f.reason == "secret_pattern:private_key" for f in findings)


def test_git_safety_blocks_csv_json_outside_allowed_paths(tmp_path: Path) -> None:
    (tmp_path / "portfolio.csv").write_text("account,balance\nDemo,1\n")
    (tmp_path / "export.json").write_text('{"realistic":"blocked"}')
    reasons = [f.reason for f in scan_path(tmp_path)]
    assert reasons.count("csv_json_only_allowed_in_examples_synthetic_or_tests_fixtures") == 2


def test_git_safety_blocks_top_level_runtime_and_data_dirs(tmp_path: Path) -> None:
    for dirname in ("data", "runtime", "exports", "reports"):
        folder = tmp_path / dirname
        folder.mkdir()
        (folder / "payload.txt").write_text("not allowed here")
    findings = scan_path(tmp_path)
    blocked_paths = {f.path for f in findings if f.reason == "runtime_or_real_data_directory_blocked"}
    assert {"data/payload.txt", "runtime/payload.txt", "exports/payload.txt", "reports/payload.txt"} <= blocked_paths


def test_git_safety_blocks_symlinks_without_reading_the_target(tmp_path: Path) -> None:
    outside = tmp_path.parent / "outside-sensitive.txt"
    outside.write_text("not inspected")
    (tmp_path / "linked.txt").symlink_to(outside)

    findings = scan_path(tmp_path)

    assert any(f.path == "linked.txt" and f.reason == "symlink_blocked" for f in findings)


def test_git_safety_blocks_reports_and_pdfs_in_repo(tmp_path: Path) -> None:
    report = tmp_path / "reports" / "crypto_inventory.pdf"
    report.parent.mkdir()
    report.write_bytes(b"%PDF-1.4 synthetic")

    findings = scan_path(tmp_path)

    assert any(
        str(f.path) == "reports/crypto_inventory.pdf"
        and f.reason == "runtime_or_real_data_directory_blocked"
        for f in findings
    )
    assert any(
        str(f.path) == "reports/crypto_inventory.pdf" and f.reason == "forbidden_file_type:.pdf"
        for f in findings
    )


def test_git_safety_blocks_backup_files_in_repo(tmp_path: Path) -> None:
    backup = tmp_path / "backups" / "finance-runtime-db-20260101T000000Z.sqlite3"
    backup.parent.mkdir()
    backup.write_bytes(b"sqlite backup placeholder")
    (backup.parent / "finance-runtime-db-20260101T000000Z.sqlite3.sha256").write_text("0" * 64)

    findings = scan_path(tmp_path)
    blocked_paths = {f.path for f in findings if f.reason == "runtime_or_real_data_directory_blocked"}
    assert "backups/finance-runtime-db-20260101T000000Z.sqlite3" in blocked_paths
    assert any(
        f.path == "backups/finance-runtime-db-20260101T000000Z.sqlite3"
        and f.reason == "forbidden_file_type:.sqlite3"
        for f in findings
    )


def test_git_safety_blocks_frontend_build_artifacts_and_env_files(tmp_path: Path) -> None:
    frontend = tmp_path / "frontend"
    (frontend / "dist").mkdir(parents=True)
    (frontend / "node_modules" / "pkg").mkdir(parents=True)
    (frontend / "dist" / "index.html").write_text("<div>built</div>")
    (frontend / "node_modules" / "pkg" / "index.js").write_text("export {}")
    (frontend / ".env.development").write_text("VITE_OPENFIGI_KEY=must-not-exist")
    (frontend / "package.json").write_text('{"private":true}')
    (frontend / "package-lock.json").write_text('{"lockfileVersion":3}')
    (frontend / "tsconfig.json").write_text('{"compilerOptions":{}}')

    findings = scan_path(tmp_path)
    reasons_by_path = {f.path: f.reason for f in findings}

    assert reasons_by_path["frontend/dist/index.html"] == "runtime_cache_file_blocked"
    assert reasons_by_path["frontend/node_modules/pkg/index.js"] == "runtime_cache_file_blocked"
    assert reasons_by_path["frontend/.env.development"] == "forbidden_secret_filename"
    assert "frontend/package.json" not in reasons_by_path
    assert "frontend/package-lock.json" not in reasons_by_path
    assert "frontend/tsconfig.json" not in reasons_by_path
