"""Manual YouTube Studio analytics CSV sanitizer and report builder."""

from __future__ import annotations

import csv
from dataclasses import dataclass
from pathlib import Path
from typing import Iterable

ALLOWED_COLUMNS = frozenset(
    {
        "video_id",
        "candidate_id",
        "title",
        "publish_date",
        "snapshot_time",
        "days_since_publish",
        "views",
        "engaged_views",
        "shown_in_feed",
        "viewed_vs_swiped_away",
        "average_view_duration",
        "average_percentage_viewed",
        "likes",
        "shares",
        "comments_count",
        "subscribers_gained",
        "traffic_source",
        "impressions",
        "click_through_rate",
        "notes_by_user",
    }
)
FORBIDDEN_COLUMNS = frozenset(
    {
        "comment",
        "comments_text",
        "comment_text",
        "message",
        "body",
        "reply",
        "author",
        "username",
        "channel_name",
        "profile",
        "email",
        "url",
        "link",
        "raw_text",
        "external_text",
    }
)


@dataclass(frozen=True)
class CsvImportResult:
    rows: tuple[dict[str, str], ...]
    ignored_columns: tuple[str, ...]


class YouTubeAnalyticsCsvSanitizer:
    """Allowlist-only sanitizer. No YouTube API calls; no raw comments."""

    NOTES_MAX_CHARS = 300

    def validate_headers(self, columns: Iterable[str]) -> tuple[str, ...]:
        normalized = tuple((c or "").strip() for c in columns)
        forbidden = [c for c in normalized if c in FORBIDDEN_COLUMNS]
        if forbidden:
            raise ValueError(f"Forbidden YouTube analytics CSV column(s): {', '.join(forbidden)}")
        unknown = [c for c in normalized if c not in ALLOWED_COLUMNS]
        if unknown:
            raise ValueError(f"Unknown/blocked YouTube analytics CSV column(s): {', '.join(unknown)}")
        return normalized

    def sanitize_cell(self, column: str, value: str) -> str:
        value = (value or "").strip()
        if column == "notes_by_user":
            return value[: self.NOTES_MAX_CHARS]
        return value

    def import_csv(self, path: str | Path) -> CsvImportResult:
        path = Path(path)
        with path.open(newline="", encoding="utf-8") as f:
            reader = csv.DictReader(f)
            if reader.fieldnames is None:
                raise ValueError("CSV has no header row")
            allowed_headers = self.validate_headers(reader.fieldnames)
            rows: list[dict[str, str]] = []
            for row in reader:
                sanitized = {col: self.sanitize_cell(col, row.get(col, "")) for col in allowed_headers}
                rows.append(sanitized)
        return CsvImportResult(rows=tuple(rows), ignored_columns=())


def import_youtube_analytics_csv(path: str | Path) -> tuple[dict[str, str], ...]:
    return YouTubeAnalyticsCsvSanitizer().import_csv(path).rows


def _to_float(row: dict[str, str], key: str) -> float:
    try:
        return float((row.get(key) or "0").replace("%", ""))
    except ValueError:
        return 0.0


def build_learning_report_from_csv(path: str | Path) -> str:
    rows = list(import_youtube_analytics_csv(path))
    if not rows:
        return "YouTube manual CSV learning report\nNo rows imported. No external API calls."
    top_views = max(rows, key=lambda r: _to_float(r, "views"))
    top_shares = max(rows, key=lambda r: _to_float(r, "shares"))
    top_subs = max(rows, key=lambda r: _to_float(r, "subscribers_gained"))
    poor_retention = [r for r in rows if _to_float(r, "average_percentage_viewed") and _to_float(r, "average_percentage_viewed") < 50]
    notes = [r.get("notes_by_user", "") for r in rows if r.get("notes_by_user")]
    note_lines = "\n".join(f"- {note}" for note in notes[:5]) or "- none"
    return "\n".join(
        [
            "YouTube manual CSV learning report",
            "Source: user-exported sanitized CSV only; no external API calls.",
            "Viewer comment content: blocked / not imported.",
            "",
            f"Rows imported: {len(rows)}",
            f"Best by views: {top_views.get('candidate_id') or top_views.get('video_id')} ({top_views.get('views', '0')} views)",
            f"Best by shares: {top_shares.get('candidate_id') or top_shares.get('video_id')} ({top_shares.get('shares', '0')} shares)",
            f"Best by subscribers gained: {top_subs.get('candidate_id') or top_subs.get('video_id')} ({top_subs.get('subscribers_gained', '0')})",
            f"Videos with weak retention (<50% avg viewed): {len(poor_retention)}",
            "",
            "Allowed analysis:",
            "- compare themes, hooks, first frames, thumbnails, shares, subscribers, retention, and comments_count",
            "- propose next video topics and safer hooks",
            "- never quote or analyze viewer comments",
            "",
            "User notes summaries:",
            note_lines,
        ]
    )
