"""Tool: brand_strategy — Analyze and improve your LinkedIn personal brand.

Audits your profile, generates a 4-week strategy, executes actions
(auto-publish posts, auto-comment/react/follow, profile suggestions),
and tracks progress.

Fully automated execution:
  - Posts: generated with voice matching and published immediately
  - Engagement: finds trending posts in your niche, comments/reacts/follows
  - Profile edits: generates suggestions (Unipile cannot edit profiles)
"""

from __future__ import annotations

import logging
import time as _time
from typing import Any

from ..db.queries import (
    get_campaign_stats,
    get_rate_limit_today,
    get_sending_days_7d,
    get_setting,
    get_weekly_invitation_sum,
    list_campaigns,
    log_action,
)
from ..formatter import progress_bar, stars, table, tree
from ..linkedin import get_account_id, get_linkedin_client, UnipileError
from ..services.brand_service import (
    capture_baseline,
    compute_progress,
    count_plan_progress,
    get_next_pending_action,
    load_brand_analysis,
    load_brand_baseline,
    load_brand_plan,
    mark_action_completed,
    save_brand_analysis,
    save_brand_baseline,
    save_brand_plan,
)
from ..services.health_score import compute_health_score

logger = logging.getLogger(__name__)


async def run_brand_strategy(
    action: str = "analyze",
    focus: str = "",
) -> str:
    """Main handler for brand_strategy tool."""

    # ── Pre-checks ──
    setup_done = get_setting("setup_complete", False)
    if not setup_done:
        return (
            "Setup required before analyzing your brand.\n\n"
            "Please run setup_profile first."
        )

    account_id = get_account_id()
    if not account_id:
        return "No LinkedIn account connected. Run setup_profile first."

    if action == "analyze":
        return await _handle_analyze(account_id, focus)
    elif action == "plan":
        return await _handle_plan(account_id, focus)
    elif action == "execute":
        return await _handle_execute(account_id)
    elif action == "progress":
        return await _handle_progress(account_id)
    else:
        return (
            "Unknown action. Available actions:\n\n"
            '  brand_strategy(action="analyze")  — Full profile audit\n'
            '  brand_strategy(action="plan")     — Generate 4-week strategy\n'
            '  brand_strategy(action="execute")  — Execute next action\n'
            '  brand_strategy(action="progress") — Track before/after metrics'
        )


# ──────────────────────────────────────────────
# Action: analyze
# ──────────────────────────────────────────────


async def _handle_analyze(account_id: str, focus: str) -> str:
    """Full profile audit with scored areas."""

    from ..ai.brand_strategist import analyze_brand_profile

    # ── Gather data ──
    profile = get_setting("profile", {})
    voice = get_setting("voice_signature", {})
    expertise = get_setting("expertise_map", {})

    # Fetch SSI and posts
    ssi_data: dict[str, Any] = {}
    posts: list[dict] = []
    try:
        client = get_linkedin_client()
        try:
            ssi_data = await client.get_ssi_score(account_id)
        except Exception as e:
            logger.warning("SSI fetch failed: %s", e)

        provider_id = profile.get("provider_id", "")
        if provider_id:
            try:
                posts = await client.get_posts(account_id, provider_id)
            except Exception as e:
                logger.warning("Posts fetch failed: %s", e)
        await client.close()
    except Exception as e:
        logger.warning("Client init failed: %s", e)

    # Aggregate campaign stats
    campaign_stats = _aggregate_campaign_stats()

    # ── Run LLM analysis ──
    analysis = await analyze_brand_profile(
        profile, posts, ssi_data, campaign_stats, voice, expertise,
    )

    # ── Save ──
    save_brand_analysis(analysis)

    # ── Format output ──
    return _format_analysis(analysis, focus)


def _aggregate_campaign_stats() -> dict[str, Any]:
    """Aggregate acceptance and reply rates across all campaigns."""
    campaigns = list_campaigns()
    total_invited = 0
    total_connected = 0
    total_replied = 0

    for c in campaigns:
        if c.get("status") in ("draft", "archived"):
            continue
        stats = get_campaign_stats(c["id"])
        total_invited += stats.get("invited", 0)
        total_connected += stats.get("connected", 0)
        total_replied += stats.get("replied", 0)

    acceptance_rate = total_connected / total_invited if total_invited > 0 else 0
    reply_rate = total_replied / total_connected if total_connected > 0 else 0

    return {
        "acceptance_rate": acceptance_rate,
        "reply_rate": reply_rate,
        "total_invited": total_invited,
        "total_connected": total_connected,
        "total_replied": total_replied,
    }


