#!/usr/bin/env python3
"""Tests for health-score.py - TDD approach (RED first, then GREEN)"""

import json
import sys
import tempfile
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

# Add scripts directory to path
sys.path.insert(0, str(Path(__file__).parent.parent))

import health_score as hs


class TestGradeCalculation:
    """Test grade boundaries"""

    def test_grade_A_at_90(self):
        assert hs.score_to_grade(90) == "A"

    def test_grade_A_at_100(self):
        assert hs.score_to_grade(100) == "A"

    def test_grade_B_at_89(self):
        assert hs.score_to_grade(89) == "B"

    def test_grade_B_at_80(self):
        assert hs.score_to_grade(80) == "B"

    def test_grade_C_at_79(self):
        assert hs.score_to_grade(79) == "C"

    def test_grade_C_at_70(self):
        assert hs.score_to_grade(70) == "C"

    def test_grade_D_at_69(self):
        assert hs.score_to_grade(69) == "D"

    def test_grade_D_at_60(self):
        assert hs.score_to_grade(60) == "D"

    def test_grade_F_at_59(self):
        assert hs.score_to_grade(59) == "F"

    def test_grade_F_at_0(self):
        assert hs.score_to_grade(0) == "F"


class TestWeightedScore:
    """Test weighted average calculation"""

    def test_weighted_score_all_100(self):
        categories = {
            "test_pass_rate": {"score": 100},
            "pyright_errors": {"score": 100},
            "code_coverage": {"score": 100},
            "tech_debt": {"score": 100},
            "security": {"score": 100},
            "documentation": {"score": 100},
            "deploy_stability": {"score": 100},
        }
        score = hs.calculate_weighted_score(categories)
        assert score == 100.0

    def test_weighted_score_all_zero(self):
        categories = {
            "test_pass_rate": {"score": 0},
            "pyright_errors": {"score": 0},
            "code_coverage": {"score": 0},
            "tech_debt": {"score": 0},
            "security": {"score": 0},
            "documentation": {"score": 0},
            "deploy_stability": {"score": 0},
        }
        score = hs.calculate_weighted_score(categories)
        assert score == 0.0

    def test_weighted_score_mixed(self):
        # test_pass_rate=100(20%), pyright_errors=0(15%), rest=50(75%)
        categories = {
            "test_pass_rate": {"score": 100},
            "pyright_errors": {"score": 0},
            "code_coverage": {"score": 50},
            "tech_debt": {"score": 50},
            "security": {"score": 50},
            "documentation": {"score": 50},
            "deploy_stability": {"score": 50},
        }
        score = hs.calculate_weighted_score(categories)
        # 100*0.20 + 0*0.15 + 50*0.10 + 50*0.15 + 50*0.15 + 50*0.10 + 50*0.15
        # = 20 + 0 + 5 + 7.5 + 7.5 + 5 + 7.5 = 52.5
        assert abs(score - 52.5) < 0.01


