#!/usr/bin/env python3
"""utils/insights_engine.py 테스트 스위트"""

import json
import sys
import tempfile
from datetime import datetime, timedelta
from pathlib import Path

import pytest

sys.path.insert(0, str(Path(__file__).parent.parent.parent))

from utils.insights_engine import InsightsEngine, InsightsSummary

TASK_TIMERS_PATH = Path("/home/jay/workspace/memory/task-timers.json")


def make_task_timers(tasks: list[dict]) -> str:
    """테스트용 task-timers.json 내용 생성"""
    return json.dumps({"tasks": {t["task_id"]: t for t in tasks}})


def _make_task(
    task_id: str,
    team_id: str = "dev1-team",
    status: str = "completed",
    duration_seconds: float = 300.0,
    days_ago: int = 1,
) -> dict:
    end_dt = datetime.now() - timedelta(days=days_ago)
    start_dt = end_dt - timedelta(seconds=duration_seconds)
    return {
        "task_id": task_id,
        "team_id": team_id,
        "description": f"{task_id} 설명",
        "start_time": start_dt.isoformat(),
        "end_time": end_dt.isoformat(),
        "duration_seconds": duration_seconds,
        "status": status,
        "duration_human": f"{int(duration_seconds//60)}분",
    }


class TestInsightsSummary:
    """InsightsSummary dataclass 구조 테스트"""

    def test_fields_exist(self) -> None:
        """필수 필드 존재 확인"""
        s = InsightsSummary(
            total_tasks=10,
            completed_tasks=8,
            avg_duration_minutes=5.0,
            total_estimated_cost=0.05,
            tasks_by_team={"dev1-team": 5},
            tasks_by_status={"completed": 8, "running": 2},
        )
        assert s.total_tasks == 10
        assert s.completed_tasks == 8
        assert s.avg_duration_minutes == 5.0
        assert s.total_estimated_cost == 0.05
        assert s.tasks_by_team == {"dev1-team": 5}
        assert s.tasks_by_status == {"completed": 8, "running": 2}


class TestInsightsEngineInit:
    """InsightsEngine 초기화 테스트"""

    def test_init_with_valid_path(self, tmp_path: Path) -> None:
        """유효한 경로로 초기화"""
        data = make_task_timers([_make_task("t1")])
        p = tmp_path / "task-timers.json"
        p.write_text(data)
        engine = InsightsEngine(p)
        assert engine is not None

    def test_init_with_string_path(self, tmp_path: Path) -> None:
        """문자열 경로로 초기화"""
        data = make_task_timers([_make_task("t1")])
        p = tmp_path / "task-timers.json"
        p.write_text(data)
        engine = InsightsEngine(str(p))
        assert engine is not None

    def test_init_nonexistent_path_ok(self) -> None:
        """존재하지 않는 경로도 초기화는 성공 (로드 시 빈 결과)"""
        engine = InsightsEngine("/nonexistent/task-timers.json")
        assert engine is not None


