import json
import os
import re
import time
import pandas as pd
import requests
from youtube_transcript_api import YouTubeTranscriptApi

OUT_DIR = r"c:\Users\jfmar\josemarichal\github.io\projects\deepfake_ai_s_impact_on_political_campaigns"
os.makedirs(OUT_DIR, exist_ok=True)

# 1. Comprehensive Reference Table of the 28 State Laws Regulating AI / Synthetic Media in Political Ads (2024-2026)
STATE_AI_LAWS = [
    ("Alabama", "HB 172 (2024)", "Mandatory Disclosure", "This video has been manipulated or generated by artificial intelligence."),
    ("Arizona", "HB 2394 / SB 1359 (2024)", "Mandatory Disclosure / Civil Action", "This video has been manipulated or generated by artificial intelligence."),
    ("California", "AB 2355 / AB 2839 / AB 730", "Mandatory Disclosure + Time-Window Ban", "This ad was generated or substantially altered using artificial intelligence."),
    ("Colorado", "HB 24-1147 (2024)", "Mandatory Disclosure", "This communication includes content generated by artificial intelligence."),
    ("Delaware", "HB 316 (2024)", "Mandatory Disclosure", "This video has been manipulated or generated by artificial intelligence."),
    ("Florida", "HB 919 / Fla. Stat. § 106.145", "Mandatory Disclosure (Criminal Misdemeanor)", "Created in whole or in part with the use of generative artificial intelligence."),
    ("Hawaii", "SB 2687 (2024)", "Mandatory Disclosure", "This video has been manipulated or generated by artificial intelligence."),
    ("Idaho", "HB 664 / Idaho Code § 67-6628A", "Mandatory Disclosure", "This video has been manipulated or generated by artificial intelligence."),
    ("Illinois", "HB 4623 / Public Act (2024)", "Deceptive Practice / Civil Liability", "This communication was generated or altered using artificial intelligence."),
    ("Indiana", "HB 1133 / IC 3-9-8", "Mandatory Disclosure", "This communication contains fabricated media."),
    ("Massachusetts", "H.4740 / Election Synthetic Media Rules", "Mandatory Disclosure", "This communication contains synthetic media generated by artificial intelligence."),
    ("Michigan", "HB 5144 / Public Act 265", "Mandatory Disclosure", "This video has been manipulated or generated by artificial intelligence."),
    ("Minnesota", "HF 1370 / Minn. Stat. § 609.771", "Non-Consensual Deepfake Ban (90-Day Window)", "Prohibits dissemination of materially deceptive electoral deepfakes within 90 days of election without consent/disclosure."),
    ("Mississippi", "SB 2577 (2024)", "Mandatory Disclosure (90-Day Window)", "This digitization was created with the aid of artificial intelligence."),
    ("Montana", "SB 2025 / Election Integrity Rules", "Mandatory Disclosure", "This communication contains content generated by artificial intelligence."),
    ("Nevada", "SB / Election Synthetic Media Rules", "Mandatory Disclosure", "This video has been manipulated or generated by artificial intelligence."),
    ("New Hampshire", "HB 1596 (2024) — Triggered by Biden AI Robocall", "Mandatory Disclosure (90-Day Window)", "This communication was generated in whole or in part using artificial intelligence."),
    ("New Jersey", "S.2540 / Deceptive Political Media Act", "Mandatory Disclosure", "This video has been manipulated or generated by artificial intelligence."),
    ("New Mexico", "HB 182 / NMSA § 1-19-26.4", "Mandatory Disclosure", "This video has been manipulated or generated by artificial intelligence."),
    ("New York", "S.6682 / Election Law § 14-106", "Mandatory Disclosure", "This video has been manipulated."),
    ("North Dakota", "Statute on Synthetic Electoral Media", "Mandatory Disclosure", "This communication contains synthetic media."),
    ("Oregon", "SB 1571 / ORS 260.515", "Mandatory Disclosure", "This video includes content generated by artificial intelligence."),
    ("Pennsylvania", "HB 2017 / Campaign AI Rules", "Mandatory Disclosure", "This communication contains content generated by artificial intelligence."),
    ("South Dakota", "SB 164 (2024)", "Mandatory Disclosure (90-Day Window)", "This video has been manipulated or generated by artificial intelligence."),
    ("Texas", "SB 751 / Election Code § 255.004", "Criminal Ban (30-Day Window Before Election)", "Prohibits deceptive deepfake video created with intent to injure a candidate within 30 days of election."),
    ("Utah", "SB 131 / Utah Code § 20A-11-1104", "Mandatory Disclosure", "This video contains synthetic media."),
    ("Washington", "SB 5152 / RCW 42.17A.345", "Mandatory Disclosure (60-Day Window)", "This video has been manipulated."),
    ("Wisconsin", "2023 Wisconsin Act 123 / Wis. Stat. § 11.1303(2m)", "Mandatory Disclosure", "Contains content generated by AI."),
]

