from __future__ import annotations

import json
from dataclasses import dataclass
from datetime import timedelta
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_account_anomalies"
TASK_NAME = "Sprawdzenie anomalii konta"


SCOPE = [
    {
        "area": "Okres porownania",
        "check": "Porownaj ostatnie 7 zakonczonych dni z poprzednimi 7 dniami, bez uzywania niepelnych danych z dzisiaj.",
    },
    {
        "area": "Metryki kampanii",
        "check": "Sprawdz koszt, klikniecia, wyswietlenia, konwersje, wartosc konwersji, CTR, CPC i ROAS na poziomie aktywnych kampanii.",
    },
    {
        "area": "Nagly spadek",
        "check": "Oznacz kampanie, w ktorych spadl ruch, koszt, konwersje, wartosc konwersji albo ROAS.",
    },
    {
        "area": "Nagly wzrost",
        "check": "Oznacz kampanie, w ktorych koszt, CPC albo ruch wzrosly szybciej niz wyniki.",
    },
    {
        "area": "Priorytet reakcji",
        "check": "Nadaj anomaliom poziom waznosci, aby agent mogl szybko zdecydowac, ktore kampanie sprawdzic jako pierwsze.",
    },
]


OUT_OF_SCOPE = [
    "zmiany budzetow i ocena pacingu budzetu",
    "zmiany strategii stawek oraz celow Docelowy ROAS/Docelowy CPA",
    "analiza zapytan uzytkownikow oraz wykluczen",
    "analiza reklam RSA, zasobow i kreacji",
    "wdrazanie zmian na koncie Google Ads",
]


@dataclass
class AccountAnomalyPlan:
    currency_code: str
    recent_period: dict
    previous_period: dict
    account_summary: list[dict]
    campaigns: list[dict]
    anomalies: 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,
            "currency_code": self.currency_code,
            "recent_period": self.recent_period,
            "previous_period": self.previous_period,
            "account_summary": self.account_summary,
            "campaigns": self.campaigns,
            "anomalies": self.anomalies,
            "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) -> "AccountAnomalyPlan":
        return cls(
            currency_code=data.get("currency_code", ""),
            recent_period=data.get("recent_period", {}),
            previous_period=data.get("previous_period", {}),
            account_summary=data.get("account_summary", []),
            campaigns=data.get("campaigns", []),
            anomalies=data.get("anomalies", []),
            scope=data.get("scope", []),
            out_of_scope=data.get("out_of_scope", []),
            knowledge_rules=data.get("knowledge_rules", []),
            warnings=data.get("warnings", []),
        )


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


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


def micros_to_amount(value: int | float) -> float:
    return round(float(value or 0) / 1_000_000, 2)


def format_money_micros(value: int | float, currency_code: str) -> str:
    suffix = f" {currency_code}" if currency_code else ""
    return f"{micros_to_amount(value):.2f}{suffix}"


def format_money_amount(value: int | float, currency_code: str) -> str:
    suffix = f" {currency_code}" if currency_code else ""
    return f"{float(value or 0):.2f}{suffix}"


def format_number(value: int | float, decimals: int = 0) -> str:
    if decimals <= 0:
        return str(int(round(float(value or 0))))
    return f"{float(value or 0):.{decimals}f}"


def format_percent_value(value: int | float) -> str:
    return f"{float(value or 0):.1f}%"


def format_change(value: float | None) -> str:
    if value is None:
        return "nowe dane"
    return f"{value:+.1f}%"


def pct_change(previous: float, recent: float) -> float | None:
    if previous == 0:
        return None
    return round(((recent - previous) / previous) * 100, 1)


def percent(numerator: int | float, denominator: int | float) -> float:
    if not denominator:
        return 0.0
    return round((float(numerator) / float(denominator)) * 100, 2)


def empty_metrics() -> dict:
    return {
        "cost_micros": 0,
        "clicks": 0,
        "impressions": 0,
        "conversions": 0.0,
        "conversion_value": 0.0,
    }