def _format_analysis(analysis: dict[str, Any], focus: str) -> str:
    """Format brand analysis for chat display."""
    overall = analysis.get("overall_score", 0)
    areas = analysis.get("areas", {})
    top_actions = analysis.get("top_3_actions", [])

    # Overall score with rating
    if overall >= 80:
        rating = "Excellent"
    elif overall >= 60:
        rating = "Good"
    elif overall >= 40:
        rating = "Needs Work"
    else:
        rating = "Critical"

    lines = [
        f"Brand Profile Audit (Score: {overall}/100 — {rating})",
        "",
    ]

    # ── Each area ──
    area_order = ["headline", "summary", "content", "profile_completeness", "ssi_breakdown", "engagement"]
    area_labels = {
        "headline": "Headline",
        "summary": "Summary/About",
        "content": "Content Strategy",
        "profile_completeness": "Profile Completeness",
        "ssi_breakdown": "SSI Score",
        "engagement": "Engagement",
    }

    for key in area_order:
        area = areas.get(key, {})
        if not area:
            continue

        if focus and focus != "all" and focus != key:
            continue

        label = area_labels.get(key, key)
        score = area.get("score", 0)
        lines.append(f"{label}: {score}/10 {stars(score, 10)}")

        # Area-specific details
        if key == "headline":
            current = area.get("current", "")
            if current:
                lines.append(f"  Current: \"{current}\"")
            suggestion = area.get("suggestion", "")
            if suggestion:
                lines.append(f"  Suggested: \"{suggestion}\"")

        issues = area.get("issues", [])
        if issues:
            for issue in issues[:3]:
                lines.append(f"  - {issue}")

        if key == "profile_completeness":
            missing = area.get("missing", [])
            if missing:
                lines.append(f"  Missing: {', '.join(missing)}")

        if key == "ssi_breakdown":
            improvements = area.get("improvements", [])
            for imp in improvements[:3]:
                lines.append(f"  - {imp}")

        lines.append("")

    # ── Top 3 actions ──
    if top_actions:
        lines.append("Top 3 Actions:")
        for i, action in enumerate(top_actions, 1):
            lines.append(f"  {i}. {action}")
        lines.append("")

    lines.append('Next: Run brand_strategy(action="plan") to generate a 4-week improvement plan.')

    return "\n".join(lines)


# ──────────────────────────────────────────────
# Action: plan
# ──────────────────────────────────────────────


async def _handle_plan(account_id: str, focus: str) -> str:
    """Generate a 4-week brand strategy plan."""

    from ..ai.brand_strategist import generate_brand_plan

    # Require existing analysis
    analysis = load_brand_analysis()
    if not analysis:
        return (
            "No brand analysis found.\n\n"
            'Run brand_strategy(action="analyze") first to audit your profile.'
        )

    profile = get_setting("profile", {})
    voice = get_setting("voice_signature", {})
    expertise = get_setting("expertise_map", {})

    # ── Generate plan via LLM ──
    plan = await generate_brand_plan(analysis, profile, voice, expertise, focus or "all")

    if not plan.get("weeks"):
        return "Failed to generate brand plan. Please try again."

    plan["focus"] = focus or "all"

    # ── Capture baseline ──
    ssi_data: dict[str, Any] = {}
    try:
        client = get_linkedin_client()
        ssi_data = await client.get_ssi_score(account_id)
        await client.close()
    except Exception:
        pass

    campaign_stats = _aggregate_campaign_stats()
    hs = _compute_current_health(ssi_data, campaign_stats)

    baseline = capture_baseline(profile, ssi_data, hs.total, campaign_stats.get("acceptance_rate", 0))
    save_brand_baseline(baseline)

    # ── Save plan ──
    save_brand_plan(plan)

    # ── Format output ──
    return _format_plan(plan)


