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_ad_asset_statuses"
TASK_NAME = "Sprawdzenie statusow reklam i zasobow"


SCOPE = [
    {
        "area": "Reklamy",
        "check": "Pokaz status reklamy, typ reklamy i status zatwierdzenia polityk dla aktywnych grup reklam.",
    },
    {
        "area": "Odrzucenia i ograniczenia",
        "check": "Oznacz reklamy odrzucone, ograniczone, oczekujace albo wstrzymane jako elementy do szybkiej reakcji.",
    },
    {
        "area": "Zasoby PMax",
        "check": "Pokaz status zasobow w asset group, jezeli Google Ads API udostepnia te dane dla konta.",
    },
    {
        "area": "Audyt techniczny",
        "check": "Oddziel dostepnosc emisji od oceny jakosci tekstow, naglowkow i kreacji.",
    },
]


OUT_OF_SCOPE = [
    "analiza jakosci tekstow reklam i kreacji",
    "budzety i wykorzystanie budzetu",
    "strategie stawek oraz cele Docelowy ROAS/Docelowy CPA",
    "zapytania uzytkownikow oraz wykluczenia",
    "wdrazanie zmian reklam albo zasobow na koncie Google Ads",
]


PROBLEM_APPROVAL_STATUSES = {
    "DISAPPROVED",
    "AREA_OF_INTEREST_ONLY",
    "APPROVED_LIMITED",
    "UNDER_REVIEW",
    "UNKNOWN",
    "UNSPECIFIED",
}


PROBLEM_ENTITY_STATUSES = {
    "PAUSED",
    "DISABLED",
    "UNKNOWN",
    "UNSPECIFIED",
}