def add_metrics(target: dict, metrics: Any) -> None:
    target["cost_micros"] += int(metrics.cost_micros or 0)
    target["clicks"] += int(metrics.clicks or 0)
    target["impressions"] += int(metrics.impressions or 0)
    target["conversions"] += float(metrics.conversions or 0)
    target["conversion_value"] += float(metrics.conversions_value or 0)


def derived_metrics(metrics: dict) -> dict:
    cost_amount = micros_to_amount(metrics["cost_micros"])
    clicks = metrics["clicks"]
    impressions = metrics["impressions"]
    conversions = metrics["conversions"]
    conversion_value = metrics["conversion_value"]
    return {
        **metrics,
        "cost_amount": cost_amount,
        "ctr_percent": percent(clicks, impressions),
        "avg_cpc_micros": int(metrics["cost_micros"] / clicks) if clicks else 0,
        "conversion_rate_percent": percent(conversions, clicks),
        "roas": round(conversion_value / cost_amount, 2) if cost_amount else 0.0,
    }


def period_labels() -> tuple[dict, dict]:
    today = now_local().date()
    recent_end = today - timedelta(days=1)
    recent_start = recent_end - timedelta(days=6)
    previous_end = recent_start - timedelta(days=1)
    previous_start = previous_end - timedelta(days=6)
    return (
        {
            "label": "ostatnie 7 zakonczonych dni",
            "start": recent_start.isoformat(),
            "end": recent_end.isoformat(),
        },
        {
            "label": "poprzednie 7 dni",
            "start": previous_start.isoformat(),
            "end": previous_end.isoformat(),
        },
    )


def fetch_currency_code(google_client, customer_id: str) -> str:
    rows = run_query(
        google_client,
        customer_id,
        """
        SELECT
          customer.currency_code
        FROM customer
        """,
    )
    if not rows:
        return ""
    return str(rows[0].customer.currency_code or "")


def fetch_campaign_period_metrics(client_config: ClientConfig) -> tuple[str, dict, dict, list[dict]]:
    google_client = get_google_ads_client(use_proto_plus=True)
    customer_id = client_config.safe_customer_id
    currency_code = fetch_currency_code(google_client, customer_id)
    recent_period, previous_period = period_labels()
    rows = run_query(
        google_client,
        customer_id,
        f"""
        SELECT
          campaign.id,
          campaign.name,
          campaign.status,
          campaign.advertising_channel_type,
          segments.date,
          metrics.cost_micros,
          metrics.clicks,
          metrics.impressions,
          metrics.conversions,
          metrics.conversions_value
        FROM campaign
        WHERE campaign.status = 'ENABLED'
          AND segments.date BETWEEN '{previous_period["start"]}' AND '{recent_period["end"]}'
        """,
    )

    campaigns: dict[str, dict] = {}
    for row in rows:
        campaign = row.campaign
        campaign_id = str(campaign.id)
        record = campaigns.setdefault(
            campaign_id,
            {
                "campaign_id": campaign_id,
                "campaign_name": campaign.name,
                "status": enum_name(campaign.status),
                "channel_type": enum_name(campaign.advertising_channel_type),
                "recent": empty_metrics(),
                "previous": empty_metrics(),
            },
        )
        row_date = str(row.segments.date)
        if recent_period["start"] <= row_date <= recent_period["end"]:
            add_metrics(record["recent"], row.metrics)
        elif previous_period["start"] <= row_date <= previous_period["end"]:
            add_metrics(record["previous"], row.metrics)

    campaign_rows = []
    for record in campaigns.values():
        recent = derived_metrics(record["recent"])
        previous = derived_metrics(record["previous"])
        campaign_rows.append(
            {
                "campaign_id": record["campaign_id"],
                "campaign_name": record["campaign_name"],
                "status": record["status"],
                "channel_type": record["channel_type"],
                "recent": recent,
                "previous": previous,
                "changes": metric_changes(previous, recent),
            }
        )
    campaign_rows.sort(key=lambda row: (-row["recent"]["cost_micros"], row["campaign_name"]))
    return currency_code, recent_period, previous_period, campaign_rows


