"""SQLite-based history store for scan records."""

import sqlite3
from datetime import datetime, timedelta
from pathlib import Path

from apiposture.core.models.scan_result import ScanResult

from apiposture_pro.features.historical_tracking.git_info import get_git_info
from apiposture_pro.features.historical_tracking.models import ScanRecord


class HistoryStore:
    """Manages historical scan records in SQLite database."""

    def __init__(self, db_path: Path | None = None) -> None:
        """
        Initialize history store.

        Args:
            db_path: Path to SQLite database file. Defaults to ~/.apiposture/history.db
        """
        if db_path is None:
            db_path = Path.home() / ".apiposture" / "history.db"

        self.db_path = db_path
        self.db_path.parent.mkdir(parents=True, exist_ok=True)
        self._init_database()

    def _init_database(self) -> None:
        """Initialize database schema."""
        with sqlite3.connect(self.db_path) as conn:
            conn.execute("""
                CREATE TABLE IF NOT EXISTS scan_records (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    project_path TEXT NOT NULL,
                    scan_time TIMESTAMP NOT NULL,
                    total_endpoints INTEGER NOT NULL,
                    total_findings INTEGER NOT NULL,
                    critical_count INTEGER NOT NULL DEFAULT 0,
                    high_count INTEGER NOT NULL DEFAULT 0,
                    medium_count INTEGER NOT NULL DEFAULT 0,
                    low_count INTEGER NOT NULL DEFAULT 0,
                    info_count INTEGER NOT NULL DEFAULT 0,
                    risk_score INTEGER NOT NULL DEFAULT 0,
                    git_commit TEXT,
                    git_branch TEXT,
                    git_is_dirty BOOLEAN DEFAULT 0,
                    scan_duration_ms INTEGER NOT NULL DEFAULT 0
                )
            """)

            # Create indexes
            conn.execute(
                "CREATE INDEX IF NOT EXISTS idx_scan_time ON scan_records(scan_time DESC)"
            )
            conn.execute(
                "CREATE INDEX IF NOT EXISTS idx_project_path ON scan_records(project_path)"
            )
            conn.execute(
                "CREATE INDEX IF NOT EXISTS idx_git_commit ON scan_records(git_commit)"
            )

            conn.commit()

    def save_scan(
        self, result: ScanResult, duration_ms: int = 0, risk_score: int = 0
    ) -> int:
        """
        Save scan result to history.

        Args:
            result: Scan result to save
            duration_ms: Scan duration in milliseconds
            risk_score: Calculated risk score

        Returns:
            ID of saved record
        """
        # Get git information
        git_info = get_git_info(result.scan_path)

        # Count findings by severity
        from collections import Counter

        severity_counts = Counter(
            f.severity.value if hasattr(f.severity, "value") else str(f.severity).lower()
            for f in result.findings
        )

        record = ScanRecord(
            project_path=str(result.scan_path),
            scan_time=datetime.now(),
            total_endpoints=len(result.endpoints),
            total_findings=len(result.findings),
            critical_count=severity_counts.get("critical", 0),
            high_count=severity_counts.get("high", 0),
            medium_count=severity_counts.get("medium", 0),
            low_count=severity_counts.get("low", 0),
            info_count=severity_counts.get("info", 0),
            risk_score=risk_score,
            git_commit=git_info.commit_sha,
            git_branch=git_info.branch,
            git_is_dirty=git_info.is_dirty,
            scan_duration_ms=duration_ms,
        )

        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.execute(
                """
                INSERT INTO scan_records (
                    project_path, scan_time, total_endpoints, total_findings,
                    critical_count, high_count, medium_count, low_count, info_count,
                    risk_score, git_commit, git_branch, git_is_dirty, scan_duration_ms
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            """,
                (
                    record.project_path,
                    record.scan_time,
                    record.total_endpoints,
                    record.total_findings,
                    record.critical_count,
                    record.high_count,
                    record.medium_count,
                    record.low_count,
                    record.info_count,
                    record.risk_score,
                    record.git_commit,
                    record.git_branch,
                    1 if record.git_is_dirty else 0,
                    record.scan_duration_ms,
                ),
            )
            conn.commit()
            return cursor.lastrowid

    def list_scans(
        self, project_path: str | None = None, limit: int = 50
    ) -> list[ScanRecord]:
        """
        List historical scans.

        Args:
            project_path: Filter by project path (optional)
            limit: Maximum number of records to return

        Returns:
            List of scan records
        """
        query = "SELECT * FROM scan_records"
        params: tuple = ()

        if project_path:
            query += " WHERE project_path = ?"
            params = (project_path,)

        query += " ORDER BY scan_time DESC LIMIT ?"
        params = params + (limit,)

        with sqlite3.connect(self.db_path) as conn:
            conn.row_factory = sqlite3.Row
            cursor = conn.execute(query, params)
            return [self._row_to_record(row) for row in cursor.fetchall()]

    def get_scan(self, scan_id: int) -> ScanRecord | None:
        """
        Get specific scan by ID.

        Args:
            scan_id: Scan record ID

        Returns:
            Scan record or None if not found
        """
        with sqlite3.connect(self.db_path) as conn:
            conn.row_factory = sqlite3.Row
            cursor = conn.execute("SELECT * FROM scan_records WHERE id = ?", (scan_id,))
            row = cursor.fetchone()
            return self._row_to_record(row) if row else None

    def get_trend(
        self, project_path: str, days: int = 30
    ) -> list[tuple[datetime, int, int]]:
        """
        Get trend data for a project.

        Args:
            project_path: Project path to analyze
            days: Number of days to include

        Returns:
            List of (scan_time, total_findings, risk_score) tuples
        """
        cutoff = datetime.now() - timedelta(days=days)

        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.execute(
                """
                SELECT scan_time, total_findings, risk_score
                FROM scan_records
                WHERE project_path = ? AND scan_time >= ?
                ORDER BY scan_time ASC
            """,
                (project_path, cutoff),
            )

            return [
                (datetime.fromisoformat(row[0]), row[1], row[2]) for row in cursor.fetchall()
            ]

    def cleanup(self, days: int = 90) -> int:
        """
        Clean up old scan records.

        Args:
            days: Delete records older than this many days

        Returns:
            Number of records deleted
        """
        cutoff = datetime.now() - timedelta(days=days)

        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.execute(
                "DELETE FROM scan_records WHERE scan_time < ?", (cutoff,)
            )
            conn.commit()
            return cursor.rowcount

    def _row_to_record(self, row: sqlite3.Row) -> ScanRecord:
        """Convert database row to ScanRecord."""
        return ScanRecord(
            id=row["id"],
            project_path=row["project_path"],
            scan_time=datetime.fromisoformat(row["scan_time"]),
            total_endpoints=row["total_endpoints"],
            total_findings=row["total_findings"],
            critical_count=row["critical_count"],
            high_count=row["high_count"],
            medium_count=row["medium_count"],
            low_count=row["low_count"],
            info_count=row["info_count"],
            risk_score=row["risk_score"],
            git_commit=row["git_commit"],
            git_branch=row["git_branch"],
            git_is_dirty=bool(row["git_is_dirty"]),
            scan_duration_ms=row["scan_duration_ms"],
        )
