#!/usr/bin/env python3
"""Pilot dry-run: scrape 3 active FB groups via Apify, run full pipeline, report.

NO writes to dgred. Uses real Apify + OpenAI calls.
"""

from __future__ import annotations

import json
import logging
import re
import sys
import time
from collections import Counter
from datetime import datetime, timedelta
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src"))

import httpx

from agent_samochodowy.config import Settings
from agent_samochodowy.dgred.client import DgredClient
from agent_samochodowy.dgred.writer import LeadWriter
from agent_samochodowy.extraction import extract
from agent_samochodowy.extraction.prefilter import is_part_request as prefilter_check
from agent_samochodowy.ingestor.groups import GroupManager
from agent_samochodowy.matcher.engine import match
from agent_samochodowy.matcher.index import CatalogIndex
from agent_samochodowy.models import Post
from agent_samochodowy.status import map_status

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)
logging.getLogger("httpx").setLevel(logging.WARNING)
logging.getLogger("openai").setLevel(logging.WARNING)
logging.getLogger("httpcore").setLevel(logging.WARNING)

APIFY_BASE = "https://api.apify.com/v2"
STATUS_ID_MAP = {"Dopasowano": 67, "Do weryfikacji": 68, "Brak dopasowania": 69}

N_GROUPS = 3
RESULTS_LIMIT = 20


def run_apify_actor(token: str, actor_id: str, group_url: str) -> list[dict]:
    """Run Apify actor for a single group, return items or empty list on error."""
    with httpx.Client(timeout=180) as client:
        logger.info("Starting Apify run for: %s", group_url)
        try:
            run_resp = client.post(
                f"{APIFY_BASE}/acts/{actor_id}/runs",
                params={"token": token},
                json={
                    "startUrls": [{"url": group_url}],
                    "resultsLimit": RESULTS_LIMIT,
                    "sortBy": "New posts",
                },
            )
            run_resp.raise_for_status()
        except httpx.HTTPStatusError as e:
            logger.warning("Apify start failed for %s: %s", group_url,
                           re.sub(r"(apify_api_)[A-Za-z0-9]+", r"\1***", str(e)))
            return []

        run_data = run_resp.json().get("data", {})
        run_id = run_data.get("id")
        dataset_id = run_data.get("defaultDatasetId")
        if not run_id:
            logger.warning("No run ID returned for %s", group_url)
            return []

        # Poll for completion
        for _ in range(90):  # max 3 min
            time.sleep(2)
            try:
                status_resp = client.get(
                    f"{APIFY_BASE}/actor-runs/{run_id}",
                    params={"token": token},
                )
                status_resp.raise_for_status()
                status = status_resp.json().get("data", {}).get("status")
                if status in ("SUCCEEDED", "FAILED", "ABORTED", "TIMED-OUT"):
                    break
            except Exception:
                continue

        if status != "SUCCEEDED":
            logger.warning("Apify run %s for %s ended with status: %s", run_id, group_url, status)
            if status == "FAILED":
                return []  # private/inaccessible group

        # Fetch results
        if not dataset_id:
            return []
        try:
            resp = client.get(
                f"{APIFY_BASE}/datasets/{dataset_id}/items",
                params={"token": token, "format": "json"},
            )
            resp.raise_for_status()
            return resp.json()
        except Exception as e:
            logger.warning("Failed to fetch dataset for %s: %s", group_url, e)
            return []


def map_item_to_post(item: dict, group_name: str) -> Post | None:
    """Map Apify output to Post model."""
    try:
        text = item.get("text") or item.get("message") or ""
        if not text.strip():
            return None

        post_id = str(item.get("postId") or item.get("id") or item.get("url", ""))
        if not post_id:
            return None

        timestamp = item.get("timestamp") or item.get("time") or item.get("date")
        if timestamp and isinstance(timestamp, str):
            # Try parsing various formats
            for fmt in ("%Y-%m-%dT%H:%M:%S.%fZ", "%Y-%m-%dT%H:%M:%SZ",
                        "%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M:%S"):
                try:
                    timestamp = datetime.strptime(timestamp, fmt)
                    break
                except ValueError:
                    continue
            else:
                timestamp = datetime.now()
        elif not timestamp:
            timestamp = datetime.now()

        author = item.get("authorName") or item.get("user", {}).get("name", "Unknown") if isinstance(item.get("user"), dict) else item.get("authorName", "Unknown")
        profile_url = item.get("authorProfileUrl") or item.get("profileUrl") or ""
        post_url = item.get("postUrl") or item.get("url") or ""

        return Post(
            post_id=post_id,
            group=group_name,
            author_name=author or "Unknown",
            author_profile_url=profile_url,
            text=text,
            timestamp=timestamp,
            post_url=post_url,
        )
    except Exception as e:
        logger.debug("Failed to map item: %s", e)
        return None