def metric_changes(previous: dict, recent: dict) -> dict:
    return {
        "cost": pct_change(previous["cost_micros"], recent["cost_micros"]),
        "clicks": pct_change(previous["clicks"], recent["clicks"]),
        "impressions": pct_change(previous["impressions"], recent["impressions"]),
        "conversions": pct_change(previous["conversions"], recent["conversions"]),
        "conversion_value": pct_change(previous["conversion_value"], recent["conversion_value"]),
        "ctr": pct_change(previous["ctr_percent"], recent["ctr_percent"]),
        "avg_cpc": pct_change(previous["avg_cpc_micros"], recent["avg_cpc_micros"]),
        "roas": pct_change(previous["roas"], recent["roas"]),
    }


def aggregate_account_summary(campaigns: list[dict], currency_code: str) -> list[dict]:
    recent = empty_metrics()
    previous = empty_metrics()
    for campaign in campaigns:
        for key in recent:
            recent[key] += campaign["recent"][key]
            previous[key] += campaign["previous"][key]
    recent = derived_metrics(recent)
    previous = derived_metrics(previous)
    return [
        summary_row("Koszt", previous["cost_micros"], recent["cost_micros"], "money_micros", currency_code),
        summary_row("Klikniecia", previous["clicks"], recent["clicks"], "number", currency_code),
        summary_row("Wyswietlenia", previous["impressions"], recent["impressions"], "number", currency_code),
        summary_row("Konwersje", previous["conversions"], recent["conversions"], "decimal", currency_code),
        summary_row(
            "Wartosc konwersji",
            previous["conversion_value"],
            recent["conversion_value"],
            "money_amount",
            currency_code,
        ),
        summary_row("CTR", previous["ctr_percent"], recent["ctr_percent"], "percent", currency_code),
        summary_row("Sredni CPC", previous["avg_cpc_micros"], recent["avg_cpc_micros"], "money_micros", currency_code),
        summary_row("ROAS", previous["roas"], recent["roas"], "decimal", currency_code),
    ]


def summary_row(label: str, previous: float, recent: float, value_type: str, currency_code: str) -> dict:
    return {
        "metric": label,
        "previous": display_metric(previous, value_type, currency_code),
        "recent": display_metric(recent, value_type, currency_code),
        "change_percent": format_change(pct_change(previous, recent)),
    }


def display_metric(value: float, value_type: str, currency_code: str) -> str:
    if value_type == "money_micros":
        return format_money_micros(value, currency_code)
    if value_type == "money_amount":
        return format_money_amount(value, currency_code)
    if value_type == "percent":
        return format_percent_value(value)
    if value_type == "decimal":
        return format_number(value, 2)
    return format_number(value)


def anomaly(
    campaign: dict,
    metric: str,
    severity: str,
    previous_value: str,
    recent_value: str,
    change_percent: str,
    reason: str,
    recommendation: str,
) -> dict:
    return {
        "campaign_id": campaign["campaign_id"],
        "campaign_name": campaign["campaign_name"],
        "channel_type": campaign["channel_type"],
        "status": campaign["status"],
        "metric": metric,
        "severity": severity,
        "previous_value": previous_value,
        "recent_value": recent_value,
        "change_percent": change_percent,
        "reason": reason,
        "recommendation": recommendation,
    }


