"""Tests for Go Security Scanner."""

from __future__ import annotations

import tempfile
from pathlib import Path

import pytest

from mcpaudit.go_scanner import (
    GO_EXTENSIONS,
    GoSecurityScanner,
    detect_mcp_go_patterns,
    is_go_file,
)
from mcpaudit.models import Severity


# --- is_go_file ---

class TestIsGoFile:
    def test_go_file(self):
        assert is_go_file(Path("server.go")) is True

    def test_non_go_file_py(self):
        assert is_go_file(Path("server.py")) is False

    def test_non_go_file_ts(self):
        assert is_go_file(Path("server.ts")) is False

    def test_non_go_file_txt(self):
        assert is_go_file(Path("readme.txt")) is False

    def test_go_extension_case(self):
        # Go files are always .go lowercase
        assert is_go_file(Path("main.go")) is True


# --- detect_mcp_go_patterns ---

class TestDetectMCPGoPatterns:
    def test_mcp_server_pattern(self):
        source = 'server := mcp.NewServer()'
        patterns = detect_mcp_go_patterns(source)
        assert "mcp.NewServer" in patterns

    def test_mcp_tool_pattern(self):
        source = 'mcp.Tool{Name: "read_file"}'
        patterns = detect_mcp_go_patterns(source)
        assert "mcp.Tool" in patterns

    def test_no_mcp_patterns(self):
        source = 'fmt.Println("hello world")'
        patterns = detect_mcp_go_patterns(source)
        assert patterns == []

    def test_multiple_patterns(self):
        source = '''
import "mcp-go"
server := mcp.NewServer()
server.RegisterTool(tool)
'''
        patterns = detect_mcp_go_patterns(source)
        assert len(patterns) >= 2


# --- GoSecurityScanner ---

class TestGoSecurityScanner:
    def test_scanner_init_default(self):
        scanner = GoSecurityScanner()
        assert len(scanner.rules) == 8  # 8 Go rules

    def test_scanner_init_custom_rules(self):
        scanner = GoSecurityScanner(rules=[])
        assert len(scanner.rules) == 0

    def test_scan_clean_file(self, tmp_path):
        go_file = tmp_path / "clean.go"
        go_file.write_text('''package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}
''')
        scanner = GoSecurityScanner()
        result = scanner.scan_file(go_file)
        assert result.file == str(go_file)
        assert result.lines_scanned > 0

    def test_scan_file_with_findings(self, tmp_path):
        go_file = tmp_path / "vuln.go"
        go_file.write_text('''package main

import (
    "fmt"
    "os/exec"
)

func runCmd(input string) {
    exec.Command("sh", "-c", input)
}
''')
        scanner = GoSecurityScanner()
        result = scanner.scan_file(go_file)
        # Should find command injection
        cmd_findings = [f for f in result.findings if f.rule_id == "GO-CMD-001"]
        assert len(cmd_findings) > 0

    def test_scan_directory(self, tmp_path):
        go_file1 = tmp_path / "main.go"
        go_file1.write_text('package main\nfunc main() {}\n')
        go_file2 = tmp_path / "handler.go"
        go_file2.write_text('package main\nfunc handler() {}\n')

        scanner = GoSecurityScanner()
        results = scanner.scan_directory(tmp_path)
        assert len(results) == 2

    def test_scan_directory_skips_vendor(self, tmp_path):
        vendor_dir = tmp_path / "vendor" / "pkg"
        vendor_dir.mkdir(parents=True)
        vendor_file = vendor_dir / "lib.go"
        vendor_file.write_text('package pkg\n')
        main_file = tmp_path / "main.go"
        main_file.write_text('package main\n')

        scanner = GoSecurityScanner()
        results = scanner.scan_directory(tmp_path)
        assert len(results) == 1
        assert "vendor" not in results[0].file

    def test_scan_summary(self, tmp_path):
        go_file = tmp_path / "main.go"
        go_file.write_text('package main\nfunc main() {}\n')

        scanner = GoSecurityScanner()
        summary = scanner.scan(tmp_path)
        assert summary.total_files == 1
        assert summary.total_scan_time_ms >= 0

    def test_scan_single_file(self, tmp_path):
        go_file = tmp_path / "main.go"
        go_file.write_text('package main\nfunc main() {}\n')

        scanner = GoSecurityScanner()
        summary = scanner.scan(go_file)
        assert summary.total_files == 1

    def test_scan_nonexistent(self, tmp_path):
        scanner = GoSecurityScanner()
        with pytest.raises(FileNotFoundError):
            scanner.scan(tmp_path / "nonexistent.go")

    def test_deobfuscate_disabled(self, tmp_path):
        go_file = tmp_path / "main.go"
        go_file.write_text('package main\nfunc main() {}\n')

        scanner = GoSecurityScanner(deobfuscate_enabled=False)
        result = scanner.scan_file(go_file)
        assert result.deobfuscation_findings == []


# --- Integration with MCPSecurityScanner ---

class TestMCPScannerGoIntegration:
    def test_scanner_detects_go_files(self, tmp_path):
        from mcpaudit.scanner import MCPSecurityScanner, detect_language

        go_file = tmp_path / "server.go"
        go_file.write_text('package main\nfunc main() {}\n')

        assert detect_language(go_file) == "go"

    def test_main_scanner_scans_go(self, tmp_path):
        from mcpaudit.scanner import MCPSecurityScanner

        go_file = tmp_path / "server.go"
        go_file.write_text('''package main

import "os/exec"

func run(input string) {
    exec.Command("sh", "-c", input)
}
''')
        scanner = MCPSecurityScanner()
        result = scanner.scan_file(go_file)
        assert any(f.rule_id.startswith("GO-") for f in result.findings)

    def test_main_scanner_directory_includes_go(self, tmp_path):
        from mcpaudit.scanner import MCPSecurityScanner

        py_file = tmp_path / "server.py"
        py_file.write_text('import os\nprint("hi")\n')

        go_file = tmp_path / "handler.go"
        go_file.write_text('package main\nfunc handler() {}\n')

        scanner = MCPSecurityScanner()
        results = scanner.scan_directory(tmp_path)
        files = [r.file for r in results]
        assert any(f.endswith(".py") for f in files)
        assert any(f.endswith(".go") for f in files)
