# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""
Etsy Shop Management Skill

Manage Etsy shop listings, orders, and stats.
Powers the Artist job class with shop management tools.

Setup:
    ETSY_API_KEY=your_keystring   (from https://www.etsy.com/developers/)
    ETSY_SHOP_ID=your_shop_id    (numeric shop ID)
    ETSY_ACCESS_TOKEN=your_oauth_token  (OAuth 2.0 token)

API: https://openapi.etsy.com/v3/
"""

import logging

from familiar.core.api_cache import cached
from familiar.skills._api_utils import (
    RATE_LIMITERS,
    APIError,
    api_get,
    api_post,
    get_env,
)

logger = logging.getLogger(__name__)

BASE_URL = "https://openapi.etsy.com/v3/application"

_limiter = RATE_LIMITERS["etsy"]


def _api_key() -> str:
    return get_env("ETSY_API_KEY") or ""


def _shop_id() -> str:
    return get_env("ETSY_SHOP_ID") or ""


def _access_token() -> str:
    return get_env("ETSY_ACCESS_TOKEN") or ""


def _headers() -> dict:
    token = _access_token()
    h = {"x-api-key": _api_key()}
    if token:
        h["Authorization"] = f"Bearer {token}"
    return h


def _check_config() -> str:
    if not _api_key():
        return (
            "Etsy API key not configured.\n"
            "Set ETSY_API_KEY from https://www.etsy.com/developers/\n"
            "Also set ETSY_SHOP_ID and ETSY_ACCESS_TOKEN."
        )
    if not _shop_id():
        return "ETSY_SHOP_ID not set. Find your shop ID in Etsy shop settings."
    return ""


def _check_rate() -> str:
    if not _limiter.acquire():
        return f"Etsy rate limit reached. Try again in {_limiter.wait_time():.0f} seconds."
    return ""


# ---------------------------------------------------------------------------
# Tool handlers
# ---------------------------------------------------------------------------


def etsy_listings(data: dict) -> str:
    """List active shop listings."""
    err = _check_config()
    if err:
        return err
    err = _check_rate()
    if err:
        return err

    state = data.get("state", "active")
    limit = min(int(data.get("limit", 25)), 100)
    sort_on = data.get("sort_on", "created")

    try:
        result = _listings_cached(_shop_id(), state, limit, sort_on)

        count = result.get("count", 0)
        listings = result.get("results", [])

        if not listings:
            return f"No {state} listings found."

        lines = [f"**{state.title()} Listings** ({count} total, showing {len(listings)}):", ""]

        for i, item in enumerate(listings, 1):
            title = item.get("title", "Untitled")
            listing_id = item.get("listing_id", "")
            price = item.get("price", {})
            price_str = ""
            if isinstance(price, dict):
                amount = price.get("amount", 0)
                divisor = price.get("divisor", 100)
                currency = price.get("currency_code", "USD")
                price_str = f"${amount / divisor:.2f} {currency}"
            quantity = item.get("quantity", 0)
            views = item.get("views", 0)
            favorites = item.get("num_favorers", 0)

            lines.append(f"{i}. **{title}** (ID: {listing_id})")
            details = []
            if price_str:
                details.append(price_str)
            details.append(f"Qty: {quantity}")
            if views:
                details.append(f"{views} views")
            if favorites:
                details.append(f"{favorites} favorites")
            lines.append(f"   {' | '.join(details)}")
            lines.append("")

        return "\n".join(lines)

    except APIError as e:
        return str(e)
    except Exception as e:
        logger.error("Etsy listings error: %s", e)
        return f"Error fetching listings: {e}"


@cached(prefix="etsy_listings", ttl=300)
def _listings_cached(shop_id: str, state: str, limit: int, sort_on: str) -> dict:
    return api_get(
        f"{BASE_URL}/shops/{shop_id}/listings/{state}",
        params={"limit": limit, "sort_on": sort_on, "sort_order": "desc"},
        headers=_headers(),
        service_name="Etsy",
    )


def etsy_orders(data: dict) -> str:
    """View recent shop orders/receipts."""
    err = _check_config()
    if err:
        return err
    if not _access_token():
        return "ETSY_ACCESS_TOKEN required for order access (OAuth 2.0)."
    err = _check_rate()
    if err:
        return err

    limit = min(int(data.get("limit", 25)), 100)
    status = data.get("status", "")

    try:
        result = _orders_cached(_shop_id(), limit)

        count = result.get("count", 0)
        receipts = result.get("results", [])

        if not receipts:
            return "No orders found."

        # Filter by status if specified
        if status:
            receipts = [r for r in receipts if r.get("status", "").lower() == status.lower()]

        lines = [f"**Recent Orders** ({count} total):", ""]

        for i, r in enumerate(receipts[:limit], 1):
            receipt_id = r.get("receipt_id", "")
            buyer = r.get("name", r.get("buyer_email", "Unknown"))
            total = r.get("grandtotal", {})
            total_str = ""
            if isinstance(total, dict):
                amount = total.get("amount", 0)
                divisor = total.get("divisor", 100)
                currency = total.get("currency_code", "USD")
                total_str = f"${amount / divisor:.2f} {currency}"
            order_status = r.get("status", "unknown")

            lines.append(f"{i}. **Order #{receipt_id}** — {buyer}")
            details = []
            if total_str:
                details.append(total_str)
            details.append(f"Status: {order_status}")
            lines.append(f"   {' | '.join(details)}")

            # Show items
            transactions = r.get("transactions", [])
            for t in transactions[:3]:
                item_title = t.get("title", "")
                qty = t.get("quantity", 1)
                if item_title:
                    lines.append(f"   - {item_title} x{qty}")
            lines.append("")

        return "\n".join(lines)

    except APIError as e:
        return str(e)
    except Exception as e:
        logger.error("Etsy orders error: %s", e)
        return f"Error fetching orders: {e}"


@cached(prefix="etsy_orders", ttl=120)
def _orders_cached(shop_id: str, limit: int) -> dict:
    return api_get(
        f"{BASE_URL}/shops/{shop_id}/receipts",
        params={"limit": limit, "sort_on": "created", "sort_order": "desc"},
        headers=_headers(),
        service_name="Etsy",
    )


def etsy_stats(data: dict) -> str:
    """Get shop overview stats."""
    err = _check_config()
    if err:
        return err
    err = _check_rate()
    if err:
        return err

    try:
        shop = _shop_info_cached(_shop_id())

        name = shop.get("shop_name", "Your Shop")
        listing_count = shop.get("listing_active_count", 0)
        sale_count = shop.get("transaction_sold_count", 0)
        review_count = shop.get("review_count", 0)
        review_avg = shop.get("review_average", 0)
        favorers = shop.get("num_favorers", 0)
        currency = shop.get("currency_code", "USD")
        url = shop.get("url", "")

        lines = [
            f"**{name}** — Shop Stats",
            "",
            f"  Active listings: {listing_count}",
            f"  Total sales: {sale_count}",
            f"  Reviews: {review_count} (avg {review_avg:.1f}/5)",
            f"  Favorites: {favorers}",
            f"  Currency: {currency}",
        ]
        if url:
            lines.append(f"  URL: {url}")

        return "\n".join(lines)

    except APIError as e:
        return str(e)
    except Exception as e:
        logger.error("Etsy stats error: %s", e)
        return f"Error fetching shop stats: {e}"


@cached(prefix="etsy_shop", ttl=600)
def _shop_info_cached(shop_id: str) -> dict:
    return api_get(
        f"{BASE_URL}/shops/{shop_id}",
        headers=_headers(),
        service_name="Etsy",
    )


def etsy_create_listing(data: dict) -> str:
    """Create a new draft listing."""
    err = _check_config()
    if err:
        return err
    if not _access_token():
        return "ETSY_ACCESS_TOKEN required to create listings."
    err = _check_rate()
    if err:
        return err

    title = data.get("title", "").strip()
    if not title:
        return "Please provide a listing title."

    description = data.get("description", "").strip()
    if not description:
        return "Please provide a listing description."

    price = data.get("price", 0)
    if not price or float(price) <= 0:
        return "Please provide a valid price."

    quantity = int(data.get("quantity", 1))
    taxonomy_id = data.get("taxonomy_id", "")
    who_made = data.get("who_made", "i_did")
    when_made = data.get("when_made", "made_to_order")
    is_supply = data.get("is_supply", False)

    try:
        body = {
            "title": title,
            "description": description,
            "price": float(price),
            "quantity": quantity,
            "who_made": who_made,
            "when_made": when_made,
            "is_supply": is_supply,
            "state": "draft",
        }
        if taxonomy_id:
            body["taxonomy_id"] = int(taxonomy_id)

        result = api_post(
            f"{BASE_URL}/shops/{_shop_id()}/listings",
            json=body,
            headers=_headers(),
            service_name="Etsy",
        )

        listing_id = result.get("listing_id", "")
        return (
            f"Draft listing created! ID: **{listing_id}**\n"
            f"Title: {title}\n"
            f"Price: ${float(price):.2f}\n\n"
            "The listing is in draft state. Publish it through Etsy or "
            "use `etsy_update_listing` to activate it."
        )

    except APIError as e:
        return str(e)
    except Exception as e:
        logger.error("Etsy create listing error: %s", e)
        return f"Error creating listing: {e}"


def etsy_update_listing(data: dict) -> str:
    """Update an existing listing."""
    err = _check_config()
    if err:
        return err
    if not _access_token():
        return "ETSY_ACCESS_TOKEN required to update listings."
    err = _check_rate()
    if err:
        return err

    listing_id = data.get("listing_id", "")
    if not listing_id:
        return "Please provide a listing_id to update."

    # Build update payload from provided fields
    body = {}
    for field in ["title", "description", "state", "who_made", "when_made"]:
        val = data.get(field, "")
        if val:
            body[field] = val

    price = data.get("price")
    if price is not None:
        body["price"] = float(price)

    quantity = data.get("quantity")
    if quantity is not None:
        body["quantity"] = int(quantity)

    if not body:
        return "Please provide at least one field to update (title, description, price, quantity, state)."

    try:
        import httpx

        resp = httpx.patch(
            f"{BASE_URL}/shops/{_shop_id()}/listings/{listing_id}",
            json=body,
            headers=_headers(),
            timeout=15,
        )
        if resp.status_code >= 400:
            return f"Etsy API error ({resp.status_code}): {resp.text[:200]}"

        result = resp.json()
        title = result.get("title", "")
        return f"Listing **{listing_id}** updated successfully.\nTitle: {title}"

    except Exception as e:
        logger.error("Etsy update listing error: %s", e)
        return f"Error updating listing: {e}"


# ---------------------------------------------------------------------------
# Tool definitions
# ---------------------------------------------------------------------------

TOOLS = [
    {
        "name": "etsy_listings",
        "description": (
            "List active Etsy shop listings with prices, quantities, views, and favorites."
            " Filter by state: active, draft, expired, inactive."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "state": {
                    "type": "string",
                    "description": "Listing state: active, draft, expired, inactive",
                    "default": "active",
                },
                "limit": {
                    "type": "integer",
                    "description": "Max results (1-100, default 25)",
                    "default": 25,
                },
                "sort_on": {
                    "type": "string",
                    "description": "Sort by: created, price, score",
                    "default": "created",
                },
            },
        },
        "handler": etsy_listings,
        "category": "etsy",
    },
    {
        "name": "etsy_orders",
        "description": (
            "View recent Etsy shop orders with buyer info, totals, and items."
            " Requires OAuth token for order access."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "limit": {
                    "type": "integer",
                    "description": "Max orders (1-100, default 25)",
                    "default": 25,
                },
                "status": {
                    "type": "string",
                    "description": "Filter by status: open, paid, completed",
                    "default": "",
                },
            },
        },
        "handler": etsy_orders,
        "category": "etsy",
    },
    {
        "name": "etsy_stats",
        "description": (
            "Get Etsy shop overview: active listings, total sales, review average, favorites count."
        ),
        "input_schema": {
            "type": "object",
            "properties": {},
        },
        "handler": etsy_stats,
        "category": "etsy",
    },
    {
        "name": "etsy_create_listing",
        "description": (
            "Create a new draft listing on Etsy."
            " Requires title, description, and price. Created as draft by default."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "title": {
                    "type": "string",
                    "description": "Listing title (max 140 chars)",
                },
                "description": {
                    "type": "string",
                    "description": "Listing description",
                },
                "price": {
                    "type": "number",
                    "description": "Price in shop currency",
                },
                "quantity": {
                    "type": "integer",
                    "description": "Available quantity (default 1)",
                    "default": 1,
                },
                "who_made": {
                    "type": "string",
                    "description": "Who made it: i_did, someone_else, collective",
                    "default": "i_did",
                },
                "when_made": {
                    "type": "string",
                    "description": "When made: made_to_order, 2020_2025, before_2020, etc.",
                    "default": "made_to_order",
                },
            },
            "required": ["title", "description", "price"],
        },
        "handler": etsy_create_listing,
        "category": "etsy",
    },
    {
        "name": "etsy_update_listing",
        "description": (
            "Update an existing Etsy listing's title, description, price, quantity, or state."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "listing_id": {
                    "type": "string",
                    "description": "Listing ID to update",
                },
                "title": {"type": "string", "description": "New title"},
                "description": {"type": "string", "description": "New description"},
                "price": {"type": "number", "description": "New price"},
                "quantity": {"type": "integer", "description": "New quantity"},
                "state": {
                    "type": "string",
                    "description": "New state: active, draft, inactive",
                },
            },
            "required": ["listing_id"],
        },
        "handler": etsy_update_listing,
        "category": "etsy",
    },
]