# Regex patterns to detect state statutory AI disclaimers or platform disclosures inside transcripts/descriptions
DISCLAIMER_REGEX = re.compile(
    r"(generated or substantially altered|manipulated or generated by artificial intelligence|"
    r"this video has been manipulated|created in whole or in part with the use of generative artificial intelligence|"
    r"contains fabricated media|includes content generated by artificial intelligence|"
    r"contains content generated by ai|contains synthetic media|created with the aid of artificial intelligence|"
    r"altered or synthetic content|ai[- ]generated|deepfake|synthetic media|artificial intelligence|paid for by|approved this message)",
    re.I,
)


def save_state_laws_csv():
    df_laws = pd.DataFrame(
        STATE_AI_LAWS,
        columns=["state", "statute_citation", "regulatory_mechanism", "mandated_verbatim_disclaimer"],
    )
    out_path = os.path.join(OUT_DIR, "state_ai_disclaimer_laws_2026.csv")
    df_laws.to_csv(out_path, index=False, encoding="utf-8")
    print(f"[OK] Saved 28-state statutory disclaimer reference table to {out_path}")
    return df_laws


def search_youtube_videos(query, max_results=20):
    """
    Query YouTube's internal youtubei/v1/search JSON API for videos matching `query`.
    No API key required; returns structured video metadata.
    """
    url = "https://www.youtube.com/youtubei/v1/search?prettyPrint=false"
    payload = {
        "context": {
            "client": {
                "hl": "en",
                "gl": "US",
                "clientName": "WEB",
                "clientVersion": "2.20250101.00.00",
            }
        },
        "query": query,
        # sp="EgIQAQ%3D%3D" filters strictly to Type: Video
        "params": "EgIQAQ%3D%3D",
    }
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
        "Content-Type": "application/json",
    }
    resp = requests.post(url, json=payload, headers=headers, timeout=15)
    resp.raise_for_status()
    data = resp.json()

    videos = []
    try:
        sections = (
            data["contents"]["twoColumnSearchResultsRenderer"]["primaryContents"]
            ["sectionListRenderer"]["contents"]
        )
        for sec in sections:
            items = sec.get("itemSectionRenderer", {}).get("contents", [])
            for item in items:
                vr = item.get("videoRenderer")
                if not vr:
                    continue
                vid = vr.get("videoId")
                if not vid:
                    continue
                title_runs = vr.get("title", {}).get("runs", [])
                title = "".join(r.get("text", "") for r in title_runs)
                owner_runs = vr.get("ownerText", {}).get("runs", [])
                channel = "".join(r.get("text", "") for r in owner_runs)
                pub_time = vr.get("publishedTimeText", {}).get("simpleText", "")
                length_text = vr.get("lengthText", {}).get("simpleText", "")
                view_text = vr.get("viewCountText", {}).get("simpleText", "")

                # Snippet / description preview from search result
                snip_runs = []
                for snip in vr.get("detailedMetadataSnippets", []):
                    for r in snip.get("snippetText", {}).get("runs", []):
                        snip_runs.append(r.get("text", ""))
                snippet = "".join(snip_runs).strip()

                videos.append({
                    "video_id": vid,
                    "title": title,
                    "channel_name": channel,
                    "published_relative": pub_time,
                    "duration": length_text,
                    "view_count_text": view_text,
                    "description_snippet": snippet,
                    "youtube_url": f"https://www.youtube.com/watch?v={vid}",
                    "search_query": query,
                })
                if len(videos) >= max_results:
                    return videos
    except Exception as e:
        print(f"Parse notice for '{query}': {e}")
    return videos