def build_campaign_anomalies(campaign: dict, currency_code: str) -> list[dict]:
    recent = campaign["recent"]
    previous = campaign["previous"]
    changes = campaign["changes"]
    found = []

    if previous["cost_micros"] >= 50_000_000 and recent["cost_micros"] == 0:
        found.append(
            anomaly(
                campaign,
                "koszt",
                "wysokie",
                format_money_micros(previous["cost_micros"], currency_code),
                format_money_micros(recent["cost_micros"], currency_code),
                "-100.0%",
                "kampania miala koszt w poprzednim okresie i nie ma kosztu w ostatnich 7 dniach",
                "sprawdz status kampanii, budzet, odrzucenia, pomiar i ostatnie zmiany",
            )
        )
    elif previous["cost_micros"] == 0 and recent["cost_micros"] >= 50_000_000:
        found.append(
            anomaly(
                campaign,
                "koszt",
                "niskie",
                format_money_micros(previous["cost_micros"], currency_code),
                format_money_micros(recent["cost_micros"], currency_code),
                "nowe dane",
                "kampania zaczela wydawac srodki po okresie bez kosztu",
                "sprawdz, czy start kampanii byl planowany i czy wyniki sa akceptowalne",
            )
        )

    if changes["cost"] is not None and previous["cost_micros"] >= 50_000_000:
        if changes["cost"] >= 60:
            found.append(
                anomaly(
                    campaign,
                    "koszt",
                    "srednie",
                    format_money_micros(previous["cost_micros"], currency_code),
                    format_money_micros(recent["cost_micros"], currency_code),
                    format_change(changes["cost"]),
                    "koszt wzrosl szybciej niz typowy tygodniowy prog alarmowy",
                    "sprawdz budzet, strategie stawek i zmiany ruchu w osobnych zadaniach",
                )
            )
        elif changes["cost"] <= -50:
            found.append(
                anomaly(
                    campaign,
                    "koszt",
                    "srednie",
                    format_money_micros(previous["cost_micros"], currency_code),
                    format_money_micros(recent["cost_micros"], currency_code),
                    format_change(changes["cost"]),
                    "koszt spadl o co najmniej polowe tydzien do tygodnia",
                    "sprawdz, czy spadek wynika z decyzji, limitow, odrzucen albo utraty ruchu",
                )
            )

    if changes["clicks"] is not None and previous["clicks"] >= 20:
        if changes["clicks"] <= -45:
            found.append(
                anomaly(
                    campaign,
                    "klikniecia",
                    "srednie",
                    format_number(previous["clicks"]),
                    format_number(recent["clicks"]),
                    format_change(changes["clicks"]),
                    "klikniecia spadly mocno przy sensownym wolumenie bazowym",
                    "sprawdz aukcje, status kampanii, budzet i zapytania w zadaniach szczegolowych",
                )
            )
        elif changes["clicks"] >= 80:
            found.append(
                anomaly(
                    campaign,
                    "klikniecia",
                    "niskie",
                    format_number(previous["clicks"]),
                    format_number(recent["clicks"]),
                    format_change(changes["clicks"]),
                    "klikniecia wzrosly bardzo mocno tydzien do tygodnia",
                    "sprawdz, czy wzrost jest jakosciowy i nie wynika z niepasujacego ruchu",
                )
            )

    if changes["conversions"] is not None and previous["conversions"] >= 3:
        if recent["conversions"] == 0:
            severity = "wysokie"
        else:
            severity = "srednie"
        if changes["conversions"] <= -50:
            found.append(
                anomaly(
                    campaign,
                    "konwersje",
                    severity,
                    format_number(previous["conversions"], 2),
                    format_number(recent["conversions"], 2),
                    format_change(changes["conversions"]),
                    "konwersje spadly o co najmniej polowe",
                    "sprawdz pomiar konwersji, ruch i ostatnie zmiany w kampanii",
                )
            )
        elif changes["conversions"] >= 80:
            found.append(
                anomaly(
                    campaign,
                    "konwersje",
                    "niskie",
                    format_number(previous["conversions"], 2),
                    format_number(recent["conversions"], 2),
                    format_change(changes["conversions"]),
                    "konwersje wzrosly bardzo mocno tydzien do tygodnia",
                    "sprawdz, czy wzrost jest rzeczywisty i czy nie zmienil sie pomiar",
                )
            )

    if changes["conversion_value"] is not None and previous["conversion_value"] >= 100:
        if changes["conversion_value"] <= -50:
            found.append(
                anomaly(
                    campaign,
                    "wartosc konwersji",
                    "wysokie",
                    format_money_amount(previous["conversion_value"], currency_code),
                    format_money_amount(recent["conversion_value"], currency_code),
                    format_change(changes["conversion_value"]),
                    "wartosc konwersji spadla o co najmniej polowe",
                    "sprawdz pomiar, koszyk, kampanie produktowe i jakosc ruchu",
                )
            )
        elif changes["conversion_value"] >= 100:
            found.append(
                anomaly(
                    campaign,
                    "wartosc konwersji",
                    "niskie",
                    format_money_amount(previous["conversion_value"], currency_code),
                    format_money_amount(recent["conversion_value"], currency_code),
                    format_change(changes["conversion_value"]),
                    "wartosc konwersji wzrosla ponad dwukrotnie",
                    "sprawdz, czy wzrost wynika z realnej sprzedazy, a nie zmiany pomiaru",
                )
            )

    if changes["ctr"] is not None and previous["impressions"] >= 500 and previous["ctr_percent"] >= 1:
        if changes["ctr"] <= -35:
            found.append(
                anomaly(
                    campaign,
                    "CTR",
                    "srednie",
                    format_percent_value(previous["ctr_percent"]),
                    format_percent_value(recent["ctr_percent"]),
                    format_change(changes["ctr"]),
                    "CTR spadl przy wystarczajacej liczbie wyswietlen",
                    "sprawdz dopasowanie ruchu, reklamy i zmiany konkurencji",
                )
            )

    if changes["avg_cpc"] is not None and previous["clicks"] >= 20:
        if changes["avg_cpc"] >= 60:
            found.append(
                anomaly(
                    campaign,
                    "sredni CPC",
                    "srednie",
                    format_money_micros(previous["avg_cpc_micros"], currency_code),
                    format_money_micros(recent["avg_cpc_micros"], currency_code),
                    format_change(changes["avg_cpc"]),
                    "sredni CPC wzrosl mocno tydzien do tygodnia",
                    "sprawdz strategie stawek, aukcje i segmenty ruchu",
                )
            )

    if changes["roas"] is not None and previous["cost_micros"] >= 50_000_000 and previous["roas"] >= 1:
        if changes["roas"] <= -40:
            found.append(
                anomaly(
                    campaign,
                    "ROAS",
                    "wysokie",
                    format_number(previous["roas"], 2),
                    format_number(recent["roas"], 2),
                    format_change(changes["roas"]),
                    "ROAS spadl przy istotnym koszcie bazowym",
                    "sprawdz rentownosc, pomiar, produkt/feed i strategie stawek w zadaniach szczegolowych",
                )
            )

    return found


