# 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

"""
Shippo Shipping Skill

Get shipping rates, create labels, and track shipments.
Powers the Artist job class with shipping for sold artwork/products.

Setup:
    SHIPPO_API_KEY=your_key   (get at https://goshippo.com/)
    Free tier: pay per label, no monthly fee

API: https://api.goshippo.com/
"""

import logging

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

logger = logging.getLogger(__name__)

BASE_URL = "https://api.goshippo.com"


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


def _headers() -> dict:
    return {
        "Authorization": f"ShippoToken {_api_key()}",
        "Content-Type": "application/json",
    }


def _check_config() -> str:
    if not _api_key():
        return (
            "Shippo API key not configured.\n"
            "Set SHIPPO_API_KEY in your environment.\n"
            "Get a key at: https://goshippo.com/"
        )
    return ""


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


def shipping_rates(data: dict) -> str:
    """Get shipping rate quotes for a package."""
    err = _check_config()
    if err:
        return err

    # Sender address
    from_zip = data.get("from_zip", "").strip()
    from_country = data.get("from_country", "US").strip().upper()

    # Recipient address
    to_zip = data.get("to_zip", "").strip()
    to_country = data.get("to_country", "US").strip().upper()

    if not from_zip or not to_zip:
        return "Please provide from_zip and to_zip for rate quotes."

    # Package dimensions
    weight = data.get("weight", "")
    length = data.get("length", "")
    width = data.get("width", "")
    height = data.get("height", "")
    weight_unit = data.get("weight_unit", "lb")
    dimension_unit = data.get("dimension_unit", "in")

    if not weight:
        return "Please provide package weight."

    try:
        shipment_body = {
            "address_from": {
                "zip": from_zip,
                "country": from_country,
            },
            "address_to": {
                "zip": to_zip,
                "country": to_country,
            },
            "parcels": [
                {
                    "weight": str(weight),
                    "mass_unit": weight_unit,
                }
            ],
            "async": False,
        }

        # Add dimensions if provided
        if length and width and height:
            shipment_body["parcels"][0].update(
                {
                    "length": str(length),
                    "width": str(width),
                    "height": str(height),
                    "distance_unit": dimension_unit,
                }
            )

        result = api_post(
            f"{BASE_URL}/shipments/",
            json=shipment_body,
            headers=_headers(),
            service_name="Shippo",
        )

        rates = result.get("rates", [])
        if not rates:
            return "No shipping rates available for this route."

        lines = [
            f"**Shipping Rates: {from_zip} ({from_country}) → {to_zip} ({to_country})**",
            f"Package: {weight} {weight_unit}",
            "",
        ]

        for i, rate in enumerate(sorted(rates, key=lambda r: float(r.get("amount", "999"))), 1):
            provider = rate.get("provider", "")
            service = rate.get("servicelevel", {}).get("name", "")
            amount = rate.get("amount", "0")
            currency = rate.get("currency", "USD")
            days = rate.get("estimated_days", "")
            rate_id = rate.get("object_id", "")

            lines.append(f"{i}. **{provider} — {service}**")
            line = f"   ${float(amount):.2f} {currency}"
            if days:
                line += f" | {days} day(s)"
            lines.append(line)
            lines.append(f"   Rate ID: {rate_id}")
            lines.append("")

        lines.append("Use `create_label` with a Rate ID to purchase a shipping label.")
        return "\n".join(lines)

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


def create_label(data: dict) -> str:
    """Purchase a shipping label from a rate quote."""
    err = _check_config()
    if err:
        return err

    rate_id = data.get("rate_id", "").strip()
    if not rate_id:
        return "Please provide a rate_id from shipping_rates results."

    try:
        result = api_post(
            f"{BASE_URL}/transactions/",
            json={
                "rate": rate_id,
                "label_file_type": "PDF",
                "async": False,
            },
            headers=_headers(),
            service_name="Shippo",
        )

        status = result.get("status", "")
        tracking = result.get("tracking_number", "")
        label_url = result.get("label_url", "")
        transaction_id = result.get("object_id", "")

        if status == "SUCCESS":
            lines = [
                "**Shipping Label Created!**",
                "",
                f"  Transaction ID: {transaction_id}",
                f"  Tracking Number: {tracking}",
                f"  Label PDF: {label_url}",
            ]
            return "\n".join(lines)

        messages = result.get("messages", [])
        error_msg = ""
        if messages:
            error_msg = "; ".join(
                m.get("text", str(m)) if isinstance(m, dict) else str(m) for m in messages
            )

        return f"Label creation failed (status: {status}). {error_msg}"

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