def fetch_transcript_text(ytt_api, video_id):
    """Fetch closed-caption or auto-generated English transcript via youtube_transcript_api."""
    try:
        fetched = ytt_api.fetch(video_id, languages=["en", "en-US", "en-GB"])
        segments = []
        for seg in fetched:
            txt = getattr(seg, "text", None)
            if txt is None and isinstance(seg, dict):
                txt = seg.get("text", "")
            if txt:
                segments.append(str(txt).replace("\n", " ").strip())
        full_text = re.sub(r"\s+", " ", " ".join(segments)).strip()
        return full_text, "Transcript Retrieved"
    except Exception as e:
        err_name = type(e).__name__
        return "", f"No Transcript ({err_name})"


def main():
    save_state_laws_csv()

    # Search queries combining statutory state disclaimer phrases + 2026 campaign AI video ads
    queries = [
        '"generated or substantially altered using artificial intelligence" ad OR campaign',
        '"manipulated or generated by artificial intelligence" ad OR election OR campaign',
        '"Created in whole or in part with the use of generative artificial intelligence"',
        '"contains content generated by AI" political ad OR campaign',
        '"includes content generated by artificial intelligence" campaign OR election',
        '"This video has been manipulated" campaign ad OR political',
        '2026 political campaign ad AI deepfake video',
        '2026 Senate campaign AI generated attack ad video',
        '2026 election AI deepfake candidate video',
        'political ad "artificial intelligence" disclaimer campaign',
        'Super PAC AI generated political ad video',
        'campaign ad AI voice clone deepfake election',
    ]

    all_videos = []
    seen_ids = set()

    print("=== Searching YouTube Video Corpus for State AI Disclaimers & Political AI Ads ===")
    for q in queries:
        try:
            vids = search_youtube_videos(q, max_results=15)
            added = 0
            for v in vids:
                if v["video_id"] not in seen_ids:
                    seen_ids.add(v["video_id"])
                    all_videos.append(v)
                    added += 1
            print(f"[Search] +{added:2d} unique videos (total {len(all_videos):3d}) | {q}")
        except Exception as e:
            print(f"[Search Error] {q}: {e}")
        time.sleep(0.4)

    # Filter to relevant political / campaign / election / AI ad videos
    pol_pat = re.compile(
        r"\b(campaign|election|candidate|senate|congress|governor|pac|political|ad|ads|vote|voter|"
        r"trump|biden|harris|desantis|newsom|democrat|republican|gop|deepfake|artificial intelligence|ai|synthetic|manipulated)\b",
        re.I,
    )
    filtered = [
        v for v in all_videos
        if pol_pat.search(f"{v['title']} {v['channel_name']} {v['description_snippet']}")
    ]
    print(f"\nFiltered to {len(filtered)} political/campaign AI video candidates. Fetching transcripts...")

    ytt_api = YouTubeTranscriptApi()
    for idx, v in enumerate(filtered, start=1):
        transcript_str, status = fetch_transcript_text(ytt_api, v["video_id"])
        v["transcript_status"] = status
        v["transcript_word_count"] = len(transcript_str.split()) if transcript_str else 0
        v["transcript"] = transcript_str

        # Detect any statutory disclaimer or campaign approval phrase inside transcript or snippet
        combined = f"{v['title']} {v['description_snippet']} {transcript_str}"
        matches = list({m.group(0).lower() for m in DISCLAIMER_REGEX.finditer(combined)})
        v["detected_ai_or_ad_disclaimers"] = "; ".join(sorted(matches)) if matches else "None detected in text"

        if idx % 15 == 0 or idx == len(filtered):
            print(f"  Processed {idx}/{len(filtered)} transcripts...")
        time.sleep(0.2)

    df = pd.DataFrame(filtered)
    # Sort so videos with retrieved transcripts appear first
    df["has_transcript"] = df["transcript_word_count"] > 0
    df = df.sort_values(["has_transcript", "transcript_word_count"], ascending=[False, False]).reset_index(drop=True)
    df = df.drop(columns=["has_transcript"])

    out_csv = os.path.join(OUT_DIR, "political_ai_videos_with_transcripts.csv")
    df.to_csv(out_csv, index=False, encoding="utf-8")

    with_trans = (df["transcript_word_count"] > 0).sum()
    print(f"\n=== Complete! Saved {len(df)} videos ({with_trans} with full spoken transcripts) to {out_csv} ===")


if __name__ == "__main__":
    main()