def _format_plan(plan: dict[str, Any]) -> str:
    """Format brand strategy plan for chat display."""
    lines = [
        "4-Week Brand Strategy",
        "",
    ]

    for week in plan.get("weeks", []):
        week_num = week.get("week", "?")
        theme = week.get("theme", "")
        lines.append(f"Week {week_num}: {theme}")

        actions = week.get("actions", [])
        for i, action in enumerate(actions):
            is_last = i == len(actions) - 1
            prefix = "└──" if is_last else "├──"
            desc = action.get("description", "")
            action_type = action.get("type", "")
            icon = {"profile_optimize": "✏️", "post": "📝", "engagement": "💬"}.get(action_type, "📌")
            lines.append(f"  {prefix} {icon} {desc}")

        lines.append("")

    # Content calendar
    calendar = plan.get("content_calendar", [])
    if calendar:
        lines.append("Content Calendar:")
        for entry in calendar:
            day = entry.get("day", "")
            ctype = entry.get("type", "")
            topic = entry.get("example_topic", "")
            lines.append(f"  {day}: {ctype} — {topic}")
        lines.append("")

    # Engagement targets
    targets = plan.get("engagement_targets", {})
    if targets:
        lines.append("Weekly Targets:")
        lines.append(f"  Posts: {targets.get('weekly_posts', 2)}/week")
        lines.append(f"  Comments: {targets.get('daily_comments', 5)}/day")
        lines.append(f"  Reactions: {targets.get('daily_reactions', 10)}/day")
        lines.append("")

    completed, total = count_plan_progress(plan)
    lines.append(f"Progress: {completed}/{total} actions")
    lines.append(f"  {progress_bar(completed, total, 20)}")
    lines.append("")
    lines.append('Next: Run brand_strategy(action="execute") to start working on your plan.')

    return "\n".join(lines)


# ──────────────────────────────────────────────
# Action: execute
# ──────────────────────────────────────────────


async def _handle_execute(account_id: str) -> str:
    """Execute the next pending action from the plan."""

    plan = load_brand_plan()
    if not plan:
        return (
            "No brand strategy plan found.\n\n"
            'Run brand_strategy(action="plan") first to generate a plan.'
        )

    action = get_next_pending_action(plan)
    if not action:
        return (
            "All brand strategy actions completed!\n\n"
            'Run brand_strategy(action="progress") to see your results.'
        )

    action_type = action.get("type", "")
    action_id = action.get("id", "")

    if action_type == "post":
        return await _execute_post_action(action, action_id, account_id)
    elif action_type == "profile_optimize":
        return await _execute_profile_action(action, action_id)
    elif action_type == "engagement":
        return await _execute_engagement_action(action, action_id, account_id)
    else:
        mark_action_completed(action_id, "Skipped — unknown type")
        return f"Skipped unknown action type: {action_type}. Moving to next."


