from __future__ import annotations

import hashlib
import json
import re
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

from sqlmodel import Session, select

from app.core.config import get_settings
from app.models.core import (
    ActivityEvent,
    ExternalPost,
    PlatformAccount,
    PostDraft,
    Provider,
    UploadRequest,
    VideoAsset,
    VideoStatus,
    WebsiteCompanion,
    YouTubeUploadAudit,
    now_utc,
)

YOUTUBE_AUDIT_DIR = Path("/home/agent/jarvis_runtime/autoshortsbot/AutoShortsBot/data/youtube_upload_audits")
WEBSITE_ROOT = Path("/home/agent/projects/TrueTraceShorts_WebSite")
TARGET_CANDIDATES = [
    "erf-027-small-delivery-fee-director-pass",
    "erf-026-fake-support-number-director-pass",
    "erf-025-mfa-prompt-without-login",
]


def read_json(path: Path) -> dict[str, Any]:
    return json.loads(path.read_text(encoding="utf-8"))


def youtube_url(video_id: str | None) -> str | None:
    return f"https://www.youtube.com/shorts/{video_id}" if video_id else None


def _video_id_from_url(url: str | None) -> str | None:
    if not url:
        return None
    match = re.search(r"(?:shorts/|watch\?v=)([A-Za-z0-9_-]+)", url)
    return match.group(1) if match else None


def _slug_key(candidate_id: str) -> str:
    return candidate_id.split("-", 2)[-1]


def find_website_sources(candidate_id: str, website_root: Path = WEBSITE_ROOT) -> dict[str, Any]:
    key = _slug_key(candidate_id)
    candidates = []
    markdown = []
    for path in (website_root / "data" / "candidates").glob("*.json"):
        text = path.read_text(encoding="utf-8", errors="ignore")
        if candidate_id in text or key in text:
            data = read_json(path)
            candidates.append({
                "path": str(path),
                "slug": data.get("slug") or data.get("websiteSlug"),
                "title": data.get("title") or data.get("shortTitle"),
                "videoUrl": data.get("videoUrl"),
                "candidate_id": data.get("candidateId") or data.get("candidate_id") or (data.get("source") or {}).get("candidate_id") or candidate_id,
            })
    for path in (website_root / "src" / "content" / "redflags").glob("*.md"):
        text = path.read_text(encoding="utf-8", errors="ignore")
        if candidate_id in text or key in text:
            title = re.search(r"title:\s*[\"'](.+?)[\"']", text)
            video = re.search(r"videoUrl:\s*[\"']?([^\"'\n]+)", text)
            markdown.append({
                "path": str(path),
                "slug": path.stem,
                "title": title.group(1) if title else None,
                "videoUrl": video.group(1).strip() if video else None,
            })
    return {"candidate_json": candidates, "markdown": markdown}


def find_audit_files(candidate_id: str, audit_dir: Path = YOUTUBE_AUDIT_DIR) -> list[Path]:
    key = _slug_key(candidate_id)
    found = []
    for path in audit_dir.glob("*.json"):
        text = path.read_text(encoding="utf-8", errors="ignore")
        if candidate_id in text or key in text:
            found.append(path)
    return sorted(found)


def _imported_account(session: Session) -> PlatformAccount:
    account = session.exec(select(PlatformAccount).where(PlatformAccount.provider == Provider.youtube, PlatformAccount.external_account_id == "existing-jarvis-uploader")).first()
    if account:
        return account
    account = PlatformAccount(
        provider=Provider.youtube,
        display_name="Existing Jarvis YouTube uploader",
        external_account_id="existing-jarvis-uploader",
        account_type="existing_jarvis_uploader",
        scopes=["https://www.googleapis.com/auth/youtube.upload"],
        status="active",
    )
    session.add(account)
    session.commit()
    session.refresh(account)
    return account