class TestOutputSchema:
    """Test JSON output schema"""

    def test_output_has_score(self):
        result = hs.build_result(
            categories={
                "test_pass_rate": {"score": 80, "details": ""},
                "pyright_errors": {"score": 80, "details": ""},
                "code_coverage": {"score": 80, "details": ""},
                "tech_debt": {"score": 80, "details": ""},
                "security": {"score": 80, "details": ""},
                "documentation": {"score": 80, "details": ""},
                "deploy_stability": {"score": 80, "details": ""},
            },
            baseline=None,
            fix_pct_result=None,
        )
        assert "score" in result
        assert "grade" in result
        assert "categories" in result

    def test_output_has_fix_pct_section(self):
        fix_pct = {"total": 10, "fix_count": 3, "pct": 30.0, "warning": False}
        result = hs.build_result(
            categories={
                "test_pass_rate": {"score": 80, "details": ""},
                "pyright_errors": {"score": 80, "details": ""},
                "code_coverage": {"score": 80, "details": ""},
                "tech_debt": {"score": 80, "details": ""},
                "security": {"score": 80, "details": ""},
                "documentation": {"score": 80, "details": ""},
                "deploy_stability": {"score": 80, "details": ""},
            },
            baseline=None,
            fix_pct_result=fix_pct,
        )
        assert "fix_pct" in result
        assert result["fix_pct"]["total"] == 10
        assert result["fix_pct"]["fix_count"] == 3

    def test_output_score_is_int(self):
        result = hs.build_result(
            categories={
                "test_pass_rate": {"score": 85, "details": ""},
                "pyright_errors": {"score": 85, "details": ""},
                "code_coverage": {"score": 85, "details": ""},
                "tech_debt": {"score": 85, "details": ""},
                "security": {"score": 85, "details": ""},
                "documentation": {"score": 85, "details": ""},
                "deploy_stability": {"score": 85, "details": ""},
            },
            baseline=None,
            fix_pct_result=None,
        )
        assert isinstance(result["score"], int)

    def test_output_is_json_serializable(self):
        result = hs.build_result(
            categories={
                "test_pass_rate": {"score": 80, "details": "ok"},
                "pyright_errors": {"score": 80, "details": "ok"},
                "code_coverage": {"score": 80, "details": "ok"},
                "tech_debt": {"score": 80, "details": "ok"},
                "security": {"score": 80, "details": "ok"},
                "documentation": {"score": 80, "details": "ok"},
                "deploy_stability": {"score": 80, "details": "ok"},
            },
            baseline=None,
            fix_pct_result=None,
        )
        # Should not raise
        json_str = json.dumps(result)
        parsed = json.loads(json_str)
        assert parsed["score"] == result["score"]


class TestBaselineComparison:
    """Test baseline comparison logic"""

    def test_baseline_comparison_improved(self):
        current_categories = {
            "test_pass_rate": {"score": 90, "details": ""},
            "pyright_errors": {"score": 80, "details": ""},
            "code_coverage": {"score": 70, "details": ""},
            "tech_debt": {"score": 80, "details": ""},
            "security": {"score": 80, "details": ""},
            "documentation": {"score": 80, "details": ""},
            "deploy_stability": {"score": 80, "details": ""},
        }
        baseline = {
            "categories": {
                "test_pass_rate": {"score": 80},
                "pyright_errors": {"score": 80},
                "code_coverage": {"score": 70},
                "tech_debt": {"score": 80},
                "security": {"score": 80},
                "documentation": {"score": 80},
                "deploy_stability": {"score": 80},
            }
        }
        result = hs.build_result(current_categories, baseline=baseline, fix_pct_result=None)
        cat = result["categories"]["test_pass_rate"]
        assert cat["delta"] == 10
        assert cat["direction"] == "improved"

    def test_baseline_comparison_degraded(self):
        current_categories = {
            "test_pass_rate": {"score": 70, "details": ""},
            "pyright_errors": {"score": 80, "details": ""},
            "code_coverage": {"score": 70, "details": ""},
            "tech_debt": {"score": 80, "details": ""},
            "security": {"score": 80, "details": ""},
            "documentation": {"score": 80, "details": ""},
            "deploy_stability": {"score": 80, "details": ""},
        }
        baseline = {
            "categories": {
                "test_pass_rate": {"score": 80},
                "pyright_errors": {"score": 80},
                "code_coverage": {"score": 70},
                "tech_debt": {"score": 80},
                "security": {"score": 80},
                "documentation": {"score": 80},
                "deploy_stability": {"score": 80},
            }
        }
        result = hs.build_result(current_categories, baseline=baseline, fix_pct_result=None)
        cat = result["categories"]["test_pass_rate"]
        assert cat["delta"] == -10
        assert cat["direction"] == "degraded"

    def test_baseline_comparison_stable(self):
        current_categories = {
            "test_pass_rate": {"score": 80, "details": ""},
            "pyright_errors": {"score": 80, "details": ""},
            "code_coverage": {"score": 70, "details": ""},
            "tech_debt": {"score": 80, "details": ""},
            "security": {"score": 80, "details": ""},
            "documentation": {"score": 80, "details": ""},
            "deploy_stability": {"score": 80, "details": ""},
        }
        baseline = {
            "categories": {
                "test_pass_rate": {"score": 80},
                "pyright_errors": {"score": 80},
                "code_coverage": {"score": 70},
                "tech_debt": {"score": 80},
                "security": {"score": 80},
                "documentation": {"score": 80},
                "deploy_stability": {"score": 80},
            }
        }
        result = hs.build_result(current_categories, baseline=baseline, fix_pct_result=None)
        cat = result["categories"]["test_pass_rate"]
        assert cat["delta"] == 0
        assert cat["direction"] == "stable"

    def test_baseline_file_missing_graceful(self):
        """When baseline file doesn't exist, no comparison, no error"""
        result = hs.build_result(
            categories={
                "test_pass_rate": {"score": 80, "details": ""},
                "pyright_errors": {"score": 80, "details": ""},
                "code_coverage": {"score": 80, "details": ""},
                "tech_debt": {"score": 80, "details": ""},
                "security": {"score": 80, "details": ""},
                "documentation": {"score": 80, "details": ""},
                "deploy_stability": {"score": 80, "details": ""},
            },
            baseline=None,
            fix_pct_result=None,
        )
        # No delta/direction keys when no baseline
        cat = result["categories"]["test_pass_rate"]
        assert "delta" not in cat
        assert "direction" not in cat

    def test_load_baseline_missing_file(self):
        """load_baseline returns None for missing file gracefully"""
        result = hs.load_baseline("/nonexistent/path/qa-baseline.json")
        assert result is None

    def test_load_baseline_valid_file(self):
        """load_baseline reads JSON from existing file"""
        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
            json.dump({"score": 85, "categories": {}}, f)
            f.flush()
            result = hs.load_baseline(f.name)
        assert result is not None
        assert result["score"] == 85


