"""AI 유입 사용자 전환 퍼널 분석기.

GA4 Data API(옵셔널) 또는 CSV 수동 입력으로 퍼널 단계별 드롭오프율을 분석한다.

Usage:
    python3 conversion_tracker.py --property-id 123456 --date-range 30d
    python3 conversion_tracker.py --events-csv events.csv --output funnel-report.md
"""

import argparse
import csv
import json
import sys
from typing import Any, Dict, List, Optional, Tuple

from config import FUNNEL_STAGES, is_ga4_configured  # type: ignore[import-not-found]

try:
    from google.analytics.data_v1beta import BetaAnalyticsDataClient  # type: ignore[import-not-found]
    from google.analytics.data_v1beta.types import (  # type: ignore[import-not-found]
        DateRange,
        Dimension,
        Metric,
        RunReportRequest,
    )

    _GA4_AVAILABLE = True
except ImportError:
    _GA4_AVAILABLE = False

_CONVERSION_STAGE = "전환"
_ARRIVAL_STAGE = "AI 검색 도착"


def calculate_dropoff(prev_users: int, curr_users: int) -> float:
    """(prev-curr)/prev*100. prev=0이면 0.0 반환."""
    if prev_users == 0:
        return 0.0
    return (prev_users - curr_users) / prev_users * 100


def calculate_funnel(stage_data: List[Tuple[str, int]]) -> List[Dict[str, Any]]:
    """퍼널 단계별 rate 및 dropoff를 계산한다."""
    if not stage_data:
        return []
    first_users = stage_data[0][1]
    funnel: List[Dict[str, Any]] = []
    for idx, (stage, users) in enumerate(stage_data):
        entry: Dict[str, Any] = {"stage": stage, "users": users}
        if idx == 0:
            entry["rate"] = "100%"
        else:
            rate = users / first_users * 100 if first_users else 0.0
            entry["rate"] = f"{rate:.1f}%"
            entry["dropoff"] = f"{calculate_dropoff(stage_data[idx - 1][1], users):.1f}%"
        funnel.append(entry)
    return funnel


def analyze_ai_sources(source_data: List[Tuple[str, int, str]]) -> Dict[str, Dict[str, Any]]:
    """AI 소스별 arrivals, conversions, CVR을 계산한다."""
    if not source_data:
        return {}
    arrivals: Dict[str, int] = {}
    conversions: Dict[str, int] = {}
    for stage, users, source in source_data:
        if stage == _ARRIVAL_STAGE:
            arrivals[source] = arrivals.get(source, 0) + users
        elif stage == _CONVERSION_STAGE:
            conversions[source] = conversions.get(source, 0) + users
    result: Dict[str, Dict[str, Any]] = {}
    for src in set(arrivals) | set(conversions):
        arr = arrivals.get(src, 0)
        conv = conversions.get(src, 0)
        cvr = conv / arr * 100 if arr else 0.0
        result[src] = {"arrivals": arr, "conversions": conv, "cvr": f"{cvr:.1f}%"}
    return result


def generate_insights(funnel: List[Dict[str, Any]], sources: Dict[str, Dict[str, Any]]) -> List[str]:
    """퍼널 및 소스 데이터에서 인사이트를 자동 생성한다."""
    insights: List[str] = []
    stages_with_dropoff = [f for f in funnel if "dropoff" in f]
    if stages_with_dropoff:
        max_stage = max(stages_with_dropoff, key=lambda f: float(f["dropoff"].replace("%", "")))
        idx = funnel.index(max_stage)
        prev_stage = funnel[idx - 1]["stage"] if idx > 0 else "?"
        insights.append(f"최대 이탈 지점: {prev_stage} → {max_stage['stage']} ({max_stage['dropoff']} 드롭오프)")
    if sources:
        sorted_src = sorted(sources.items(), key=lambda kv: float(kv[1]["cvr"].replace("%", "")), reverse=True)
        best_src, best_val = sorted_src[0]
        insights.append(f"최고 전환 소스: {best_src} (CVR {best_val['cvr']})")
        if len(sorted_src) > 1:
            worst_src, worst_val = sorted_src[-1]
            insights.append(f"최저 전환 소스: {worst_src} (CVR {worst_val['cvr']})")
    return insights


def parse_csv(file_obj) -> List[Dict[str, Any]]:
    """CSV(stage,users[,source])를 파싱한다."""
    reader = csv.DictReader(file_obj)
    rows: List[Dict[str, Any]] = []
    for row in reader:
        entry: Dict[str, Any] = {"stage": row["stage"].strip(), "users": int(row["users"].strip())}
        entry["source"] = row["source"].strip() if "source" in row else None
        rows.append(entry)
    return rows


