from __future__ import annotations

import json
from collections import Counter
from dataclasses import dataclass
from pathlib import Path
from typing import Any

from ..config import ClientConfig, client_dir
from ..google_ads import get_google_ads_client, run_query
from ..history import append_change_markdown, append_history, now_local
from ..knowledge.store import rules_for_task
from ..table import print_table


TASK_ID = "check_auction_insights"
TASK_NAME = "Sprawdzenie Auction Insights"


SCOPE = [
    {
        "area": "Konkurenci w aukcji",
        "check": "Pobierz domeny konkurentow z segmentu Auction Insights, jezeli developer token ma dostep do tych metryk.",
    },
    {
        "area": "Overlap i outranking",
        "check": "Pokaz overlap rate, position above rate i outranking share dla konkurentow.",
    },
    {
        "area": "Widocznosc top",
        "check": "Pokaz top rate i absolute top rate jako sygnal presji konkurencyjnej.",
    },
    {
        "area": "Granica zadania",
        "check": "Nie podejmuj tutaj decyzji o budzecie, stawkach ani strategiach; to tylko diagnoza aukcji.",
    },
]


OUT_OF_SCOPE = [
    "zmiany budzetow",
    "zmiany stawek i strategii ustalania stawek",
    "decyzje o zmianie Docelowego ROAS albo Docelowego CPA",
    "analiza zapytan uzytkownikow",
    "wdrazanie zmian na koncie Google Ads",
]


@dataclass
class AuctionInsightsPlan:
    competitors: list[dict]
    campaign_summary: list[dict]
    domain_summary: list[dict]
    scope: list[dict]
    out_of_scope: list[str]
    knowledge_rules: list[dict]
    warnings: list[str]

    def to_dict(self) -> dict:
        return {
            "task": TASK_ID,
            "task_name": TASK_NAME,
            "competitors": self.competitors,
            "campaign_summary": self.campaign_summary,
            "domain_summary": self.domain_summary,
            "scope": self.scope,
            "out_of_scope": self.out_of_scope,
            "knowledge_rules": self.knowledge_rules,
            "warnings": self.warnings,
            "changes": [],
        }

    @classmethod
    def from_dict(cls, data: dict) -> "AuctionInsightsPlan":
        return cls(
            competitors=data.get("competitors", []),
            campaign_summary=data.get("campaign_summary", []),
            domain_summary=data.get("domain_summary", []),
            scope=data.get("scope", []),
            out_of_scope=data.get("out_of_scope", []),
            knowledge_rules=data.get("knowledge_rules", []),
            warnings=data.get("warnings", []),
        )


def md_cell(value: Any) -> str:
    return str(value or "").replace("|", "\\|").replace("\n", " ").strip()


def enum_name(value: Any) -> str:
    name = getattr(value, "name", None)
    if name:
        return name
    return str(value)


def percent(value: float | int | None) -> float:
    if value is None:
        return 0.0
    return round(float(value or 0) * 100, 1)


def percent_label(value: float | int | None) -> str:
    return f"{percent(value):.1f}%"


def compact_api_error(exc: Exception) -> str:
    message = str(exc)
    request_id = ""
    if "request_id:" in message:
        request_id = message.split("request_id:", 1)[1].splitlines()[0].strip().strip('"')
    if "METRIC_ACCESS_DENIED" in message:
        base = "METRIC_ACCESS_DENIED: developer token nie ma dostepu do metryk Auction Insights"
    elif "PERMISSION_DENIED" in message:
        base = "PERMISSION_DENIED: brak uprawnien do pobrania danych Auction Insights"
    else:
        base = "Nie udalo sie pobrac danych Auction Insights z Google Ads API"
    if request_id:
        return f"{base}. Request ID: {request_id}."
    return f"{base}."


def auction_severity(row: dict) -> str:
    if row["position_above_rate"] >= 0.5 or row["outranking_share"] <= 0.2:
        return "wysokie"
    if row["overlap_rate"] >= 0.5 or row["position_above_rate"] >= 0.3:
        return "srednie"
    return "niskie"