@dataclass
class AdAssetStatusPlan:
    ads: list[dict]
    assets: list[dict]
    ad_status_summary: list[dict]
    approval_summary: list[dict]
    asset_status_summary: list[dict]
    problem_items: 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,
            "ads": self.ads,
            "assets": self.assets,
            "ad_status_summary": self.ad_status_summary,
            "approval_summary": self.approval_summary,
            "asset_status_summary": self.asset_status_summary,
            "problem_items": self.problem_items,
            "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) -> "AdAssetStatusPlan":
        return cls(
            ads=data.get("ads", []),
            assets=data.get("assets", []),
            ad_status_summary=data.get("ad_status_summary", []),
            approval_summary=data.get("approval_summary", []),
            asset_status_summary=data.get("asset_status_summary", []),
            problem_items=data.get("problem_items", []),
            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 ad_label(row: dict) -> str:
    if row.get("ad_name"):
        return row["ad_name"]
    return f"{row['ad_type']} {row['ad_id']}"


def item_severity(status: str, approval_status: str) -> str:
    if approval_status == "DISAPPROVED":
        return "wysokie"
    if status in PROBLEM_ENTITY_STATUSES:
        return "srednie"
    if approval_status in {"APPROVED_LIMITED", "AREA_OF_INTEREST_ONLY"}:
        return "srednie"
    if approval_status == "UNDER_REVIEW":
        return "niskie"
    if approval_status in {"UNKNOWN", "UNSPECIFIED"}:
        return "niskie"
    return "ok"


def item_flags(status: str, approval_status: str) -> list[str]:
    flags = []
    if status in PROBLEM_ENTITY_STATUSES:
        flags.append(f"status: {status}")
    if approval_status in PROBLEM_APPROVAL_STATUSES:
        flags.append(f"polityka: {approval_status}")
    return flags or ["ok"]


def fetch_ads(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,
          ad_group.id,
          ad_group.name,
          ad_group.status,
          ad_group_ad.ad.id,
          ad_group_ad.ad.name,
          ad_group_ad.ad.type,
          ad_group_ad.status,
          ad_group_ad.policy_summary.approval_status
        FROM ad_group_ad
        WHERE campaign.status != 'REMOVED'
          AND ad_group.status != 'REMOVED'
          AND ad_group_ad.status != 'REMOVED'
        """,
    )

    ads = []
    for row in rows:
        ad = row.ad_group_ad.ad
        status = enum_name(row.ad_group_ad.status)
        approval_status = enum_name(row.ad_group_ad.policy_summary.approval_status)
        record = {
            "item_type": "reklama",
            "campaign_id": str(row.campaign.id),
            "campaign_name": row.campaign.name,
            "campaign_status": enum_name(row.campaign.status),
            "channel_type": enum_name(row.campaign.advertising_channel_type),
            "ad_group_id": str(row.ad_group.id),
            "ad_group_name": row.ad_group.name,
            "ad_group_status": enum_name(row.ad_group.status),
            "ad_id": str(ad.id),
            "ad_name": str(ad.name or ""),
            "ad_type": enum_name(ad.type),
            "status": status,
            "approval_status": approval_status,
        }
        record["label"] = ad_label(record)
        record["severity"] = item_severity(status, approval_status)
        record["flags"] = item_flags(status, approval_status)
        ads.append(record)
    ads.sort(key=lambda row: (row["severity"], row["campaign_name"], row["ad_group_name"], row["ad_type"]))
    return ads


def fetch_asset_group_assets(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,
          asset_group.id,
          asset_group.name,
          asset.id,
          asset.name,
          asset.type,
          asset_group_asset.field_type,
          asset_group_asset.status,
          asset_group_asset.policy_summary.approval_status
        FROM asset_group_asset
        WHERE campaign.status != 'REMOVED'
          AND asset_group_asset.status != 'REMOVED'
        """,
    )

    assets = []
    for row in rows:
        status = enum_name(row.asset_group_asset.status)
        approval_status = enum_name(row.asset_group_asset.policy_summary.approval_status)
        record = {
            "item_type": "zasob",
            "campaign_id": str(row.campaign.id),
            "campaign_name": row.campaign.name,
            "campaign_status": enum_name(row.campaign.status),
            "asset_group_id": str(row.asset_group.id),
            "asset_group_name": row.asset_group.name,
            "asset_id": str(row.asset.id),
            "asset_name": str(row.asset.name or ""),
            "asset_type": enum_name(row.asset.type),
            "field_type": enum_name(row.asset_group_asset.field_type),
            "status": status,
            "approval_status": approval_status,
        }
        record["label"] = record["asset_name"] or f"{record['asset_type']} {record['asset_id']}"
        record["severity"] = item_severity(status, approval_status)
        record["flags"] = item_flags(status, approval_status)
        assets.append(record)
    assets.sort(key=lambda row: (row["severity"], row["campaign_name"], row["asset_group_name"], row["field_type"]))
    return assets


def build_counter_summary(rows: list[dict], field: str, label: str) -> list[dict]:
    counter = Counter(row.get(field, "") or "(brak)" for row in rows)
    return [{label: key, "count": value} for key, value in counter.most_common()]


def build_problem_items(ads: list[dict], assets: list[dict]) -> list[dict]:
    items = []
    for ad in ads:
        if ad["flags"] == ["ok"]:
            continue
        items.append(
            {
                "item_type": "reklama",
                "severity": ad["severity"],
                "campaign_name": ad["campaign_name"],
                "container": ad["ad_group_name"],
                "label": ad["label"],
                "status": ad["status"],
                "approval_status": ad["approval_status"],
                "flags": ad["flags"],
                "recommendation": "sprawdz przyczyne ograniczenia lub odrzucenia w Google Ads",
            }
        )
    for asset in assets:
        if asset["flags"] == ["ok"]:
            continue
        items.append(
            {
                "item_type": "zasob",
                "severity": asset["severity"],
                "campaign_name": asset["campaign_name"],
                "container": asset["asset_group_name"],
                "label": asset["label"],
                "status": asset["status"],
                "approval_status": asset["approval_status"],
                "flags": asset["flags"],
                "recommendation": "sprawdz status zasobu i polityki w asset group",
            }
        )
    severity_order = {"wysokie": 0, "srednie": 1, "niskie": 2, "ok": 9}
    items.sort(key=lambda row: (severity_order.get(row["severity"], 9), row["campaign_name"], row["item_type"]))
    return items


def build_ad_asset_status_plan(client_config: ClientConfig) -> AdAssetStatusPlan:
    warnings = []
    try:
        ads = fetch_ads(client_config)
    except Exception as exc:
        ads = []
        warnings.append(f"Nie udalo sie pobrac reklam z Google Ads API: {exc}")

    try:
        assets = fetch_asset_group_assets(client_config)
    except Exception as exc:
        assets = []
        warnings.append(f"Nie udalo sie pobrac zasobow PMax z Google Ads API: {exc}")

    if not ads:
        warnings.append("Nie znaleziono reklam albo nie udalo sie ich pobrac.")
    if not assets:
        warnings.append("Nie znaleziono zasobow asset group albo API nie udostepnilo ich dla tego konta.")

    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 odrzucen reklam i zasobow bedziemy dopisywac osobno po akceptacji uzytkownika."
        )

    return AdAssetStatusPlan(
        ads=ads,
        assets=assets,
        ad_status_summary=build_counter_summary(ads, "status", "status"),
        approval_summary=build_counter_summary(ads + assets, "approval_status", "approval_status"),
        asset_status_summary=build_counter_summary(assets, "status", "status"),
        problem_items=build_problem_items(ads, assets),
        scope=SCOPE,
        out_of_scope=OUT_OF_SCOPE,
        knowledge_rules=knowledge_rules,
        warnings=warnings,
    )