def build_report(
    stage_data: List[Tuple[str, int]],
    source_data: List[Tuple[str, int, str]],
) -> Dict[str, Any]:
    """퍼널 + 소스 데이터로 최종 리포트 딕셔너리를 생성한다."""
    funnel = calculate_funnel(stage_data)
    sources = analyze_ai_sources(source_data)
    return {"funnel": funnel, "ai_source_breakdown": sources, "insights": generate_insights(funnel, sources)}


def _fetch_ga4_funnel(property_id: str, date_range: str = "30d") -> List[Tuple[str, int]]:
    """GA4 API로 퍼널 단계별 사용자 수를 수집한다 (옵셔널)."""
    if not _GA4_AVAILABLE or not is_ga4_configured():
        return []
    days = date_range.rstrip("d")
    try:
        client = BetaAnalyticsDataClient()  # type: ignore[possibly-undefined]
        request = RunReportRequest(  # type: ignore[possibly-undefined]
            property=f"properties/{property_id}",
            dimensions=[Dimension(name="eventName")],  # type: ignore[possibly-undefined]
            metrics=[Metric(name="eventCount")],  # type: ignore[possibly-undefined]
            date_ranges=[DateRange(start_date=f"{days}daysAgo", end_date="today")],  # type: ignore[possibly-undefined]
        )
        response = client.run_report(request)
        stage_map = {row.dimension_values[0].value: int(row.metric_values[0].value) for row in response.rows}
        return [(s, stage_map[s]) for s in FUNNEL_STAGES if s in stage_map]
    except Exception:
        return []


def _render_markdown(report: Dict[str, Any]) -> str:
    """리포트를 마크다운 형식으로 렌더링한다."""
    lines = [
        "# AI 유입 전환 퍼널 분석",
        "",
        "## 퍼널 단계별 현황",
        "| 단계 | 사용자 | 잔존율 | 드롭오프 |",
        "|------|--------|--------|----------|",
    ]
    for s in report["funnel"]:
        lines.append(f"| {s['stage']} | {s['users']:,} | {s['rate']} | {s.get('dropoff', '-')} |")
    if report["ai_source_breakdown"]:
        lines += ["", "## AI 소스별 전환율", "| 소스 | 유입 | 전환 | CVR |", "|------|------|------|-----|"]
        for src, val in report["ai_source_breakdown"].items():
            lines.append(f"| {src} | {val['arrivals']:,} | {val['conversions']:,} | {val['cvr']} |")
    if report["insights"]:
        lines += ["", "## 인사이트"]
        lines += [f"- {i}" for i in report["insights"]]
    return "\n".join(lines)


def main() -> None:
    parser = argparse.ArgumentParser(description="AI 유입 전환 퍼널 분석기")
    parser.add_argument("--property-id", help="GA4 Property ID")
    parser.add_argument("--date-range", default="30d", help="날짜 범위 (예: 30d)")
    parser.add_argument("--conversion-event", default="전환", help="전환 이벤트명")
    parser.add_argument("--events-csv", help="CSV 파일 경로 (수동 모드)")
    parser.add_argument("--output", help="출력 파일 경로 (.md 또는 .json)")
    args = parser.parse_args()

    stage_data: List[Tuple[str, int]] = []
    source_data: List[Tuple[str, int, str]] = []

    if args.events_csv:
        with open(args.events_csv, encoding="utf-8") as f:
            rows = parse_csv(f)
        has_source = any(r.get("source") for r in rows)
        if has_source:
            source_data = [(r["stage"], r["users"], r["source"]) for r in rows]
            stage_totals: Dict[str, int] = {}
            for r in rows:
                stage_totals[r["stage"]] = stage_totals.get(r["stage"], 0) + r["users"]
            stage_data = [(s, stage_totals[s]) for s in FUNNEL_STAGES if s in stage_totals]
        else:
            stage_data = [(r["stage"], r["users"]) for r in rows]
    elif args.property_id:
        stage_data = _fetch_ga4_funnel(args.property_id, args.date_range)
        if not stage_data:
            print("[경고] GA4 API 연결 실패 또는 미설정. CSV 모드를 사용하세요.", file=sys.stderr)
            sys.exit(1)
    else:
        parser.print_help()
        sys.exit(1)

    report = build_report(stage_data, source_data)

    if args.output and args.output.endswith(".md"):
        with open(args.output, "w", encoding="utf-8") as f:
            f.write(_render_markdown(report))
        print(f"[완료] 마크다운 저장: {args.output}")
    elif args.output:
        with open(args.output, "w", encoding="utf-8") as f:
            json.dump(report, f, ensure_ascii=False, indent=2)
        print(f"[완료] JSON 저장: {args.output}")
    else:
        print(json.dumps(report, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    main()