def build_anomalies(campaigns: list[dict], currency_code: str) -> list[dict]:
    anomalies = []
    for campaign in campaigns:
        anomalies.extend(build_campaign_anomalies(campaign, currency_code))
    severity_order = {"wysokie": 0, "srednie": 1, "niskie": 2}
    anomalies.sort(
        key=lambda row: (
            severity_order.get(row["severity"], 9),
            row["campaign_name"],
            row["metric"],
        )
    )
    return anomalies


def compact_campaign_row(campaign: dict, currency_code: str) -> dict:
    previous = campaign["previous"]
    recent = campaign["recent"]
    return {
        "campaign_id": campaign["campaign_id"],
        "campaign_name": campaign["campaign_name"],
        "status": campaign["status"],
        "channel_type": campaign["channel_type"],
        "previous_cost": format_money_micros(previous["cost_micros"], currency_code),
        "recent_cost": format_money_micros(recent["cost_micros"], currency_code),
        "cost_change": format_change(campaign["changes"]["cost"]),
        "previous_clicks": format_number(previous["clicks"]),
        "recent_clicks": format_number(recent["clicks"]),
        "clicks_change": format_change(campaign["changes"]["clicks"]),
        "previous_conversions": format_number(previous["conversions"], 2),
        "recent_conversions": format_number(recent["conversions"], 2),
        "conversions_change": format_change(campaign["changes"]["conversions"]),
        "previous_roas": format_number(previous["roas"], 2),
        "recent_roas": format_number(recent["roas"], 2),
        "roas_change": format_change(campaign["changes"]["roas"]),
    }