def _ensure_video_and_draft(session: Session, candidate_id: str, audit: dict[str, Any], website: dict[str, Any]) -> tuple[VideoAsset, PostDraft]:
    video = session.exec(select(VideoAsset).where(VideoAsset.candidate_id == candidate_id)).first()
    video_id = audit.get("youtube_video_id") or (audit.get("upload_result") or {}).get("id")
    title = audit.get("title") or ((audit.get("upload_result") or {}).get("snippet") or {}).get("title")
    checksum = audit.get("video_sha256") or hashlib.sha256(candidate_id.encode()).hexdigest()
    if not video:
        video = session.exec(select(VideoAsset).where(VideoAsset.checksum == checksum)).first()
    if not video:
        video = VideoAsset(
            candidate_id=candidate_id,
            package_id=candidate_id,
            source="imported_existing_upload",
            file_path="",
            checksum=checksum,
            working_title=title or candidate_id,
            external_youtube_id=video_id,
            status=VideoStatus.uploaded_private,
            source_label="existing_jarvis_uploader",
        )
    else:
        if video_id and (not video.external_youtube_id or str(video.external_youtube_id).startswith("dryrun_")):
            video.external_youtube_id = video_id
        video.status = VideoStatus.uploaded_private
        video.updated_at = now_utc()
    session.add(video)
    session.commit()
    session.refresh(video)

    draft = session.exec(select(PostDraft).where(PostDraft.video_asset_id == video.id, PostDraft.provider == Provider.youtube)).first()
    snippet = (audit.get("upload_result") or {}).get("snippet") or {}
    status = (audit.get("upload_result") or {}).get("status") or {}
    if not draft:
        draft = PostDraft(video_asset_id=video.id, provider=Provider.youtube)
    draft.title = draft.title or title or snippet.get("title") or video.working_title
    draft.description = draft.description or snippet.get("description")
    draft.tags_json = draft.tags_json or audit.get("tags") or snippet.get("tags") or []
    draft.category_id = draft.category_id or snippet.get("categoryId") or "22"
    draft.privacy_status = status.get("privacyStatus") or audit.get("privacyStatus") or audit.get("privacy_status") or "private"
    if video_id and (not draft.external_post_id or str(draft.external_post_id).startswith("dryrun_")):
        draft.external_post_id = video_id
    draft.status = "uploaded_private"
    draft.updated_at = now_utc()
    session.add(draft)
    session.commit()
    session.refresh(draft)
    return video, draft


def _upsert_external_post(session: Session, video: VideoAsset, draft: PostDraft, video_id: str, title: str | None) -> ExternalPost:
    external = session.exec(select(ExternalPost).where(ExternalPost.provider == Provider.youtube, ExternalPost.external_post_id == video_id)).first()
    if not external:
        external = ExternalPost(provider=Provider.youtube, external_post_id=video_id)
    external.video_asset_id = video.id
    external.title = title or draft.title or video.working_title
    external.source_label = "existing_jarvis_uploader"
    external.mapping_status = "linked"
    external.updated_at = now_utc()
    session.add(external)
    session.commit()
    session.refresh(external)
    return external


def _upsert_website_companion(session: Session, video: VideoAsset, draft: PostDraft, candidate_id: str, video_id: str | None, website: dict[str, Any]) -> WebsiteCompanion:
    comp = session.exec(select(WebsiteCompanion).where(WebsiteCompanion.video_asset_id == video.id)).first()
    if not comp:
        comp = WebsiteCompanion(video_asset_id=video.id, post_draft_id=draft.id)
    first_md = (website.get("markdown") or [{}])[0]
    first_candidate = (website.get("candidate_json") or [{}])[0]
    comp.post_draft_id = draft.id
    comp.website_repo_path = str(WEBSITE_ROOT)
    comp.slug = first_md.get("slug") or first_candidate.get("slug") or _slug_key(candidate_id)
    comp.title = first_md.get("title") or first_candidate.get("title") or draft.title
    comp.youtube_video_id = video_id
    comp.video_url_internal = youtube_url(video_id)
    comp.generated_content_path = first_md.get("path")
    comp.candidate_payload_json = first_candidate or {"candidate_id": candidate_id}
    comp.website_link_allowed = bool(first_md.get("videoUrl") or first_candidate.get("videoUrl")) and not str(video_id or "").startswith("dryrun_")
    comp.status = "pushed" if first_md else ("generated" if first_candidate else "missing")
    comp.updated_at = now_utc()
    session.add(comp)
    session.commit()
    session.refresh(comp)
    return comp