class TestFixPct:
    """Test fix percentage calculation"""

    def test_fix_pct_no_fix_tasks(self):
        tasks = {
            "task-1": {"task_id": "task-1", "description": "새 기능 추가", "status": "completed"},
            "task-2": {"task_id": "task-2", "description": "리팩토링", "status": "completed"},
        }
        result = hs.calculate_fix_pct(tasks)
        assert result["total"] == 2
        assert result["fix_count"] == 0
        assert result["pct"] == 0.0
        assert result["warning"] is False

    def test_fix_pct_all_fix_tasks(self):
        tasks = {
            "task-1": {"task_id": "task-1", "description": "버그 수정 작업", "status": "completed"},
            "task-2": {"task_id": "task-2", "description": "fix login error", "status": "completed"},
        }
        result = hs.calculate_fix_pct(tasks)
        assert result["total"] == 2
        assert result["fix_count"] == 2
        assert result["pct"] == 100.0
        assert result["warning"] is True

    def test_fix_pct_30pct_threshold_warning(self):
        """31% should trigger warning"""
        tasks = {}
        for i in range(69):
            tasks[f"task-{i}"] = {"task_id": f"task-{i}", "description": "일반 작업", "status": "completed"}
        for i in range(69, 100):
            tasks[f"task-{i}"] = {"task_id": f"task-{i}", "description": "fix something", "status": "completed"}
        result = hs.calculate_fix_pct(tasks)
        assert result["total"] == 100
        assert result["fix_count"] == 31
        assert result["pct"] == 31.0
        assert result["warning"] is True

    def test_fix_pct_30pct_exact_no_warning(self):
        """Exactly 30% should NOT trigger warning"""
        tasks = {}
        for i in range(70):
            tasks[f"task-{i}"] = {"task_id": f"task-{i}", "description": "일반 작업", "status": "completed"}
        for i in range(70, 100):
            tasks[f"task-{i}"] = {"task_id": f"task-{i}", "description": "fix something", "status": "completed"}
        result = hs.calculate_fix_pct(tasks)
        assert result["total"] == 100
        assert result["fix_count"] == 30
        assert result["pct"] == 30.0
        assert result["warning"] is False

    def test_fix_pct_keywords_fix(self):
        tasks = {"t1": {"task_id": "t1", "description": "fix the broken pipeline", "status": "completed"}}
        result = hs.calculate_fix_pct(tasks)
        assert result["fix_count"] == 1

    def test_fix_pct_keywords_수정(self):
        tasks = {"t1": {"task_id": "t1", "description": "코드 수정 필요", "status": "completed"}}
        result = hs.calculate_fix_pct(tasks)
        assert result["fix_count"] == 1

    def test_fix_pct_keywords_버그(self):
        tasks = {"t1": {"task_id": "t1", "description": "버그 발견 및 처리", "status": "completed"}}
        result = hs.calculate_fix_pct(tasks)
        assert result["fix_count"] == 1

    def test_fix_pct_empty_tasks(self):
        """Empty tasks dict should return pct=0 gracefully"""
        result = hs.calculate_fix_pct({})
        assert result["total"] == 0
        assert result["fix_count"] == 0
        assert result["pct"] == 0.0
        assert result["warning"] is False

    def test_load_task_timers_missing_file(self):
        """load_task_timers returns empty dict for missing file gracefully"""
        result = hs.load_task_timers("/nonexistent/task-timers.json")
        assert result == {}

    def test_load_task_timers_valid_file(self):
        """load_task_timers reads tasks from valid JSON file"""
        data = {
            "tasks": {
                "task-1": {"task_id": "task-1", "description": "test task", "status": "completed"},
            }
        }
        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
            json.dump(data, f)
            f.flush()
            result = hs.load_task_timers(f.name)
        assert "task-1" in result
        assert result["task-1"]["description"] == "test task"