def auction_flags(row: dict) -> list[str]:
    flags = []
    if row["overlap_rate"] >= 0.5:
        flags.append("wysoki overlap")
    if row["position_above_rate"] >= 0.5:
        flags.append("konkurent czesto wyzej")
    elif row["position_above_rate"] >= 0.3:
        flags.append("pozycja konkurenta do oceny")
    if row["outranking_share"] <= 0.2:
        flags.append("niski outranking share")
    return flags or ["ok"]


def fetch_auction_insights(client_config: ClientConfig) -> list[dict]:
    google_client = get_google_ads_client(use_proto_plus=True)
    rows = run_query(
        google_client,
        client_config.safe_customer_id,
        """
        SELECT
          campaign.id,
          campaign.name,
          campaign.status,
          campaign.advertising_channel_type,
          segments.auction_insight_domain,
          metrics.auction_insight_search_impression_share,
          metrics.auction_insight_search_overlap_rate,
          metrics.auction_insight_search_position_above_rate,
          metrics.auction_insight_search_outranking_share,
          metrics.auction_insight_search_top_impression_percentage,
          metrics.auction_insight_search_absolute_top_impression_percentage
        FROM campaign
        WHERE campaign.status != 'REMOVED'
          AND segments.date DURING LAST_30_DAYS
        LIMIT 1000
        """,
    )

    competitors = []
    for row in rows:
        domain = str(row.segments.auction_insight_domain or "").strip()
        if not domain:
            continue
        metrics = row.metrics
        record = {
            "campaign_id": str(row.campaign.id),
            "campaign_name": row.campaign.name,
            "status": enum_name(row.campaign.status),
            "channel_type": enum_name(row.campaign.advertising_channel_type),
            "domain": domain,
            "impression_share": float(metrics.auction_insight_search_impression_share or 0),
            "overlap_rate": float(metrics.auction_insight_search_overlap_rate or 0),
            "position_above_rate": float(metrics.auction_insight_search_position_above_rate or 0),
            "outranking_share": float(metrics.auction_insight_search_outranking_share or 0),
            "top_rate": float(metrics.auction_insight_search_top_impression_percentage or 0),
            "absolute_top_rate": float(metrics.auction_insight_search_absolute_top_impression_percentage or 0),
        }
        record["severity"] = auction_severity(record)
        record["flags"] = auction_flags(record)
        competitors.append(record)
    severity_order = {"wysokie": 0, "srednie": 1, "niskie": 2, "ok": 9}
    competitors.sort(
        key=lambda row: (
            severity_order.get(row["severity"], 9),
            row["campaign_name"],
            -row["overlap_rate"],
            row["domain"],
        )
    )
    return competitors


def build_campaign_summary(competitors: list[dict]) -> list[dict]:
    buckets: dict[str, dict] = {}
    for item in competitors:
        row = buckets.setdefault(
            item["campaign_name"],
            {
                "campaign_name": item["campaign_name"],
                "channel_type": item["channel_type"],
                "competitors": 0,
                "high_risk": 0,
                "max_overlap_rate": 0.0,
                "max_position_above_rate": 0.0,
            },
        )
        row["competitors"] += 1
        if item["severity"] == "wysokie":
            row["high_risk"] += 1
        row["max_overlap_rate"] = max(row["max_overlap_rate"], item["overlap_rate"])
        row["max_position_above_rate"] = max(row["max_position_above_rate"], item["position_above_rate"])
    return sorted(buckets.values(), key=lambda row: (-row["high_risk"], -row["competitors"], row["campaign_name"]))


def build_domain_summary(competitors: list[dict]) -> list[dict]:
    counter = Counter(item["domain"] for item in competitors)
    return [{"domain": key, "campaigns": value} for key, value in counter.most_common()]