def build_account_anomaly_plan(client_config: ClientConfig) -> AccountAnomalyPlan:
    warnings = []
    recent_period, previous_period = period_labels()
    try:
        currency_code, recent_period, previous_period, campaigns_raw = fetch_campaign_period_metrics(client_config)
    except Exception as exc:
        currency_code = ""
        campaigns_raw = []
        warnings.append(f"Nie udalo sie pobrac metryk kampanii z Google Ads API: {exc}")

    if not campaigns_raw:
        warnings.append("Nie znaleziono kampanii z danymi w analizowanym okresie albo nie udalo sie ich pobrac.")

    anomalies = build_anomalies(campaigns_raw, currency_code)
    if not anomalies and campaigns_raw:
        warnings.append("Nie wykryto anomalii wedlug obecnych progow. Wyniki nadal warto porownac z kontekstem klienta.")

    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 anomalii i alertow bedziemy dopisywac osobno po akceptacji uzytkownika."
        )

    campaigns = [compact_campaign_row(campaign, currency_code) for campaign in campaigns_raw]
    return AccountAnomalyPlan(
        currency_code=currency_code,
        recent_period=recent_period,
        previous_period=previous_period,
        account_summary=aggregate_account_summary(campaigns_raw, currency_code),
        campaigns=campaigns,
        anomalies=anomalies,
        scope=SCOPE,
        out_of_scope=OUT_OF_SCOPE,
        knowledge_rules=knowledge_rules,
        warnings=warnings,
    )