def track_shipment(data: dict) -> str:
    """Track a shipment by carrier and tracking number."""
    err = _check_config()
    if err:
        return err

    carrier = data.get("carrier", "").strip().lower()
    tracking_number = data.get("tracking_number", "").strip()

    if not tracking_number:
        return "Please provide a tracking_number."
    if not carrier:
        return "Please provide the carrier name: usps, ups, fedex, dhl_express, etc."

    try:
        result = _track_cached(carrier, tracking_number)

        status = result.get("tracking_status", {})
        current_status = status.get("status", "UNKNOWN") if status else "UNKNOWN"
        status_detail = status.get("status_details", "") if status else ""
        location = ""
        if status:
            loc = status.get("location", {})
            if loc:
                city = loc.get("city", "")
                state = loc.get("state", "")
                location = f"{city}, {state}".strip(", ")

        eta = result.get("eta", "")
        carrier_name = result.get("carrier", carrier.upper())

        lines = [
            f"**Tracking: {tracking_number}**",
            f"Carrier: {carrier_name}",
            "",
            f"  Status: **{current_status}**",
        ]
        if status_detail:
            lines.append(f"  Detail: {status_detail}")
        if location:
            lines.append(f"  Location: {location}")
        if eta:
            lines.append(f"  ETA: {eta}")

        # History
        history = result.get("tracking_history", [])
        if history:
            lines.append("")
            lines.append("**Tracking History:**")
            for event in history[-5:]:
                event_status = event.get("status", "")
                event_detail = event.get("status_details", "")
                event_date = event.get("status_date", "")
                event_loc = event.get("location", {})
                loc_str = ""
                if event_loc:
                    loc_str = f" — {event_loc.get('city', '')}, {event_loc.get('state', '')}"
                date_str = event_date[:16] if event_date else ""
                lines.append(f"  {date_str} | {event_status}: {event_detail}{loc_str}")

        return "\n".join(lines)

    except APIError as e:
        return str(e)
    except Exception as e:
        logger.error("Shippo tracking error: %s", e)
        return f"Error tracking shipment: {e}"


@cached(prefix="shippo_track", ttl=600)
def _track_cached(carrier: str, tracking_number: str) -> dict:
    return api_get(
        f"{BASE_URL}/tracks/{carrier}/{tracking_number}",
        headers=_headers(),
        service_name="Shippo",
    )


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

TOOLS = [
    {
        "name": "shipping_rates",
        "description": (
            "Get shipping rate quotes for a package."
            " Provide origin/destination ZIP codes and package weight."
            " Returns rates from USPS, UPS, FedEx, DHL, and more."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "from_zip": {
                    "type": "string",
                    "description": "Sender ZIP code",
                },
                "to_zip": {
                    "type": "string",
                    "description": "Recipient ZIP code",
                },
                "weight": {
                    "type": "number",
                    "description": "Package weight",
                },
                "length": {"type": "number", "description": "Package length"},
                "width": {"type": "number", "description": "Package width"},
                "height": {"type": "number", "description": "Package height"},
                "weight_unit": {
                    "type": "string",
                    "description": "Weight unit: lb or kg (default lb)",
                    "default": "lb",
                },
                "dimension_unit": {
                    "type": "string",
                    "description": "Dimension unit: in or cm (default in)",
                    "default": "in",
                },
                "from_country": {
                    "type": "string",
                    "description": "Sender country code (default US)",
                    "default": "US",
                },
                "to_country": {
                    "type": "string",
                    "description": "Recipient country code (default US)",
                    "default": "US",
                },
            },
            "required": ["from_zip", "to_zip", "weight"],
        },
        "handler": shipping_rates,
        "category": "shippo",
    },
    {
        "name": "create_label",
        "description": (
            "Purchase a shipping label using a rate ID from shipping_rates."
            " Returns tracking number and label PDF download link."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "rate_id": {
                    "type": "string",
                    "description": "Rate ID from shipping_rates results",
                },
            },
            "required": ["rate_id"],
        },
        "handler": create_label,
        "category": "shippo",
    },
    {
        "name": "track_shipment",
        "description": (
            "Track a shipment by carrier and tracking number."
            " Shows current status, location, ETA, and tracking history."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "carrier": {
                    "type": "string",
                    "description": "Carrier: usps, ups, fedex, dhl_express",
                },
                "tracking_number": {
                    "type": "string",
                    "description": "Tracking number",
                },
            },
            "required": ["carrier", "tracking_number"],
        },
        "handler": track_shipment,
        "category": "shippo",
    },
]