async def _execute_post_action(action: dict[str, Any], action_id: str, account_id: str) -> str:
    """Generate and auto-publish a LinkedIn post."""
    topic = action.get("topic", action.get("description", ""))
    tone = action.get("tone", "thought-leader")

    profile = get_setting("profile", {})
    voice = get_setting("voice_signature", {})

    name = profile.get("name", "")
    title = profile.get("title", "")
    company = profile.get("company", "")
    industry = profile.get("industry", "")
    voice_tone = voice.get("tone", "")
    voice_patterns = ", ".join(voice.get("communication_style", [])[:5])

    prompt = f"""Write a LinkedIn post for {name} ({title} at {company}).
Industry: {industry}
Writing style: {voice_tone}
Tone: {tone}
Patterns: {voice_patterns}

Topic: {topic}

Requirements:
- Write in first person, using {name}'s voice and style
- Keep it between 150-1200 characters (optimal LinkedIn engagement)
- Include a hook in the first line to stop scrolling
- Use short paragraphs (1-3 sentences each)
- End with a question or call-to-action to drive engagement
- Do NOT use hashtags unless the user's style includes them
- Do NOT use emojis unless the user's style includes them
- Be authentic and conversational, not corporate
- Share a genuine insight, story, or perspective

Return ONLY the post text, nothing else."""

    try:
        from ..config import has_local_llm_key, is_backend_mode

        if is_backend_mode() and not has_local_llm_key():
            # Route through backend — use generate_message endpoint with post context
            client = get_linkedin_client()
            resp = await client.generate_message(
                {"name": name, "title": title, "company": company, "industry": industry},
                {"name": "audience", "title": "LinkedIn audience"},
                voice,
                {
                    "target": f"LinkedIn post about: {topic}",
                    "type": "post",
                    "max_chars": "1200",
                    "instructions": prompt,
                },
            )
            post_text = resp.get("message", "")
            await client.close()
        else:
            from ..ai.llm import LLMClient

            llm = LLMClient()
            post_text = await llm.generate(prompt, max_tokens=800)
    except Exception as e:
        logger.error("Brand post generation failed: %s", e)
        mark_action_completed(action_id, f"Failed: {e}")
        return f"Failed to generate post: {e}"

    if not post_text or len(post_text) < 30:
        mark_action_completed(action_id, "Failed: empty post")
        return "Failed to generate post content. Try again."

    # ── Auto-publish ──
    try:
        client = get_linkedin_client()
        result = await client.create_post(account_id, post_text)
        await client.close()

        if result.get("success"):
            log_action("brand_post_published", details={
                "topic": topic, "tone": tone, "chars": len(post_text),
                "post_id": result.get("post_id", ""),
            })
            mark_action_completed(action_id, f"Published: {topic[:50]}")

            plan = load_brand_plan()
            completed, total = count_plan_progress(plan) if plan else (0, 0)

            return (
                f"Brand Strategy: Post Published!\n\n"
                f'   "{post_text[:300]}{"..." if len(post_text) > 300 else ""}"\n'
                f"   ({len(post_text)} chars)\n\n"
                f"Tip: Reply to comments in the first 2 hours to boost reach.\n\n"
                f"Progress: {completed}/{total} actions completed"
            )
        else:
            error = result.get("error", "Unknown error")
            mark_action_completed(action_id, f"Publish failed: {error}")
            return f"Post generated but publish failed: {error}\n\nDraft:\n{post_text}"
    except Exception as e:
        mark_action_completed(action_id, f"Publish failed: {e}")
        return f"Post generated but publish failed: {e}\n\nDraft:\n{post_text}"


async def _execute_profile_action(action: dict[str, Any], action_id: str) -> str:
    """Generate profile optimization suggestions (cannot auto-edit via API)."""
    from ..ai.brand_strategist import generate_brand_action

    profile = get_setting("profile", {})
    voice = get_setting("voice_signature", {})
    analysis = load_brand_analysis() or {}
    subtype = action.get("subtype", "headline")

    result = await generate_brand_action(action, profile, voice, analysis)

    if result.get("error"):
        return f"Failed to generate suggestion: {result['error']}"

    mark_action_completed(action_id, f"Generated {subtype} suggestions")

    plan = load_brand_plan()
    completed, total = count_plan_progress(plan) if plan else (0, 0)

    lines = [
        f"Brand Strategy: Optimize {subtype.title()}",
        "(LinkedIn API does not support profile editing — apply manually)",
        "",
    ]

    if subtype == "headline":
        options = result.get("options", [])
        if options:
            for i, opt in enumerate(options, 1):
                text = opt.get("text", "")
                rationale = opt.get("rationale", "")
                lines.append(f"Option {i}: \"{text}\"")
                if rationale:
                    lines.append(f"  Why: {rationale}")
                lines.append("")
            lines.append("Copy your preferred headline and update it on LinkedIn.")
        else:
            lines.append("Could not generate headline options.")

    elif subtype == "summary":
        summary = result.get("summary", "")
        hook = result.get("hook", "")
        if summary:
            lines.append("Suggested About Section:")
            lines.append("")
            lines.append(f"  {summary}")
            lines.append("")
            if hook:
                lines.append(f"Hook (first 2 lines): \"{hook}\"")
                lines.append("")
            lines.append("Copy this to your LinkedIn About section and customize as needed.")
        else:
            lines.append("Could not generate summary.")

    lines.append("")
    lines.append(f"Progress: {completed}/{total} actions completed")

    return "\n".join(lines)