def main() -> None:
    settings = Settings()
    settings.dry_run = True

    print("=" * 80)
    print("PILOT DRY-RUN — Apify FB Groups → Pipeline")
    print("=" * 80)
    print(f"LLM:           {settings.llm_provider} / {settings.llm_model}")
    print(f"Post max age:  {settings.post_max_age_hours}h")
    print(f"Groups to scan: {N_GROUPS}")
    print(f"Posts per group: {RESULTS_LIMIT}")
    print()

    # Pick groups
    gm = GroupManager(settings.db_path)
    groups = gm.pick_groups(N_GROUPS)
    print(f"Selected groups ({len(groups)}):")
    for g in groups:
        print(f"  {g['group_id']} | {g['nazwa'][:60]}")
    print()

    # Scrape via Apify
    all_posts: list[Post] = []
    groups_ok = 0
    groups_failed = 0

    for g in groups:
        items = run_apify_actor(settings.apify_token, settings.apify_actor_id, g["url"])
        if items:
            groups_ok += 1
            for item in items:
                post = map_item_to_post(item, g["nazwa"])
                if post:
                    all_posts.append(post)
            gm.record_scan(g["group_id"], queries=0, hits=0)  # updated after pipeline
            logger.info("Group %s: %d items → %d posts mapped",
                        g["group_id"], len(items), sum(1 for i in items if map_item_to_post(i, g["nazwa"])))
        else:
            groups_failed += 1
            logger.warning("Group %s (%s): no data (private/inaccessible)",
                           g["group_id"], g["nazwa"][:40])

    total_fetched = len(all_posts)

    # Freshness filter
    cutoff = datetime.now() - timedelta(hours=settings.post_max_age_hours) if settings.post_max_age_hours > 0 else None
    if cutoff:
        fresh = [p for p in all_posts if p.timestamp.replace(tzinfo=None) >= cutoff]
        skipped_old = len(all_posts) - len(fresh)
        all_posts = fresh
    else:
        skipped_old = 0

    print(f"\nGroups accessible:   {groups_ok}")
    print(f"Groups private/fail: {groups_failed}")
    print(f"Posts fetched:       {total_fetched}")
    print(f"Too old (>{settings.post_max_age_hours}h):     {skipped_old}")
    print(f"To analyze:          {len(all_posts)}")
    print("=" * 80)

    # Pipeline
    index = CatalogIndex(settings.db_path)
    dgred = DgredClient(settings)
    writer = LeadWriter(dgred, settings)

    statuses: Counter[str] = Counter()
    methods: Counter[str] = Counter()
    part_requests = 0
    leads: list[dict] = []  # collect for example display

    for post in all_posts:
        passes = prefilter_check(post.text)
        if not passes:
            continue

        query = extract(post, settings)
        if query is None or not query.is_part_request:
            continue

        part_requests += 1
        result = match(query, index)
        status_name = map_status(result, settings)
        statuses[status_name] += 1
        methods[result.method] += 1

        leads.append({
            "post": post,
            "query": query,
            "result": result,
            "status_name": status_name,
        })

    index.close()
    dgred.close()
    gm.close()

    # Report
    print()
    print("=" * 80)
    print("PODSUMOWANIE PILOTA")
    print("=" * 80)
    print(f"Grup przeskanowanych:    {groups_ok} / {N_GROUPS}")
    print(f"Grup prywatnych/niedost.: {groups_failed}")
    print(f"Postów pobranych:        {total_fetched}")
    print(f"Za stare (>{settings.post_max_age_hours}h):         {skipped_old}")
    print(f"Do analizy:              {len(all_posts)}")
    print(f"Zapytania o część:       {part_requests}")
    print(f"Odrzucone (prefilter+LLM): {len(all_posts) - part_requests}")
    print()
    print("Rozkład statusów:")
    for s in ["Dopasowano", "Do weryfikacji", "Brak dopasowania"]:
        print(f"  {STATUS_ID_MAP.get(s, '?')} {s:25s} {statuses.get(s, 0)}")
    print()
    print("Trafienia wg metody:")
    for m in ["oe", "engine_code", "atrybuty", "fuzzy", "brak"]:
        if methods.get(m, 0):
            print(f"  {m:20s} {methods[m]}")

    # Show up to 8 example leads
    print()
    print("=" * 80)
    print(f"PRZYKŁADOWE LEADY (do {min(8, len(leads))} z {len(leads)})")
    print("=" * 80)

    for lead in leads[:8]:
        post = lead["post"]
        query = lead["query"]
        result = lead["result"]
        status_name = lead["status_name"]
        status_id = STATUS_ID_MAP.get(status_name, 0)

        print(f"\n{'─' * 70}")
        print(f"POST: {post.text[:100]}")
        print(f"Grupa: {post.group}")
        print(f"Autor: {post.author_name} | {post.timestamp}")
        print(f"ATRYBUTY: marka={query.brand} model={query.model} "
              f"cat={query.category} OE={','.join(query.oe_numbers) if query.oe_numbers else '—'}")
        print(f"MATCH: {result.method} | {result.confidence:.0%} | {len(result.offers)} ofert")
        if result.offers:
            for o in result.offers[:2]:
                price_str = f"{o.price} {o.currency}" if o.price else "?"
                print(f"  → {o.title[:65]}")
                print(f"    {o.url}")
                print(f"    Cena: {price_str}")
        print(f"STATUS: {status_id} = {status_name}")

    print()
    print("=" * 80)
    print("NIE zapisano do dgred (dry-run).")
    print("=" * 80)


if __name__ == "__main__":
    main()