class TestCollectorFunctions:
    """Test category collector functions"""

    def test_collect_returns_score_and_details(self):
        """Each collector must return dict with score and details"""
        with tempfile.TemporaryDirectory() as tmpdir:
            result = hs.collect_test_pass_rate(tmpdir)
            assert "score" in result
            assert "details" in result
            assert 0 <= result["score"] <= 100

    def test_collect_pyright_errors_returns_score_and_details(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            result = hs.collect_pyright_errors(tmpdir)
            assert "score" in result
            assert "details" in result
            assert 0 <= result["score"] <= 100

    def test_collect_code_coverage_returns_score_and_details(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            result = hs.collect_code_coverage(tmpdir)
            assert "score" in result
            assert "details" in result
            assert 0 <= result["score"] <= 100

    def test_collect_tech_debt_returns_score_and_details(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            result = hs.collect_tech_debt(tmpdir)
            assert "score" in result
            assert "details" in result
            assert 0 <= result["score"] <= 100

    def test_collect_security_returns_score_and_details(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            result = hs.collect_security(tmpdir)
            assert "score" in result
            assert "details" in result
            assert 0 <= result["score"] <= 100

    def test_collect_documentation_returns_score_and_details(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            result = hs.collect_documentation(tmpdir)
            assert "score" in result
            assert "details" in result
            assert 0 <= result["score"] <= 100

    def test_collect_deploy_stability_returns_score_and_details(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            result = hs.collect_deploy_stability(tmpdir)
            assert "score" in result
            assert "details" in result
            assert 0 <= result["score"] <= 100

    def test_collect_test_pass_rate_empty_dir(self):
        """Empty directory should return score=0 gracefully"""
        with tempfile.TemporaryDirectory() as tmpdir:
            result = hs.collect_test_pass_rate(tmpdir)
            assert result["score"] == 0
            assert result["details"] != ""

    def test_collect_all_categories(self):
        """collect_all_categories returns all 7 categories"""
        with tempfile.TemporaryDirectory() as tmpdir:
            categories = hs.collect_all_categories(tmpdir)
            expected_keys = {
                "test_pass_rate",
                "pyright_errors",
                "code_coverage",
                "tech_debt",
                "security",
                "documentation",
                "deploy_stability",
            }
            assert set(categories.keys()) == expected_keys