def build_auction_insights_plan(client_config: ClientConfig) -> AuctionInsightsPlan:
    warnings = []
    try:
        competitors = fetch_auction_insights(client_config)
    except Exception as exc:
        competitors = []
        message = str(exc)
        if "METRIC_ACCESS_DENIED" in message or "auction_insight" in message:
            warnings.append(
                "Google Ads API zwrocilo brak dostepu do metryk Auction Insights dla obecnego developer tokena. "
                "Zadanie jest gotowe, ale zacznie zwracac konkurentow dopiero po odblokowaniu tych metryk."
            )
        warnings.append(compact_api_error(exc))

    if not competitors:
        warnings.append("Nie znaleziono danych Auction Insights albo nie udalo sie ich pobrac.")
    warnings.append(
        "To zadanie tylko diagnozuje presje konkurencji w aukcji. Decyzje o budzetach, stawkach i strategiach pozostaja w osobnych zadaniach."
    )

    knowledge_rules = [
        {
            "id": rule.id,
            "topic": rule.topic,
            "rule_type": rule.rule_type,
            "condition": rule.condition,
            "recommendation": rule.recommendation,
            "risk": rule.risk,
            "source": rule.source,
        }
        for rule in rules_for_task(TASK_ID)
    ]
    if not knowledge_rules:
        warnings.append(
            "Do tego zadania nie przypisano jeszcze regul z bazy wiedzy. "
            "Reguly dotyczace Auction Insights bedziemy dopisywac osobno po akceptacji uzytkownika."
        )

    return AuctionInsightsPlan(
        competitors=competitors,
        campaign_summary=build_campaign_summary(competitors),
        domain_summary=build_domain_summary(competitors),
        scope=SCOPE,
        out_of_scope=OUT_OF_SCOPE,
        knowledge_rules=knowledge_rules,
        warnings=warnings,
    )


def save_auction_insights_plan(domain: str, plan: AuctionInsightsPlan) -> tuple[Path, Path]:
    ts = now_local()
    base = client_dir(domain) / "plans"
    base.mkdir(parents=True, exist_ok=True)
    stem = f"{ts.strftime('%Y-%m-%d_%H-%M-%S')}_{TASK_ID}"
    json_path = base / f"{stem}.json"
    md_path = base / f"{stem}.md"
    payload = {
        "created_at": ts.isoformat(timespec="seconds"),
        "client": domain,
        **plan.to_dict(),
    }
    json_path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")

    lines = [
        "# Plan: Sprawdzenie Auction Insights",
        "",
        f"Klient: {domain}",
        f"Utworzono: {ts.isoformat(timespec='seconds')}",
        "",
        "## Podsumowanie",
        "",
        f"- Rekordy konkurentow: {len(plan.competitors)}",
        f"- Kampanie z konkurentami: {len(plan.campaign_summary)}",
        f"- Domeny konkurentow: {len(plan.domain_summary)}",
        f"- Reguly wiedzy przypisane do zadania: {len(plan.knowledge_rules)}",
        "- Zmiany do wdrozenia: 0",
        "",
    ]
    if plan.warnings:
        lines.extend(["## Uwagi", ""])
        lines.extend(f"- {item}" for item in plan.warnings)
        lines.append("")
    lines.extend(["## Zakres zadania", "", "| Obszar | Co sprawdzic |", "| --- | --- |"])
    for row in plan.scope:
        lines.append(f"| {md_cell(row.get('area', ''))} | {md_cell(row.get('check', ''))} |")
    lines.append("")
    lines.extend(["## Poza zakresem tego zadania", ""])
    lines.extend(f"- {item}" for item in plan.out_of_scope)
    lines.append("")
    if plan.campaign_summary:
        lines.extend(
            [
                "## Podsumowanie po kampaniach",
                "",
                "| Kampania | Typ | Konkurenci | Wysokie ryzyko | Max overlap | Max position above |",
                "| --- | --- | --- | --- | --- | --- |",
            ]
        )
        for row in plan.campaign_summary:
            lines.append(
                f"| {md_cell(row['campaign_name'])} | {row['channel_type']} | {row['competitors']} | "
                f"{row['high_risk']} | {percent_label(row['max_overlap_rate'])} | {percent_label(row['max_position_above_rate'])} |"
            )
        lines.append("")
    if plan.domain_summary:
        lines.extend(["## Domeny konkurentow", "", "| Domena | Kampanie |", "| --- | --- |"])
        for row in plan.domain_summary:
            lines.append(f"| {md_cell(row['domain'])} | {row['campaigns']} |")
        lines.append("")
    if plan.competitors:
        lines.extend(
            [
                "## Auction Insights",
                "",
                "| Waznosc | Kampania | Konkurent | Impression share | Overlap | Position above | Outranking | Top | Abs. top | Flagi |",
                "| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |",
            ]
        )
        for item in plan.competitors:
            lines.append(
                f"| {item['severity']} | {md_cell(item['campaign_name'])} | {md_cell(item['domain'])} | "
                f"{percent_label(item['impression_share'])} | {percent_label(item['overlap_rate'])} | "
                f"{percent_label(item['position_above_rate'])} | {percent_label(item['outranking_share'])} | "
                f"{percent_label(item['top_rate'])} | {percent_label(item['absolute_top_rate'])} | "
                f"{md_cell(', '.join(item['flags']))} |"
            )
        lines.append("")
    if plan.knowledge_rules:
        lines.extend(
            [
                "## Reguly z bazy wiedzy",
                "",
                "| ID | Temat | Rekomendacja | Ryzyko |",
                "| --- | --- | --- | --- |",
            ]
        )
        for rule in plan.knowledge_rules:
            lines.append(
                f"| {md_cell(rule.get('id', ''))} | {md_cell(rule.get('topic', ''))} | "
                f"{md_cell(rule.get('recommendation', ''))} | {md_cell(rule.get('risk', ''))} |"
            )
        lines.append("")
    md_path.write_text("\n".join(lines), encoding="utf-8")
    return json_path, md_path