async def _execute_engagement_action(
    action: dict[str, Any], action_id: str, account_id: str,
) -> str:
    """Auto-execute engagement: find trending posts, comment/react/follow."""
    desc = action.get("description", "")
    target = action.get("target_count", 5)
    engagement_type = action.get("engagement_type", "comment")  # comment, react, follow

    profile = get_setting("profile", {})
    voice = get_setting("voice_signature", {})
    expertise = get_setting("expertise_map", {})

    # Derive search keywords from user's expertise and industry
    keywords = expertise.get("credible_topics", expertise.get("core", ""))
    if not keywords:
        keywords = profile.get("industry", profile.get("title", ""))

    results: list[str] = []
    errors: list[str] = []
    count = 0

    try:
        client = get_linkedin_client()

        # ── Step 1: Discover trending posts in the user's niche ──
        posts: list[dict] = []
        try:
            search_results, _ = await client.search_posts(
                account_id, keywords, limit=min(target * 2, 20),
            )
            for sr in search_results:
                post_id = sr.get("post_id", sr.get("id", ""))
                text = sr.get("text", "")
                author = sr.get("author_name", "")
                if post_id and text and len(text) > 30:
                    posts.append({
                        "post_id": post_id,
                        "text": text,
                        "author": author,
                        "provider_id": sr.get("author_provider_id", sr.get("provider_id", "")),
                    })
        except Exception as e:
            logger.warning("Post search failed: %s", e)
            errors.append(f"Post search failed: {e}")

        # ── Step 2: Execute engagements ──
        for post in posts[:target]:
            post_id = post["post_id"]

            if engagement_type == "follow" and post.get("provider_id"):
                # Follow the post author
                try:
                    await client.follow_profile(account_id, post["provider_id"])
                    results.append(f"Followed {post['author']}")
                    count += 1
                except Exception as e:
                    errors.append(f"Follow failed ({post['author']}): {e}")
                continue

            if engagement_type == "react" or engagement_type == "like":
                # React to the post
                try:
                    await client.send_post_reaction(account_id, post_id, "LIKE")
                    results.append(f"Liked post by {post['author']}")
                    count += 1
                except Exception as e:
                    errors.append(f"React failed ({post['author']}): {e}")
                continue

            # Default: comment
            try:
                voice_tone = voice.get("tone", "professional")
                comment_prompt = f"""Write a brief, authentic LinkedIn comment on this post.

Post by {post['author']}:
"{post['text'][:500]}"

You are {profile.get('name', '')} ({profile.get('title', '')}).
Your voice: {voice_tone}

Rules:
- 50-200 characters max
- Be genuine, add value (insight, question, agreement with nuance)
- Match the user's voice tone
- No generic "Great post!" or "Thanks for sharing!"
- Do NOT use emojis unless the user's style includes them

Return ONLY the comment text."""

                from ..config import has_local_llm_key, is_backend_mode

                if is_backend_mode() and not has_local_llm_key():
                    resp = await client.generate_comment(
                        {"name": profile.get("name", ""), "title": profile.get("title", "")},
                        {"name": post["author"]},
                        voice,
                        {"text": post["text"][:500], "post_id": post["post_id"]},
                    )
                    comment_text = resp.get("comment", "")
                else:
                    from ..ai.llm import LLMClient

                    llm = LLMClient()
                    comment_text = await llm.generate(comment_prompt, max_tokens=200)
                if comment_text and len(comment_text) >= 10:
                    await client.send_post_comment(account_id, post_id, comment_text)
                    results.append(
                        f"Commented on {post['author']}'s post: "
                        f'"{comment_text[:60]}{"..." if len(comment_text) > 60 else ""}"'
                    )
                    count += 1
                else:
                    # Fallback to reaction if comment generation fails
                    await client.send_post_reaction(account_id, post_id, "LIKE")
                    results.append(f"Liked post by {post['author']} (comment gen failed)")
                    count += 1
            except Exception as e:
                # Fallback to reaction on any error
                try:
                    await client.send_post_reaction(account_id, post_id, "LIKE")
                    results.append(f"Liked post by {post['author']} (comment failed)")
                    count += 1
                except Exception:
                    errors.append(f"Engage failed ({post['author']}): {e}")

        await client.close()
    except Exception as e:
        logger.error("Engagement execution failed: %s", e)
        errors.append(f"Client error: {e}")

    log_action("brand_engagement", details={
        "type": engagement_type, "target": target,
        "completed": count, "keywords": keywords[:100],
    })

    mark_action_completed(action_id, f"{engagement_type}: {count}/{target}")

    plan = load_brand_plan()
    completed, total = count_plan_progress(plan) if plan else (0, 0)

    # ── Format output ──
    lines = [
        f"Brand Strategy: Engagement ({engagement_type.title()})",
        f"Target: {desc}",
        f"Completed: {count}/{target}",
        "",
    ]

    if results:
        lines.append("Actions taken:")
        for r in results:
            lines.append(f"  ✅ {r}")
        lines.append("")

    if errors:
        lines.append("Issues:")
        for e in errors[:3]:
            lines.append(f"  ⚠️ {e}")
        lines.append("")

    lines.append(f"Progress: {completed}/{total} actions completed")

    return "\n".join(lines)


