from __future__ import annotations

import json
from dataclasses import dataclass
from pathlib import Path

from google.protobuf import field_mask_pb2

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


@dataclass
class SettingsPlan:
    campaigns: list[dict]
    changes: list[dict]
    skipped_rules: list[str]
    warnings: list[str]

    def to_dict(self) -> dict:
        return {
            "task": "check_pla_settings",
            "campaigns": self.campaigns,
            "changes": self.changes,
            "skipped_rules": self.skipped_rules,
            "warnings": self.warnings,
        }

    @classmethod
    def from_dict(cls, data: dict) -> "SettingsPlan":
        return cls(
            campaigns=data.get("campaigns", []),
            changes=data.get("changes", []),
            skipped_rules=data.get("skipped_rules", []),
            warnings=data.get("warnings", []),
        )


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


def priority_name(value) -> str:
    raw = enum_name(value)
    return {"0": "LOW", "1": "MEDIUM", "2": "HIGH"}.get(raw, raw)


def human_geo(value: str) -> str:
    return {
        "PRESENCE": "Obecność",
        "PRESENCE_OR_INTEREST": "Obecność lub zainteresowanie",
        "SEARCH_INTEREST": "Zainteresowanie wyszukiwaniem",
    }.get(value, value)


def human_priority(value: str) -> str:
    return {
        "LOW": "Niski",
        "MEDIUM": "Średni",
        "HIGH": "Wysoki",
    }.get(value, value)


def build_settings_plan(client_config: ClientConfig, global_rules: dict) -> SettingsPlan:
    rules = client_config.effective_rules(global_rules, "pla_settings")
    require_presence_only = bool(rules.get("require_presence_only", True))
    require_high_priority = bool(rules.get("require_high_priority", True))

    skipped_rules = []
    if not require_presence_only:
        skipped_rules.append("Regula lokalizacji Obecnosc jest wylaczona dla tego klienta.")
    if not require_high_priority:
        skipped_rules.append("Regula priorytetu wysokiego jest wylaczona dla tego klienta.")

    google_client = get_google_ads_client(use_proto_plus=True)
    customer_id = client_config.safe_customer_id
    rows = run_query(
        google_client,
        customer_id,
        """
        SELECT
          campaign.id,
          campaign.name,
          campaign.status,
          campaign.advertising_channel_type,
          campaign.geo_target_type_setting.positive_geo_target_type,
          campaign.shopping_setting.campaign_priority
        FROM campaign
        WHERE campaign.advertising_channel_type = 'SHOPPING'
          AND campaign.status = 'ENABLED'
        """,
    )

    campaigns = []
    changes = []
    for row in rows:
        campaign = row.campaign
        positive_geo = enum_name(campaign.geo_target_type_setting.positive_geo_target_type)
        priority = priority_name(campaign.shopping_setting.campaign_priority)
        record = {
            "campaign_id": str(campaign.id),
            "campaign_name": campaign.name,
            "status": enum_name(campaign.status),
            "positive_geo_target_type": positive_geo,
            "positive_geo_target_type_label": human_geo(positive_geo),
            "campaign_priority": priority,
            "campaign_priority_label": human_priority(priority),
        }
        campaigns.append(record)

        if require_presence_only and positive_geo != "PRESENCE":
            changes.append(
                {
                    "campaign_id": str(campaign.id),
                    "campaign_name": campaign.name,
                    "setting": "lokalizacje",
                    "current_value": positive_geo,
                    "target_value": "PRESENCE",
                    "current_label": human_geo(positive_geo),
                    "target_label": human_geo("PRESENCE"),
                    "description": "Ustaw lokalizacje na Obecnosc.",
                }
            )
        if require_high_priority and priority != "HIGH":
            changes.append(
                {
                    "campaign_id": str(campaign.id),
                    "campaign_name": campaign.name,
                    "setting": "priorytet kampanii",
                    "current_value": priority,
                    "target_value": "HIGH",
                    "current_label": human_priority(priority),
                    "target_label": human_priority("HIGH"),
                    "description": "Ustaw priorytet kampanii na wysoki.",
                }
            )

    warnings = []
    if not campaigns:
        warnings.append("Nie znaleziono kampanii PLA.")
    return SettingsPlan(campaigns=campaigns, changes=changes, skipped_rules=skipped_rules, warnings=warnings)


def save_settings_plan(domain: str, plan: SettingsPlan) -> 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')}_check_pla_settings"
    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 ustawien kampanii PLA",
        "",
        f"Klient: {domain}",
        f"Utworzono: {ts.isoformat(timespec='seconds')}",
        "",
        "## Podsumowanie",
        "",
        f"- Kampanie PLA: {len(plan.campaigns)}",
        f"- Korekty do wdrozenia: {len(plan.changes)}",
        "",
    ]
    if plan.skipped_rules:
        lines.extend(["## Wylaczone reguly", ""])
        lines.extend(f"- {item}" for item in plan.skipped_rules)
        lines.append("")
    if plan.warnings:
        lines.extend(["## Uwagi", ""])
        lines.extend(f"- {item}" for item in plan.warnings)
        lines.append("")
    lines.extend(["## Kampanie PLA", "", "| Kampania | Status | Lokalizacje | Priorytet |", "| --- | --- | --- | --- |"])
    for row in plan.campaigns:
        lines.append(
            f"| {row['campaign_name']} | {row['status']} | "
            f"{row.get('positive_geo_target_type_label', row['positive_geo_target_type'])} | "
            f"{row.get('campaign_priority_label', row['campaign_priority'])} |"
        )
    lines.append("")
    if plan.changes:
        lines.extend(["## Planowane korekty", "", "| Kampania | Ustawienie | Obecnie | Docelowo |", "| --- | --- | --- | --- |"])
        for row in plan.changes:
            lines.append(
                f"| {row['campaign_name']} | {row['setting']} | "
                f"{row.get('current_label', row['current_value'])} | "
                f"{row.get('target_label', row['target_value'])} |"
            )
        lines.append("")
    md_path.write_text("\n".join(lines), encoding="utf-8")
    return json_path, md_path