def print_auction_insights_plan(plan: AuctionInsightsPlan) -> None:
    print("\nPlan sprawdzenia Auction Insights")
    print_table(
        ["Metryka", "Liczba"],
        [
            ["Rekordy konkurentow", str(len(plan.competitors))],
            ["Kampanie z konkurentami", str(len(plan.campaign_summary))],
            ["Domeny konkurentow", str(len(plan.domain_summary))],
            ["Reguly wiedzy", str(len(plan.knowledge_rules))],
            ["Zmiany do wdrozenia", "0"],
        ],
    )
    if plan.warnings:
        print("\nUwagi")
        print_table(["Nr", "Uwaga"], [[str(index), item] for index, item in enumerate(plan.warnings, 1)])
    print("\nZakres zadania")
    print_table(
        ["Nr", "Obszar", "Co sprawdzic"],
        [[str(index), row["area"], row["check"]] for index, row in enumerate(plan.scope, 1)],
    )
    print("\nPoza zakresem")
    print_table(["Nr", "Nie analizujemy tutaj"], [[str(index), item] for index, item in enumerate(plan.out_of_scope, 1)])
    if plan.campaign_summary:
        print("\nPodsumowanie po kampaniach")
        print_table(
            ["Kampania", "Typ", "Konkurenci", "Wysokie", "Max overlap", "Max above"],
            [
                [
                    row["campaign_name"],
                    row["channel_type"],
                    str(row["competitors"]),
                    str(row["high_risk"]),
                    percent_label(row["max_overlap_rate"]),
                    percent_label(row["max_position_above_rate"]),
                ]
                for row in plan.campaign_summary
            ],
        )
    if plan.domain_summary:
        print("\nDomeny konkurentow")
        print_table(["Domena", "Kampanie"], [[row["domain"], str(row["campaigns"])] for row in plan.domain_summary[:30]])
    if plan.competitors:
        print("\nAuction Insights")
        print_table(
            ["Nr", "Waznosc", "Kampania", "Konkurent", "Overlap", "Above", "Outranking", "Flagi"],
            [
                [
                    str(index),
                    item["severity"],
                    item["campaign_name"],
                    item["domain"],
                    percent_label(item["overlap_rate"]),
                    percent_label(item["position_above_rate"]),
                    percent_label(item["outranking_share"]),
                    ", ".join(item["flags"]),
                ]
                for index, item in enumerate(plan.competitors[:30], 1)
            ],
        )
        if len(plan.competitors) > 30:
            print(f"... oraz {len(plan.competitors) - 30} kolejnych rekordow w pliku planu")
    if plan.knowledge_rules:
        print("\nReguly z bazy wiedzy")
        print_table(
            ["Nr", "ID", "Temat", "Rekomendacja"],
            [
                [str(index), rule["id"], rule["topic"], rule["recommendation"]]
                for index, rule in enumerate(plan.knowledge_rules[:10], 1)
            ],
        )
        if len(plan.knowledge_rules) > 10:
            print(f"... oraz {len(plan.knowledge_rules) - 10} kolejnych regul")


