"""Target (audience/people) management tools for Influ2 MCP."""

from __future__ import annotations

import json
from typing import Annotated, Literal

from influ2_mcp.client import get_api_client
from influ2_mcp.config import resolve_client_id
from influ2_mcp.errors import (
    Influ2Error,
    Influ2JobTimeoutError,
    format_error_for_llm,
)
from influ2_mcp.polling import poll_job_until_complete
from influ2_mcp.server import mcp


@mcp.tool()
async def influ2_list_targets(
    cohort_id: Annotated[str, "The cohort ID to list targets for"],
    client_id: Annotated[
        str | None,
        "Influ2 client ID. Omit to use configured default.",
    ] = None,
    offset: Annotated[int, "Start from this target index (default 0)"] = 0,
    limit: Annotated[int, "Max targets to return per page (1-100, default 50)"] = 50,
    search: Annotated[
        str | None,
        "Filter targets by name, email, or company (case-insensitive substring match). "
        "Example: 'acme' to find all targets at Acme Corp.",
    ] = None,
) -> str:
    """List the target people in a cohort with pagination.

    Returns a page of targets from the cohort's audience. The API returns all
    targets at once, so this tool handles pagination locally to stay under
    MCP's response size limits.

    Each target includes: external_id (CRM ID), name, email, title, company,
    LinkedIn URL.

    When a user asks: "Who is in this campaign?", "Show me the target list",
    "List the people in the cohort", "Find targets at [company]",
    "How many people are we targeting?", "Show me the audience",
    "Is [person] in this campaign?"
    """
    try:
        cid = resolve_client_id(client_id)
        api_client = get_api_client()

        # Fetch the cohort — API returns all targets in one payload
        cohort_data = await api_client.get(
            f"/public/v1/clients/{cid}/cohorts/{cohort_id}"
        )

        all_targets = cohort_data.get("targets", []) or []
        total = len(all_targets)

        # Apply search filter if provided
        if search:
            search_lower = search.lower()
            all_targets = [
                t for t in all_targets
                if search_lower in (t.get("first_name", "") or "").lower()
                or search_lower in (t.get("last_name", "") or "").lower()
                or search_lower in (t.get("email", "") or "").lower()
                or search_lower in (t.get("company_name", "") or "").lower()
                or search_lower in (t.get("external_id", "") or "").lower()
                or search_lower in (t.get("title", "") or "").lower()
            ]
            filtered_total = len(all_targets)
        else:
            filtered_total = total

        # Paginate
        safe_offset = max(0, offset)
        safe_limit = min(max(1, limit), 100)
        page = all_targets[safe_offset : safe_offset + safe_limit]

        result = {
            "targets": page,
            "page_count": len(page),
            "total_in_cohort": total,
            "offset": safe_offset,
            "limit": safe_limit,
        }

        if search:
            result["search"] = search
            result["matched"] = filtered_total

        has_more = safe_offset + safe_limit < filtered_total
        if has_more:
            result["_next_steps"] = [
                f"Next page: influ2_list_targets(cohort_id='{cohort_id}', "
                f"offset={safe_offset + safe_limit}, limit={safe_limit})"
            ]

        return json.dumps(result, indent=2, default=str)

    except Influ2Error as e:
        return json.dumps(format_error_for_llm(e))
    except Exception as e:
        return json.dumps(format_error_for_llm(e))