# ──────────────────────────────────────────────
# Action: progress
# ──────────────────────────────────────────────


async def _handle_progress(account_id: str) -> str:
    """Show before/after metrics and action progress."""

    baseline = load_brand_baseline()
    plan = load_brand_plan()

    if not baseline or not plan:
        return (
            "No brand strategy in progress.\n\n"
            'Run brand_strategy(action="analyze") first, then brand_strategy(action="plan").'
        )

    # Fetch current metrics
    ssi_data: dict[str, Any] = {}
    try:
        client = get_linkedin_client()
        ssi_data = await client.get_ssi_score(account_id)
        await client.close()
    except Exception:
        pass

    campaign_stats = _aggregate_campaign_stats()
    hs = _compute_current_health(ssi_data, campaign_stats)

    progress = compute_progress(
        baseline,
        ssi_data,
        campaign_stats.get("acceptance_rate", 0),
        hs.total,
        plan,
    )

    return _format_progress(progress, plan)


def _format_progress(progress: dict[str, Any], plan: dict[str, Any]) -> str:
    """Format progress report."""

    def _change_str(change: float | int, is_pct: bool = False) -> str:
        if change > 0:
            return f"+{change:.0%}" if is_pct else f"+{change}"
        elif change < 0:
            return f"{change:.0%}" if is_pct else f"{change}"
        return "—"

    lines = [
        f"Brand Strategy Progress (Day {progress['days_since_start']})",
        "",
    ]

    # Metrics comparison table
    headers = ["Metric", "Before", "Now", "Change"]
    rows = [
        [
            "SSI Score",
            str(progress["ssi_before"]),
            str(progress["ssi_now"]),
            _change_str(progress["ssi_change"]),
        ],
        [
            "Acceptance Rate",
            f"{progress['acceptance_before']:.0%}",
            f"{progress['acceptance_now']:.0%}",
            _change_str(progress["acceptance_change"], is_pct=True),
        ],
        [
            "Health Score",
            str(progress["health_before"]),
            str(progress["health_now"]),
            _change_str(progress["health_change"]),
        ],
    ]

    lines.append(table(headers, rows))
    lines.append("")

    # Action progress
    completed = progress["actions_completed"]
    total = progress["actions_total"]
    lines.append(f"Actions: {completed}/{total} completed")
    lines.append(f"  {progress_bar(completed, total, 20)}")
    lines.append("")

    # Per-week breakdown
    for week in plan.get("weeks", []):
        week_num = week.get("week", "?")
        theme = week.get("theme", "")
        actions = week.get("actions", [])
        week_done = sum(1 for a in actions if a.get("status") == "completed")
        week_total = len(actions)
        status_icon = "✅" if week_done == week_total else "⏳"
        lines.append(f"  {status_icon} Week {week_num}: {theme} ({week_done}/{week_total})")

    lines.append("")

    if completed < total:
        lines.append(f'{progress["actions_remaining"]} actions remaining.')
        lines.append('Run brand_strategy(action="execute") to continue.')
    else:
        lines.append("All actions completed! Great work on your personal brand.")
        lines.append('Run brand_strategy(action="analyze") for a fresh audit to measure improvement.')

    return "\n".join(lines)


# ──────────────────────────────────────────────
# Helpers
# ──────────────────────────────────────────────


def _compute_current_health(ssi_data: dict[str, Any], campaign_stats: dict[str, Any]):
    """Compute current health score from available data."""
    daily = get_rate_limit_today()
    weekly = get_weekly_invitation_sum()
    sending_days = get_sending_days_7d()

    return compute_health_score(
        ssi_score=ssi_data.get("score", 0),
        acceptance_rate=campaign_stats.get("acceptance_rate", 0),
        total_sent=campaign_stats.get("total_invited", 0),
        daily_sent=daily,
        weekly_sent=weekly,
        sending_days_7d=sending_days,
    )