def save_ad_asset_status_plan(domain: str, plan: AdAssetStatusPlan) -> 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 statusow reklam i zasobow",
        "",
        f"Klient: {domain}",
        f"Utworzono: {ts.isoformat(timespec='seconds')}",
        "",
        "## Podsumowanie",
        "",
        f"- Reklamy: {len(plan.ads)}",
        f"- Zasoby asset group: {len(plan.assets)}",
        f"- Elementy do oceny: {len(plan.problem_items)}",
        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.problem_items:
        lines.extend(
            [
                "## Elementy do oceny",
                "",
                "| Waznosc | Typ | Kampania | Kontener | Element | Status | Polityka | Flagi | Rekomendacja |",
                "| --- | --- | --- | --- | --- | --- | --- | --- | --- |",
            ]
        )
        for item in plan.problem_items:
            lines.append(
                f"| {item['severity']} | {item['item_type']} | {md_cell(item['campaign_name'])} | "
                f"{md_cell(item['container'])} | {md_cell(item['label'])} | {item['status']} | "
                f"{item['approval_status']} | {md_cell(', '.join(item['flags']))} | {md_cell(item['recommendation'])} |"
            )
        lines.append("")
    if plan.ad_status_summary:
        lines.extend(["## Statusy reklam", "", "| Status | Liczba |", "| --- | --- |"])
        for row in plan.ad_status_summary:
            lines.append(f"| {row['status']} | {row['count']} |")
        lines.append("")
    if plan.approval_summary:
        lines.extend(["## Statusy zatwierdzenia", "", "| Status polityki | Liczba |", "| --- | --- |"])
        for row in plan.approval_summary:
            lines.append(f"| {row['approval_status']} | {row['count']} |")
        lines.append("")
    if plan.ads:
        lines.extend(
            [
                "## Reklamy",
                "",
                "| Kampania | Grupa reklam | Typ | Status | Polityka | Flagi |",
                "| --- | --- | --- | --- | --- | --- |",
            ]
        )
        for ad in plan.ads:
            lines.append(
                f"| {md_cell(ad['campaign_name'])} | {md_cell(ad['ad_group_name'])} | {ad['ad_type']} | "
                f"{ad['status']} | {ad['approval_status']} | {md_cell(', '.join(ad['flags']))} |"
            )
        lines.append("")
    if plan.assets:
        lines.extend(
            [
                "## Zasoby asset group",
                "",
                "| Kampania | Asset group | Typ | Pole | Status | Polityka | Flagi |",
                "| --- | --- | --- | --- | --- | --- | --- |",
            ]
        )
        for asset in plan.assets:
            lines.append(
                f"| {md_cell(asset['campaign_name'])} | {md_cell(asset['asset_group_name'])} | {asset['asset_type']} | "
                f"{asset['field_type']} | {asset['status']} | {asset['approval_status']} | {md_cell(', '.join(asset['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_ad_asset_status_plan(plan: AdAssetStatusPlan) -> None:
    print("\nPlan sprawdzenia statusow reklam i zasobow")
    print_table(
        ["Metryka", "Liczba"],
        [
            ["Reklamy", str(len(plan.ads))],
            ["Zasoby asset group", str(len(plan.assets))],
            ["Elementy do oceny", str(len(plan.problem_items))],
            ["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.problem_items:
        print("\nElementy do oceny")
        print_table(
            ["Nr", "Waznosc", "Typ", "Kampania", "Kontener", "Status", "Polityka", "Flagi"],
            [
                [
                    str(index),
                    item["severity"],
                    item["item_type"],
                    item["campaign_name"],
                    item["container"],
                    item["status"],
                    item["approval_status"],
                    ", ".join(item["flags"]),
                ]
                for index, item in enumerate(plan.problem_items[:30], 1)
            ],
        )
        if len(plan.problem_items) > 30:
            print(f"... oraz {len(plan.problem_items) - 30} kolejnych elementow w pliku planu")
    if plan.ad_status_summary:
        print("\nStatusy reklam")
        print_table(["Status", "Liczba"], [[row["status"], str(row["count"])] for row in plan.ad_status_summary])
    if plan.approval_summary:
        print("\nStatusy zatwierdzenia")
        print_table(
            ["Status polityki", "Liczba"],
            [[row["approval_status"], str(row["count"])] for row in plan.approval_summary],
        )
    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_ad_asset_status_plan(
    client_config: ClientConfig,
    plan: AdAssetStatusPlan,
    show_navigation: bool = True,
) -> None:
    print("\nTo zadanie jest audytem statusow reklam i zasobow 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.problem_items[:10]),
            "summary": {
                "ads": len(plan.ads),
                "assets": len(plan.assets),
                "problem_items": len(plan.problem_items),
                "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_ad_asset_statuses(
    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 = AdAssetStatusPlan.from_dict(plan_data)
        print_ad_asset_status_plan(plan)
        apply_ad_asset_status_plan(client_config, plan, show_navigation=show_navigation)
        return

    print(f"\nKlient: {client_config.domain}")
    print("Przygotowuje plan sprawdzenia statusow reklam i zasobow...")
    plan = build_ad_asset_status_plan(client_config)
    print_ad_asset_status_plan(plan)
    json_path, md_path = save_ad_asset_status_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.problem_items[:10]),
            "summary": {
                "ads": len(plan.ads),
                "assets": len(plan.assets),
                "problem_items": len(plan.problem_items),
                "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 statusow reklam i zasobow.")
    if show_navigation:
        print_next_navigation(client_config.domain)
