"""Debt trend analysis over time.

Compares sequential scans to determine if technical debt is
improving, stable, or degrading over time.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any

from ..store import DebtStore


@dataclass
class TrendPoint:
    """A single data point in the trend."""

    scan_id: str
    timestamp: float
    total_score: float
    total_issues: int
    branch: str
    commit_sha: str

    def to_dict(self) -> dict[str, Any]:
        return {
            "scan_id": self.scan_id,
            "timestamp": self.timestamp,
            "total_score": round(self.total_score, 1),
            "total_issues": self.total_issues,
            "branch": self.branch,
            "commit_sha": self.commit_sha,
        }


@dataclass
class TrendAnalysis:
    """Result of trend analysis."""

    repo_path: str
    direction: str  # "improving", "degrading", "stable", "insufficient_data"
    score_change: float  # Positive = more debt, negative = less
    score_change_pct: float
    issue_change: int
    data_points: list[TrendPoint]
    period_days: float

    def to_dict(self) -> dict[str, Any]:
        return {
            "repo_path": self.repo_path,
            "direction": self.direction,
            "score_change": round(self.score_change, 1),
            "score_change_pct": round(self.score_change_pct, 1),
            "issue_change": self.issue_change,
            "data_points": [p.to_dict() for p in self.data_points],
            "period_days": round(self.period_days, 1),
        }


class TrendAnalyzer:
    """Analyze technical debt trends over time."""

    def __init__(self, store: DebtStore) -> None:
        self.store = store

    def analyze(
        self, repo_path: str, limit: int = 20
    ) -> TrendAnalysis:
        """Analyze the debt trend for a repository.

        Args:
            repo_path: Repository path to analyze.
            limit: Max number of historical scans to consider.

        Returns:
            TrendAnalysis with direction and metrics.
        """
        scans = self.store.get_scans_for_repo(repo_path, limit)

        if len(scans) < 2:
            return TrendAnalysis(
                repo_path=repo_path,
                direction="insufficient_data",
                score_change=0,
                score_change_pct=0,
                issue_change=0,
                data_points=[self._scan_to_point(s) for s in scans],
                period_days=0,
            )

        # Scans are newest-first, reverse for chronological
        scans.reverse()

        points = [self._scan_to_point(s) for s in scans]
        first = scans[0]
        last = scans[-1]

        score_change = last.total_score - first.total_score
        score_change_pct = (
            (score_change / first.total_score * 100) if first.total_score > 0 else 0
        )
        issue_change = last.total_issues - first.total_issues
        period_days = (last.timestamp - first.timestamp) / 86400.0

        # Determine direction using linear regression slope
        direction = self._determine_direction(scans)

        return TrendAnalysis(
            repo_path=repo_path,
            direction=direction,
            score_change=score_change,
            score_change_pct=score_change_pct,
            issue_change=issue_change,
            data_points=points,
            period_days=period_days,
        )

    def compare_branches(
        self, repo_path: str, branch_a: str, branch_b: str
    ) -> dict[str, Any]:
        """Compare debt levels between two branches.

        Finds the most recent scan for each branch and compares.
        """
        all_scans = self.store.get_scans_for_repo(repo_path, limit=100)

        scan_a = next((s for s in all_scans if s.branch == branch_a), None)
        scan_b = next((s for s in all_scans if s.branch == branch_b), None)

        if not scan_a or not scan_b:
            return {
                "error": "Could not find scans for both branches",
                "branch_a": branch_a,
                "branch_b": branch_b,
                "scan_a_found": scan_a is not None,
                "scan_b_found": scan_b is not None,
            }

        score_diff = scan_b.total_score - scan_a.total_score
        issue_diff = scan_b.total_issues - scan_a.total_issues

        return {
            "branch_a": {
                "branch": branch_a,
                "scan_id": scan_a.scan_id,
                "total_score": round(scan_a.total_score, 1),
                "total_issues": scan_a.total_issues,
            },
            "branch_b": {
                "branch": branch_b,
                "scan_id": scan_b.scan_id,
                "total_score": round(scan_b.total_score, 1),
                "total_issues": scan_b.total_issues,
            },
            "score_diff": round(score_diff, 1),
            "issue_diff": issue_diff,
            "better_branch": branch_a if score_diff > 0 else branch_b,
            "summary_comparison": self._compare_summaries(
                scan_a.summary, scan_b.summary
            ),
        }

    def _determine_direction(self, scans: list) -> str:
        """Determine trend direction using simple linear regression on scores."""
        if len(scans) < 2:
            return "insufficient_data"

        n = len(scans)
        x_values = list(range(n))
        y_values = [s.total_score for s in scans]

        # Calculate slope of linear regression
        x_mean = sum(x_values) / n
        y_mean = sum(y_values) / n

        numerator = sum(
            (x - x_mean) * (y - y_mean) for x, y in zip(x_values, y_values)
        )
        denominator = sum((x - x_mean) ** 2 for x in x_values)

        if denominator == 0:
            return "stable"

        slope = numerator / denominator

        # Threshold for significance (relative to mean)
        threshold = y_mean * 0.02 if y_mean > 0 else 0.5

        if slope > threshold:
            return "degrading"  # Score increasing = more debt
        elif slope < -threshold:
            return "improving"  # Score decreasing = less debt
        return "stable"

    def _scan_to_point(self, scan) -> TrendPoint:
        return TrendPoint(
            scan_id=scan.scan_id,
            timestamp=scan.timestamp,
            total_score=scan.total_score,
            total_issues=scan.total_issues,
            branch=scan.branch,
            commit_sha=scan.commit_sha,
        )

    def _compare_summaries(
        self, summary_a: dict, summary_b: dict
    ) -> dict[str, Any]:
        """Compare two scan summaries dimension by dimension."""
        comparison: dict[str, Any] = {}

        for key in summary_a:
            if key in summary_b:
                a_data = summary_a[key]
                b_data = summary_b[key]
                if isinstance(a_data, dict) and isinstance(b_data, dict):
                    dim_diff: dict[str, Any] = {}
                    for metric in a_data:
                        if metric in b_data and isinstance(a_data[metric], (int, float)):
                            diff = b_data[metric] - a_data[metric]
                            dim_diff[metric] = round(diff, 2)
                    comparison[key] = dim_diff

        return comparison