@mcp.tool()
async def influ2_manage_targets(
    cohort_id: Annotated[str, "The cohort ID to manage targets for"],
    action: Annotated[
        Literal["upsert", "replace", "delete"],
        "Operation to perform:\n"
        "- 'upsert': Add new targets or update existing ones (matched by external_id). "
        "Safe, incremental — will NOT remove existing targets.\n"
        "- 'replace': Replace ALL targets in the cohort with this list. "
        "WARNING: removes anyone not in the new list!\n"
        "- 'delete': Remove specific targets from the cohort.",
    ],
    targets: Annotated[
        str,
        "JSON array of target objects. Each must have 'external_id' (your CRM/system ID). "
        "Optional fields: target_id, first_name, last_name, email, title, "
        "company_name, company_website, linkedin.\n"
        "Example: '[{\"external_id\": \"SF-12345\", \"first_name\": \"John\", "
        "\"last_name\": \"Smith\", \"email\": \"john@acme.com\", "
        "\"company_name\": \"Acme Corp\", \"title\": \"VP Engineering\"}]'",
    ],
    client_id: Annotated[
        str | None,
        "Influ2 client ID. Omit to use configured default.",
    ] = None,
    poll_timeout: Annotated[
        int,
        "Max seconds to wait for the operation to complete (default 120). "
        "Large target lists (1000+) may need longer.",
    ] = 120,
) -> str:
    """Add, update, or remove target people in a cohort campaign.

    Targets are the specific individuals your ads will be shown to. Each target
    is identified by an external_id (typically your CRM contact/lead ID) which
    ensures reliable cross-system syncing.

    This is an async operation — the tool polls until completion. For large lists
    (1000+ targets), increase poll_timeout or use influ2_check_job to monitor.

    Actions:
    - 'upsert': Best for ongoing CRM syncs. Adds new, updates existing, never removes.
    - 'replace': Full sync. Replaces the ENTIRE audience. Use with caution.
    - 'delete': Remove specific people (e.g., closed-won contacts, opt-outs).

    When a user asks: "Add these people to the campaign", "Update target info",
    "Sync CRM contacts into the cohort", "Remove this person from the campaign",
    "Replace the entire target list", "Add the buying committee members",
    "This person left the company — remove them"
    """
    try:
        cid = resolve_client_id(client_id)
        api_client = get_api_client()

        targets_list = json.loads(targets)

        # Route to the correct endpoint based on action
        if action == "upsert":
            response = await api_client.post(
                f"/public/v1/clients/{cid}/cohorts/{cohort_id}/targets/upsert",
                json=targets_list,
            )
        elif action == "replace":
            response = await api_client.put(
                f"/public/v1/clients/{cid}/cohorts/{cohort_id}/targets",
                json=targets_list,
            )
        elif action == "delete":
            response = await api_client.post(
                f"/public/v1/clients/{cid}/cohorts/{cohort_id}/targets/delete",
                json=targets_list,
            )
        else:
            return json.dumps({
                "error": True,
                "message": f"Invalid action '{action}'. Must be 'upsert', 'replace', or 'delete'.",
            })

        # Poll the async job
        job_id = response.get("id") or response.get("job_id")
        if job_id:
            try:
                timeout = min(max(poll_timeout, 10), 600)
                job_result = await poll_job_until_complete(
                    api_client, job_id, timeout_seconds=timeout
                )
                result = {
                    "status": "completed",
                    "action": action,
                    "targets_count": len(targets_list),
                    "job": job_result,
                    "_next_steps": [
                        f"View updated cohort with influ2_get_cohort_details(cohort_id='{cohort_id}')",
                    ],
                }
                return json.dumps(result, indent=2, default=str)

            except Influ2JobTimeoutError as e:
                return json.dumps({
                    "status": "polling_timeout",
                    "action": action,
                    "targets_count": len(targets_list),
                    "job_id": e.job_id,
                    "message": e.message,
                    "suggestion": e.suggestion,
                })
        else:
            return json.dumps({
                "status": "submitted",
                "action": action,
                "targets_count": len(targets_list),
                "response": response,
            }, indent=2, default=str)

    except Influ2Error as e:
        return json.dumps(format_error_for_llm(e))
    except json.JSONDecodeError as e:
        return json.dumps({
            "error": True,
            "message": f"Invalid JSON in targets parameter: {e}",
            "suggestion": (
                "Ensure targets is a valid JSON array of objects, each with at "
                "least an 'external_id' field."
            ),
        })
    except Exception as e:
        return json.dumps(format_error_for_llm(e))