class TestGetSummary:
    """get_summary() 테스트"""

    def test_returns_insights_summary(self, tmp_path: Path) -> None:
        """InsightsSummary 반환"""
        tasks = [_make_task("t1"), _make_task("t2")]
        p = tmp_path / "task-timers.json"
        p.write_text(make_task_timers(tasks))
        engine = InsightsEngine(p)
        result = engine.get_summary(days=30)
        assert isinstance(result, InsightsSummary)

    def test_total_tasks_counts_all(self, tmp_path: Path) -> None:
        """total_tasks는 기간 내 전체 태스크 수"""
        tasks = [_make_task(f"t{i}", days_ago=1) for i in range(5)]
        p = tmp_path / "task-timers.json"
        p.write_text(make_task_timers(tasks))
        engine = InsightsEngine(p)
        result = engine.get_summary(days=30)
        assert result.total_tasks == 5

    def test_completed_tasks_filters_status(self, tmp_path: Path) -> None:
        """completed_tasks는 status=completed만 카운트"""
        tasks = [
            _make_task("t1", status="completed"),
            _make_task("t2", status="completed"),
            _make_task("t3", status="running"),
        ]
        p = tmp_path / "task-timers.json"
        p.write_text(make_task_timers(tasks))
        engine = InsightsEngine(p)
        result = engine.get_summary(days=30)
        assert result.completed_tasks == 2

    def test_avg_duration_minutes_calculation(self, tmp_path: Path) -> None:
        """평균 소요시간 계산 (분 단위)"""
        tasks = [
            _make_task("t1", duration_seconds=120.0),  # 2분
            _make_task("t2", duration_seconds=180.0),  # 3분
        ]
        p = tmp_path / "task-timers.json"
        p.write_text(make_task_timers(tasks))
        engine = InsightsEngine(p)
        result = engine.get_summary(days=30)
        # 평균 = (2+3)/2 = 2.5분
        assert abs(result.avg_duration_minutes - 2.5) < 0.1

    def test_tasks_by_team_grouped(self, tmp_path: Path) -> None:
        """팀별 태스크 집계"""
        tasks = [
            _make_task("t1", team_id="dev1-team"),
            _make_task("t2", team_id="dev1-team"),
            _make_task("t3", team_id="dev2-team"),
        ]
        p = tmp_path / "task-timers.json"
        p.write_text(make_task_timers(tasks))
        engine = InsightsEngine(p)
        result = engine.get_summary(days=30)
        assert result.tasks_by_team.get("dev1-team") == 2
        assert result.tasks_by_team.get("dev2-team") == 1

    def test_tasks_by_status_grouped(self, tmp_path: Path) -> None:
        """상태별 태스크 집계"""
        tasks = [
            _make_task("t1", status="completed"),
            _make_task("t2", status="completed"),
            _make_task("t3", status="running"),
        ]
        p = tmp_path / "task-timers.json"
        p.write_text(make_task_timers(tasks))
        engine = InsightsEngine(p)
        result = engine.get_summary(days=30)
        assert result.tasks_by_status.get("completed") == 2
        assert result.tasks_by_status.get("running") == 1

    def test_days_filter_excludes_old_tasks(self, tmp_path: Path) -> None:
        """days 필터: 기간 밖 태스크 제외"""
        tasks = [
            _make_task("recent", days_ago=5),
            _make_task("old", days_ago=60),
        ]
        p = tmp_path / "task-timers.json"
        p.write_text(make_task_timers(tasks))
        engine = InsightsEngine(p)
        result = engine.get_summary(days=30)
        assert result.total_tasks == 1

    def test_empty_data_returns_zero_summary(self, tmp_path: Path) -> None:
        """데이터 없으면 0값 Summary"""
        p = tmp_path / "task-timers.json"
        p.write_text(json.dumps({"tasks": {}}))
        engine = InsightsEngine(p)
        result = engine.get_summary(days=30)
        assert result.total_tasks == 0
        assert result.completed_tasks == 0

    def test_nonexistent_file_returns_zero_summary(self) -> None:
        """파일 없으면 0값 Summary"""
        engine = InsightsEngine("/nonexistent/task-timers.json")
        result = engine.get_summary(days=30)
        assert result.total_tasks == 0

    def test_total_estimated_cost_nonnegative(self, tmp_path: Path) -> None:
        """비용 추정값은 음수가 아님"""
        tasks = [_make_task(f"t{i}") for i in range(3)]
        p = tmp_path / "task-timers.json"
        p.write_text(make_task_timers(tasks))
        engine = InsightsEngine(p)
        result = engine.get_summary(days=30)
        assert result.total_estimated_cost >= 0.0

    def test_real_task_timers(self) -> None:
        """실제 task-timers.json 로드"""
        if not TASK_TIMERS_PATH.exists():
            pytest.skip("task-timers.json 없음")
        engine = InsightsEngine(TASK_TIMERS_PATH)
        result = engine.get_summary(days=365)
        assert result.total_tasks > 0
        assert isinstance(result.tasks_by_team, dict)


class TestRenderAsciiChart:
    """render_ascii_chart() 테스트"""

    def test_returns_string(self, tmp_path: Path) -> None:
        """str 반환"""
        p = tmp_path / "t.json"
        p.write_text(json.dumps({"tasks": {}}))
        engine = InsightsEngine(p)
        result = engine.render_ascii_chart({"a": 3, "b": 1})
        assert isinstance(result, str)

    def test_contains_keys(self, tmp_path: Path) -> None:
        """레이블이 차트에 포함됨"""
        p = tmp_path / "t.json"
        p.write_text(json.dumps({"tasks": {}}))
        engine = InsightsEngine(p)
        result = engine.render_ascii_chart({"dev1-team": 10, "dev2-team": 5})
        assert "dev1-team" in result
        assert "dev2-team" in result

    def test_contains_bar_chars(self, tmp_path: Path) -> None:
        """막대 문자(█) 포함"""
        p = tmp_path / "t.json"
        p.write_text(json.dumps({"tasks": {}}))
        engine = InsightsEngine(p)
        result = engine.render_ascii_chart({"a": 10})
        assert "█" in result

    def test_with_title(self, tmp_path: Path) -> None:
        """title 인자 반영"""
        p = tmp_path / "t.json"
        p.write_text(json.dumps({"tasks": {}}))
        engine = InsightsEngine(p)
        result = engine.render_ascii_chart({"x": 1}, title="팀별 태스크")
        assert "팀별 태스크" in result

    def test_empty_data(self, tmp_path: Path) -> None:
        """빈 데이터는 에러 없이 빈 문자열 또는 메시지 반환"""
        p = tmp_path / "t.json"
        p.write_text(json.dumps({"tasks": {}}))
        engine = InsightsEngine(p)
        result = engine.render_ascii_chart({})
        assert isinstance(result, str)

    def test_larger_value_has_longer_bar(self, tmp_path: Path) -> None:
        """큰 값이 더 긴 막대"""
        p = tmp_path / "t.json"
        p.write_text(json.dumps({"tasks": {}}))
        engine = InsightsEngine(p)
        result = engine.render_ascii_chart({"big": 100, "small": 1})
        lines = [l for l in result.splitlines() if "big" in l or "small" in l]
        assert len(lines) == 2
        big_line = next(l for l in lines if "big" in l)
        small_line = next(l for l in lines if "small" in l)
        assert len(big_line) > len(small_line)