def print_settings_plan(plan: SettingsPlan) -> None:
    print("\nPlan sprawdzenia ustawien kampanii PLA")
    print(f"Kampanie PLA: {len(plan.campaigns)}")
    print(f"Korekty do wdrozenia: {len(plan.changes)}")
    for item in plan.skipped_rules:
        print(f"Pominieto: {item}")
    for item in plan.warnings:
        print(f"Uwaga: {item}")
    for change in plan.changes[:30]:
        print(
            f"  {change['campaign_name']} | {change['setting']} | "
            f"{change.get('current_label', change['current_value'])} -> "
            f"{change.get('target_label', change['target_value'])}"
        )
    if len(plan.changes) > 30:
        print(f"  ... oraz {len(plan.changes) - 30} kolejnych korekt")


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_settings_plan(client_config: ClientConfig, plan: SettingsPlan, show_navigation: bool = True) -> None:
    google_client = get_google_ads_client(use_proto_plus=True)
    customer_id = client_config.safe_customer_id
    service = google_client.get_service("CampaignService")

    changes_by_campaign: dict[str, dict] = {}
    for change in plan.changes:
        changes_by_campaign.setdefault(
            change["campaign_id"],
            {"campaign_name": change["campaign_name"], "settings": set()},
        )
        changes_by_campaign[change["campaign_id"]]["settings"].add(change["setting"])

    operations = []
    for campaign_id, row in changes_by_campaign.items():
        op = google_client.get_type("CampaignOperation")
        campaign = op.update
        campaign.resource_name = service.campaign_path(customer_id, campaign_id)
        paths = []
        if "lokalizacje" in row["settings"]:
            campaign.geo_target_type_setting.positive_geo_target_type = (
                google_client.enums.PositiveGeoTargetTypeEnum.PRESENCE
            )
            paths.append("geo_target_type_setting.positive_geo_target_type")
        if "priorytet kampanii" in row["settings"]:
            campaign.shopping_setting.campaign_priority = 2
            paths.append("shopping_setting.campaign_priority")
        op.update_mask = field_mask_pb2.FieldMask(paths=paths)
        operations.append(op)

    changed = 0
    if operations:
        response = service.mutate_campaigns(customer_id=customer_id, operations=operations)
        changed = len(response.results)

    print("\nWynik wdrozenia zmian")
    print(f"Zmieniono kampanii: {changed}")
    print(f"Korekty ustawien: {len(plan.changes)}")

    rows = [
        {
            "klient": client_config.domain,
            "kampania": change["campaign_name"],
            "czynnosc": change["description"],
            "grupa reklam": "",
            "produkt": f"{change.get('current_label', change['current_value'])} -> {change.get('target_label', change['target_value'])}",
        }
        for change in plan.changes
    ]
    changes_path = append_change_markdown(client_config.domain, "Sprawdzenie ustawien kampanii PLA", rows)
    history_path = append_history(
        client_config.domain,
        {
            "task": "Sprawdzenie ustawien",
            "status": "wdrozono zmiany",
            "campaign": ", ".join(sorted({change["campaign_name"] for change in plan.changes})[:10]),
            "summary": {"campaigns_changed": changed, "settings_changes": len(plan.changes)},
        },
    )
    print(f"Historia JSONL: {history_path}")
    print(f"Historia Markdown: {changes_path}")
    if show_navigation:
        print_next_navigation(client_config.domain)


def run_check_pla_settings(
    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:
    if apply_plan_path:
        if confirm_apply != "TAK":
            print("Do wdrozenia planu 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 = SettingsPlan.from_dict(plan_data)
        print_settings_plan(plan)
        apply_settings_plan(client_config, plan, show_navigation=show_navigation)
        return

    print(f"\nKlient: {client_config.domain}")
    print("Sprawdzam ustawienia kampanii PLA...")
    plan = build_settings_plan(client_config, global_rules)
    print_settings_plan(plan)
    json_path, md_path = save_settings_plan(client_config.domain, plan)
    print(f"\nPlan JSON: {json_path}")
    print(f"Plan Markdown: {md_path}")

    append_history(
        client_config.domain,
        {
            "task": "Sprawdzenie ustawien",
            "status": "plan przygotowany",
            "campaign": ", ".join(campaign["campaign_name"] for campaign in plan.campaigns[:10]),
            "summary": {"campaigns": len(plan.campaigns), "changes": len(plan.changes)},
        },
    )

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

    if not plan.changes:
        print("\nBrak zmian do wdrozenia.")
        append_change_markdown(client_config.domain, "Sprawdzenie ustawien kampanii PLA", [])
        if show_navigation:
            print_next_navigation(client_config.domain)
        return

    answer = input("\nWpisz TAK, aby wdrozyc powyzsze zmiany: ").strip()
    if answer != "TAK":
        print("Przerwano. Zmiany nie zostaly wdrozone.")
        append_history(
            client_config.domain,
            {
                "task": "Sprawdzenie ustawien",
                "status": "odrzucono wdrozenie",
                "campaign": ", ".join(campaign["campaign_name"] for campaign in plan.campaigns[:10]),
            },
        )
        if show_navigation:
            print_next_navigation(client_config.domain)
        return

    apply_settings_plan(client_config, plan, show_navigation=show_navigation)