def print_next_navigation(domain: str) -> None:
    print("\nCo dalej:")
    print(f"1. Lista zadan klienta {domain}")
    print("2. Lista klientow")
    print("3. Zakoncz")
    print("\nKomendy:")
    print(f"1 -> python gads.py analiza-klienta --client {domain}")
    print("2 -> python gads.py analiza-klienta")


def apply_auction_insights_plan(
    client_config: ClientConfig,
    plan: AuctionInsightsPlan,
    show_navigation: bool = True,
) -> None:
    print("\nTo zadanie jest audytem Auction Insights i nie wdraza zmian na koncie Google Ads.")
    changes_path = append_change_markdown(client_config.domain, TASK_NAME, [])
    history_path = append_history(
        client_config.domain,
        {
            "task": TASK_NAME,
            "status": "audyt oznaczony jako wykonany",
            "campaign": ", ".join(row["campaign_name"] for row in plan.campaign_summary[:10]),
            "summary": {
                "competitors": len(plan.competitors),
                "campaigns": len(plan.campaign_summary),
                "knowledge_rules": len(plan.knowledge_rules),
                "changes": 0,
            },
        },
    )
    print(f"Historia JSONL: {history_path}")
    print(f"Historia Markdown: {changes_path}")
    if show_navigation:
        print_next_navigation(client_config.domain)


def run_check_auction_insights(
    client_config: ClientConfig,
    global_rules: dict,
    plan_only: bool = False,
    apply_plan_path: str | None = None,
    confirm_apply: str | None = None,
    show_navigation: bool = True,
) -> None:
    _ = global_rules
    if apply_plan_path:
        if confirm_apply != "TAK":
            print("Do oznaczenia audytu jako wykonanego wymagane jest --confirm-apply TAK.")
            if show_navigation:
                print_next_navigation(client_config.domain)
            return
        plan_data = json.loads(Path(apply_plan_path).read_text(encoding="utf-8"))
        if plan_data.get("client") != client_config.domain:
            print(f"Plan jest dla klienta {plan_data.get('client')}, a wybrano {client_config.domain}.")
            if show_navigation:
                print_next_navigation(client_config.domain)
            return
        plan = AuctionInsightsPlan.from_dict(plan_data)
        print_auction_insights_plan(plan)
        apply_auction_insights_plan(client_config, plan, show_navigation=show_navigation)
        return

    print(f"\nKlient: {client_config.domain}")
    print("Przygotowuje plan sprawdzenia Auction Insights...")
    plan = build_auction_insights_plan(client_config)
    print_auction_insights_plan(plan)
    json_path, md_path = save_auction_insights_plan(client_config.domain, plan)
    print(f"\nPlan JSON: {json_path}")
    print(f"Plan Markdown: {md_path}")

    append_history(
        client_config.domain,
        {
            "task": TASK_NAME,
            "status": "plan przygotowany",
            "campaign": ", ".join(row["campaign_name"] for row in plan.campaign_summary[:10]),
            "summary": {
                "competitors": len(plan.competitors),
                "campaigns": len(plan.campaign_summary),
                "knowledge_rules": len(plan.knowledge_rules),
                "changes": 0,
            },
        },
    )

    if plan_only:
        print("\nTryb plan-only: zmiany nie zostaly wdrozone.")
        if show_navigation:
            print_next_navigation(client_config.domain)
        return

    print("\nBrak zmian do wdrozenia. To zadanie tworzy plan audytu Auction Insights.")
    if show_navigation:
        print_next_navigation(client_config.domain)