def save_account_anomaly_plan(domain: str, plan: AccountAnomalyPlan) -> 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 anomalii konta",
        "",
        f"Klient: {domain}",
        f"Utworzono: {ts.isoformat(timespec='seconds')}",
        "",
        "## Okresy",
        "",
        f"- Ostatnie 7 zakonczonych dni: {plan.recent_period.get('start', '')} - {plan.recent_period.get('end', '')}",
        f"- Poprzednie 7 dni: {plan.previous_period.get('start', '')} - {plan.previous_period.get('end', '')}",
        "",
        "## Podsumowanie",
        "",
        f"- Kampanie z danymi: {len(plan.campaigns)}",
        f"- Wykryte anomalie: {len(plan.anomalies)}",
        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.account_summary:
        lines.extend(
            [
                "## Podsumowanie konta",
                "",
                "| Metryka | Poprzednie 7 dni | Ostatnie 7 dni | Zmiana |",
                "| --- | --- | --- | --- |",
            ]
        )
        for row in plan.account_summary:
            lines.append(f"| {row['metric']} | {row['previous']} | {row['recent']} | {row['change_percent']} |")
        lines.append("")
    if plan.anomalies:
        lines.extend(
            [
                "## Wykryte anomalie",
                "",
                "| Waznosc | Kampania | Metryka | Poprzednio | Teraz | Zmiana | Powod | Rekomendacja |",
                "| --- | --- | --- | --- | --- | --- | --- | --- |",
            ]
        )
        for item in plan.anomalies:
            lines.append(
                f"| {item['severity']} | {md_cell(item['campaign_name'])} | {item['metric']} | "
                f"{item['previous_value']} | {item['recent_value']} | {item['change_percent']} | "
                f"{md_cell(item['reason'])} | {md_cell(item['recommendation'])} |"
            )
        lines.append("")
    if plan.campaigns:
        lines.extend(
            [
                "## Porownanie kampanii",
                "",
                "| Kampania | Typ | Status | Koszt poprzednio | Koszt teraz | Zmiana kosztu | Konwersje poprzednio | Konwersje teraz | Zmiana konwersji | ROAS poprzednio | ROAS teraz | Zmiana ROAS |",
                "| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |",
            ]
        )
        for campaign in plan.campaigns:
            lines.append(
                f"| {md_cell(campaign['campaign_name'])} | {campaign['channel_type']} | {campaign['status']} | "
                f"{campaign['previous_cost']} | {campaign['recent_cost']} | {campaign['cost_change']} | "
                f"{campaign['previous_conversions']} | {campaign['recent_conversions']} | {campaign['conversions_change']} | "
                f"{campaign['previous_roas']} | {campaign['recent_roas']} | {campaign['roas_change']} |"
            )
        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_account_anomaly_plan(plan: AccountAnomalyPlan) -> None:
    print("\nPlan sprawdzenia anomalii konta")
    print_table(
        ["Metryka", "Liczba"],
        [
            ["Kampanie z danymi", str(len(plan.campaigns))],
            ["Wykryte anomalie", str(len(plan.anomalies))],
            ["Reguly wiedzy", str(len(plan.knowledge_rules))],
            ["Zmiany do wdrozenia", "0"],
        ],
    )
    print("\nOkresy porownania")
    print_table(
        ["Okres", "Od", "Do"],
        [
            [plan.previous_period.get("label", "poprzednie 7 dni"), plan.previous_period.get("start", ""), plan.previous_period.get("end", "")],
            [plan.recent_period.get("label", "ostatnie 7 zakonczonych dni"), plan.recent_period.get("start", ""), plan.recent_period.get("end", "")],
        ],
    )
    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.account_summary:
        print("\nPodsumowanie konta")
        print_table(
            ["Metryka", "Poprzednie 7 dni", "Ostatnie 7 dni", "Zmiana"],
            [[row["metric"], row["previous"], row["recent"], row["change_percent"]] for row in plan.account_summary],
        )
    if plan.anomalies:
        print("\nWykryte anomalie")
        print_table(
            ["Nr", "Waznosc", "Kampania", "Metryka", "Poprzednio", "Teraz", "Zmiana", "Powod"],
            [
                [
                    str(index),
                    item["severity"],
                    item["campaign_name"],
                    item["metric"],
                    item["previous_value"],
                    item["recent_value"],
                    item["change_percent"],
                    item["reason"],
                ]
                for index, item in enumerate(plan.anomalies[:30], 1)
            ],
        )
        if len(plan.anomalies) > 30:
            print(f"... oraz {len(plan.anomalies) - 30} kolejnych anomalii w pliku planu")
    if plan.campaigns:
        print("\nPorownanie kampanii")
        print_table(
            ["Nr", "Kampania", "Typ", "Koszt poprz.", "Koszt teraz", "Zmiana", "Konw. poprz.", "Konw. teraz", "ROAS teraz"],
            [
                [
                    str(index),
                    campaign["campaign_name"],
                    campaign["channel_type"],
                    campaign["previous_cost"],
                    campaign["recent_cost"],
                    campaign["cost_change"],
                    campaign["previous_conversions"],
                    campaign["recent_conversions"],
                    campaign["recent_roas"],
                ]
                for index, campaign in enumerate(plan.campaigns[:30], 1)
            ],
        )
        if len(plan.campaigns) > 30:
            print(f"... oraz {len(plan.campaigns) - 30} kolejnych kampanii 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_account_anomaly_plan(
    client_config: ClientConfig,
    plan: AccountAnomalyPlan,
    show_navigation: bool = True,
) -> None:
    print("\nTo zadanie jest audytem anomalii 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(item["campaign_name"] for item in plan.anomalies[:10]),
            "summary": {
                "campaigns": len(plan.campaigns),
                "anomalies": len(plan.anomalies),
                "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_account_anomalies(
    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 = AccountAnomalyPlan.from_dict(plan_data)
        print_account_anomaly_plan(plan)
        apply_account_anomaly_plan(client_config, plan, show_navigation=show_navigation)
        return

    print(f"\nKlient: {client_config.domain}")
    print("Przygotowuje plan sprawdzenia anomalii konta...")
    plan = build_account_anomaly_plan(client_config)
    print_account_anomaly_plan(plan)
    json_path, md_path = save_account_anomaly_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(item["campaign_name"] for item in plan.anomalies[:10]),
            "summary": {
                "campaigns": len(plan.campaigns),
                "anomalies": len(plan.anomalies),
                "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 anomalii.")
    if show_navigation:
        print_next_navigation(client_config.domain)