def import_existing_audit(session: Session, audit_path: Path, dry_run: bool = False) -> dict[str, Any]:
    audit = read_json(audit_path)
    candidate_id = audit.get("candidate_id") or audit_path.name.split("_", 1)[0]
    video_id = audit.get("youtube_video_id") or (audit.get("upload_result") or {}).get("id")
    website = find_website_sources(candidate_id)
    if dry_run:
        return {"candidate_id": candidate_id, "youtube_video_id": video_id, "website": website, "audit_path": str(audit_path), "dry_run": True}
    account = _imported_account(session)
    video, draft = _ensure_video_and_draft(session, candidate_id, audit, website)
    if video_id:
        _upsert_external_post(session, video, draft, video_id, audit.get("title"))
    existing = session.exec(select(YouTubeUploadAudit).where(YouTubeUploadAudit.youtube_video_id == video_id, YouTubeUploadAudit.video_asset_id == video.id)).first()
    yta = existing or YouTubeUploadAudit(video_asset_id=video.id, post_draft_id=draft.id, platform_account_id=account.id)
    yta.post_draft_id = draft.id
    yta.platform_account_id = account.id
    yta.youtube_video_id = video_id
    yta.youtube_url = youtube_url(video_id)
    yta.privacy_status = audit.get("privacyStatus") or ((audit.get("upload_result") or {}).get("status") or {}).get("privacyStatus") or "private"
    yta.title = audit.get("title") or draft.title
    yta.description_hash = audit.get("description_hash")
    yta.tags_json = audit.get("tags") or ((audit.get("upload_result") or {}).get("snippet") or {}).get("tags") or []
    yta.category_id = ((audit.get("upload_result") or {}).get("snippet") or {}).get("categoryId") or draft.category_id
    yta.contains_synthetic_media = bool(audit.get("containsSyntheticMedia", True))
    yta.self_declared_made_for_kids = bool(audit.get("selfDeclaredMadeForKids", False))
    yta.notify_subscribers = False
    yta.upload_status = "external_existing_upload"
    yta.api_endpoints_called = audit.get("api_endpoints_called") or ["videos.insert"]
    yta.oauth_scopes_used = audit.get("oauth_scopes_used") or ["https://www.googleapis.com/auth/youtube.upload"]
    yta.website_link_allowed = bool((website.get("markdown") or [{}])[0].get("videoUrl") or (website.get("candidate_json") or [{}])[0].get("videoUrl"))
    yta.request_redacted_json = {"source_audit_path": str(audit_path), "candidate_id": candidate_id, "version": audit.get("version")}
    yta.response_redacted_json = {"youtube_video_id": video_id, "privacyStatus": yta.privacy_status, "source": "existing_jarvis_uploader"}
    yta.updated_at = now_utc()
    session.add(yta)
    comp = _upsert_website_companion(session, video, draft, candidate_id, video_id, website)
    request = session.exec(select(UploadRequest).where(UploadRequest.expected_candidate_id == candidate_id, UploadRequest.status.in_(["approved", "sent_to_executor", "upload_detected"]))).first()
    if request:
        request.status = "completed"
        request.youtube_video_id = video_id
        request.upload_audit_id = yta.id
        request.completed_at = now_utc()
        request.updated_at = now_utc()
        session.add(request)
    for event_type, label in [
        ("existing_youtube_upload_detected", "Existing YouTube upload detected"),
        ("existing_website_guide_detected", "Existing website guide detected"),
        ("website_state_reconciled", "Website state reconciled"),
    ]:
        session.add(ActivityEvent(entity_type="video", entity_id=video.id, event_type=event_type, label=label, payload_json={"candidate_id": candidate_id, "youtube_video_id": video_id, "website_status": comp.status, "audit_path": str(audit_path)}))
    session.commit()
    return {"candidate_id": candidate_id, "video_asset_id": video.id, "post_draft_id": draft.id, "youtube_video_id": video_id, "audit_id": yta.id, "website_slug": comp.slug, "website_status": comp.status, "audit_path": str(audit_path)}


def reconcile_candidates(session: Session, candidate_ids: list[str] | None = None, dry_run: bool = False) -> dict[str, Any]:
    rows = []
    for candidate_id in candidate_ids or TARGET_CANDIDATES:
        files = find_audit_files(candidate_id)
        if not files:
            rows.append({"candidate_id": candidate_id, "status": "missing_audit", "website": find_website_sources(candidate_id)})
            continue
        rows.append(import_existing_audit(session, files[-1], dry_run=dry_run))
    return {"items": rows, "count": len(rows), "dry_run": dry_run}


def detect_pending_website_duplicates(session: Session) -> dict[str, Any]:
    pending = WEBSITE_ROOT / "src" / "content" / "redflags" / "login-prompt-you-didn-t-start-don-t-approve.md"
    existing = WEBSITE_ROOT / "src" / "content" / "redflags" / "mfa-prompt-without-login.md"
    duplicate = pending.exists() and existing.exists()
    result = {
        "potential_duplicates": [],
        "pending_files": [],
        "push_status": "waiting for approval",
    }
    if pending.exists():
        result["pending_files"].append(str(pending))
    cand = WEBSITE_ROOT / "data" / "candidates" / "package_real_003.json"
    thumb = WEBSITE_ROOT / "public" / "redflags" / "thumbnails" / "login-prompt-you-didn-t-start-don-t-approve-start.webp"
    for p in [cand, thumb]:
        if p.exists():
            result["pending_files"].append(str(p))
    if duplicate:
        item = {
            "status": "potential_duplicate",
            "existing_slug": "mfa-prompt-without-login",
            "pending_slug": "login-prompt-you-didn-t-start-don-t-approve",
            "recommendation": "merge / discard pending / keep as new — requires explicit approval",
            "existing_path": str(existing),
            "pending_path": str(pending),
        }
        result["potential_duplicates"].append(item)
        comp = session.exec(select(WebsiteCompanion).where(WebsiteCompanion.slug == "mfa-prompt-without-login")).first()
        if comp:
            comp.duplicate_status_json = item
            comp.updated_at = now_utc()
            session.add(comp)
            session.add(ActivityEvent(entity_type="website", entity_id=comp.id, event_type="website_state_reconciled", label="Potential duplicate website guide detected", payload_json=item))
            session.commit()
    return result
