"""John Pye invoice management tools."""

from datetime import datetime
from decimal import Decimal

from pydantic_ai import RunContext
from pydantic_ai.toolsets import FunctionToolset

from pydanticai_multiagent.dependencies import BaseDeps

johnpye_toolset: FunctionToolset[BaseDeps] = FunctionToolset()


# ============================================
# Invoice CRUD
# ============================================


@johnpye_toolset.tool
async def add_invoice(
    ctx: RunContext[BaseDeps],
    invoice_number: str,
    auction_name: str,
    grand_total: Decimal,
    items_subtotal: Decimal | None = None,
    delivery_cost: Decimal = Decimal("0"),
    buyers_premium: Decimal | None = None,
    total_vat: Decimal | None = None,
    delivery_type: str = "collection",
    delivery_method: str | None = None,
    invoice_status: str = "new",
    auction_location: str | None = None,
    viewing_datetime: str | None = None,
    notes: str | None = None,
) -> str:
    """Add a new John Pye invoice.

    Args:
        ctx: Run context with dependencies.
        invoice_number: The 9-digit invoice number (e.g., "453698746").
        auction_name: Auction name (e.g., "Nottingham SR2 - 634 - Appliances").
        grand_total: Total invoice amount in GBP.
        items_subtotal: Sum of item subtotals (optional, for breakdown).
        delivery_cost: Delivery charge in GBP (default 0).
        buyers_premium: Buyer's premium amount (optional, typically 25% of items).
        total_vat: Total VAT amount (optional).
        delivery_type: "collection" or "parcel_delivery".
        delivery_method: e.g., "DPD" for parcel delivery.
        invoice_status: "new" (unpaid) or "paid".
        auction_location: Full address of auction location.
        viewing_datetime: Viewing date/time as text.
        notes: Optional notes.
    """
    # Check for duplicate
    existing = await ctx.deps.db.fetch_one(
        "SELECT 1 FROM johnpye_invoices WHERE user_id = $1 AND invoice_number = $2",
        ctx.deps.user_id,
        invoice_number,
    )
    if existing:
        return f"Invoice {invoice_number} already exists. Use update_invoice_status to modify it."

    await ctx.deps.db.execute(
        """
        INSERT INTO johnpye_invoices (
            user_id, invoice_number, created_at, auction_name, auction_location,
            viewing_datetime, delivery_type, delivery_method,
            items_subtotal_pence, delivery_cost_pence, buyers_premium_pence,
            total_vat_pence, grand_total_pence, invoice_status, notes
        ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
        """,
        ctx.deps.user_id,
        invoice_number,
        datetime.now(),
        auction_name,
        auction_location,
        viewing_datetime,
        delivery_type,
        delivery_method,
        int((items_subtotal or Decimal("0")) * 100),
        int(delivery_cost * 100),
        int((buyers_premium or Decimal("0")) * 100),
        int((total_vat or Decimal("0")) * 100),
        int(grand_total * 100),
        invoice_status,
        notes,
    )
    return f"Added invoice {invoice_number} - {auction_name} - £{grand_total:.2f} ({invoice_status})"


@johnpye_toolset.tool
async def get_invoice(
    ctx: RunContext[BaseDeps],
    invoice_number: str,
) -> str:
    """Get details of a specific invoice including all lot items.

    Args:
        ctx: Run context with dependencies.
        invoice_number: The invoice number to look up.
    """
    invoice = await ctx.deps.db.fetch_one(
        "SELECT * FROM johnpye_invoices WHERE user_id = $1 AND invoice_number = $2",
        ctx.deps.user_id,
        invoice_number,
    )
    if not invoice:
        return f"Invoice {invoice_number} not found."

    items = await ctx.deps.db.fetch_all(
        "SELECT * FROM johnpye_lot_items WHERE invoice_id = $1 ORDER BY lot_number",
        invoice["id"],
    )

    # Format output
    lines = [
        f"Invoice: {invoice['invoice_number']}",
        f"Auction: {invoice['auction_name']}",
        f"Status: {invoice['invoice_status'].upper()}",
        f"Delivery: {invoice.get('delivery_status') or 'N/A'}",
    ]

    if items:
        lines.append("")
        lines.append("Items:")
        for item in items:
            lines.append(
                f"  Lot {item['lot_number']}: {item['description']} - "
                f"£{item['subtotal_pence'] / 100:.2f}"
            )

    lines.extend(
        [
            "",
            f"Items Subtotal: £{invoice.get('items_subtotal_pence', 0) / 100:.2f}",
            f"Delivery: £{invoice.get('delivery_cost_pence', 0) / 100:.2f}",
            f"Buyer's Premium: £{invoice.get('buyers_premium_pence', 0) / 100:.2f}",
            f"VAT: £{invoice.get('total_vat_pence', 0) / 100:.2f}",
            f"Grand Total: £{invoice.get('grand_total_pence', 0) / 100:.2f}",
        ]
    )

    if invoice.get("notes"):
        lines.append(f"\nNotes: {invoice['notes']}")

    return "\n".join(lines)


@johnpye_toolset.tool
async def list_invoices(
    ctx: RunContext[BaseDeps],
    status: str | None = None,
    delivery_status: str | None = None,
    limit: int = 20,
) -> str:
    """List John Pye invoices with optional filters.

    Args:
        ctx: Run context with dependencies.
        status: Filter by invoice status ("new" or "paid").
        delivery_status: Filter by delivery status.
        limit: Maximum invoices to return (default 20).
    """
    conditions = ["user_id = $1"]
    params: list = [ctx.deps.user_id]

    if status:
        params.append(status)
        conditions.append(f"invoice_status = ${len(params)}")

    if delivery_status:
        params.append(delivery_status)
        conditions.append(f"delivery_status = ${len(params)}")

    params.append(limit)
    query = f"""
        SELECT invoice_number, auction_name, grand_total_pence, invoice_status,
               delivery_status, created_at
        FROM johnpye_invoices
        WHERE {' AND '.join(conditions)}
        ORDER BY created_at DESC
        LIMIT ${len(params)}
    """

    invoices = await ctx.deps.db.fetch_all(query, *params)

    if not invoices:
        return "No invoices found."

    lines = [f"Found {len(invoices)} invoice(s):", ""]
    for inv in invoices:
        status_icon = "[PAID]" if inv["invoice_status"] == "paid" else "[NEW]"
        created = inv["created_at"]
        date_str = created.strftime("%Y-%m-%d") if hasattr(created, "strftime") else str(created)[:10]
        lines.append(
            f"{status_icon} {inv['invoice_number']} | £{inv['grand_total_pence'] / 100:.2f} | "
            f"{inv['auction_name'][:40]} | {date_str}"
        )

    return "\n".join(lines)


@johnpye_toolset.tool
async def update_invoice_status(
    ctx: RunContext[BaseDeps],
    invoice_number: str,
    invoice_status: str | None = None,
    delivery_status: str | None = None,
    notes: str | None = None,
) -> str:
    """Update invoice payment or delivery status.

    Args:
        ctx: Run context with dependencies.
        invoice_number: The invoice to update.
        invoice_status: New payment status ("new" or "paid").
        delivery_status: New delivery status ("paid", "preparing", "packed", "shipped", "delivered").
        notes: Optional notes to add.
    """
    updates = []
    params: list = []

    if invoice_status:
        params.append(invoice_status)
        updates.append(f"invoice_status = ${len(params)}")

    if delivery_status:
        params.append(delivery_status)
        updates.append(f"delivery_status = ${len(params)}")

    if notes:
        params.append(notes)
        updates.append(f"notes = ${len(params)}")

    if not updates:
        return "No updates specified."

    params.extend([invoice_number, ctx.deps.user_id])
    query = f"""
        UPDATE johnpye_invoices
        SET {', '.join(updates)}
        WHERE invoice_number = ${len(params) - 1} AND user_id = ${len(params)}
    """

    await ctx.deps.db.execute(query, *params)

    update_desc = []
    if invoice_status:
        update_desc.append(f"status={invoice_status}")
    if delivery_status:
        update_desc.append(f"delivery={delivery_status}")
    if notes:
        update_desc.append("notes updated")

    return f"Updated invoice {invoice_number}: {', '.join(update_desc)}"


@johnpye_toolset.tool
async def delete_invoice(
    ctx: RunContext[BaseDeps],
    invoice_number: str,
) -> str:
    """Delete an invoice and all its lot items.

    Args:
        ctx: Run context with dependencies.
        invoice_number: The invoice to delete.
    """
    await ctx.deps.db.execute(
        "DELETE FROM johnpye_invoices WHERE user_id = $1 AND invoice_number = $2",
        ctx.deps.user_id,
        invoice_number,
    )
    return f"Deleted invoice {invoice_number}"


# ============================================
# Lot Item Management
# ============================================


@johnpye_toolset.tool
async def add_lot_item(
    ctx: RunContext[BaseDeps],
    invoice_number: str,
    lot_number: int,
    description: str,
    hammer_price: Decimal,
    vat: Decimal = Decimal("0"),
    listing_id: str | None = None,
) -> str:
    """Add a lot item to an existing invoice.

    Args:
        ctx: Run context with dependencies.
        invoice_number: The invoice to add the item to.
        lot_number: Lot number from the auction.
        description: Item description.
        hammer_price: Hammer price in GBP.
        vat: VAT amount in GBP (default 0).
        listing_id: Optional John Pye listing reference.
    """
    invoice = await ctx.deps.db.fetch_one(
        "SELECT id FROM johnpye_invoices WHERE user_id = $1 AND invoice_number = $2",
        ctx.deps.user_id,
        invoice_number,
    )
    if not invoice:
        return f"Invoice {invoice_number} not found. Add the invoice first."

    subtotal = hammer_price + vat

    await ctx.deps.db.execute(
        """
        INSERT INTO johnpye_lot_items (invoice_id, lot_number, description,
            hammer_price_pence, vat_pence, subtotal_pence, listing_id)
        VALUES ($1, $2, $3, $4, $5, $6, $7)
        ON CONFLICT (invoice_id, lot_number) DO UPDATE SET
            description = EXCLUDED.description,
            hammer_price_pence = EXCLUDED.hammer_price_pence,
            vat_pence = EXCLUDED.vat_pence,
            subtotal_pence = EXCLUDED.subtotal_pence,
            listing_id = EXCLUDED.listing_id
        """,
        invoice["id"],
        lot_number,
        description,
        int(hammer_price * 100),
        int(vat * 100),
        int(subtotal * 100),
        listing_id,
    )
    return f"Added lot {lot_number} to invoice {invoice_number}: {description} - £{subtotal:.2f}"


# ============================================
# Reports & Analytics
# ============================================


@johnpye_toolset.tool
async def get_spending_summary(
    ctx: RunContext[BaseDeps],
) -> str:
    """Get overall spending summary for John Pye purchases.

    Args:
        ctx: Run context with dependencies.
    """
    result = await ctx.deps.db.fetch_one(
        """
        SELECT
            COUNT(*) as total_invoices,
            COALESCE(SUM(grand_total_pence), 0) as total_spent,
            COALESCE(SUM(CASE WHEN invoice_status = 'new' THEN grand_total_pence ELSE 0 END), 0) as outstanding,
            COALESCE(SUM(buyers_premium_pence), 0) as total_premium,
            COALESCE(SUM(delivery_cost_pence), 0) as total_delivery,
            COALESCE(SUM(total_vat_pence), 0) as total_vat
        FROM johnpye_invoices
        WHERE user_id = $1
        """,
        ctx.deps.user_id,
    )

    items_result = await ctx.deps.db.fetch_one(
        """
        SELECT COUNT(*) as total_items
        FROM johnpye_lot_items li
        JOIN johnpye_invoices i ON li.invoice_id = i.id
        WHERE i.user_id = $1
        """,
        ctx.deps.user_id,
    )

    if not result:
        return "No invoices found."

    total_items = items_result["total_items"] if items_result else 0

    return f"""John Pye Spending Summary
========================
Total Invoices: {result['total_invoices']}
Total Items: {total_items}

Total Spent: £{result['total_spent'] / 100:.2f}
Outstanding: £{result['outstanding'] / 100:.2f}

Breakdown:
  Buyer's Premium: £{result['total_premium'] / 100:.2f}
  Delivery Costs: £{result['total_delivery'] / 100:.2f}
  VAT Paid: £{result['total_vat'] / 100:.2f}"""


@johnpye_toolset.tool
async def get_outstanding_invoices(
    ctx: RunContext[BaseDeps],
) -> str:
    """Get all unpaid invoices.

    Args:
        ctx: Run context with dependencies.
    """
    invoices = await ctx.deps.db.fetch_all(
        """
        SELECT invoice_number, auction_name, grand_total_pence, payment_deadline, created_at
        FROM johnpye_invoices
        WHERE user_id = $1 AND invoice_status = 'new'
        ORDER BY payment_deadline ASC NULLS LAST, created_at DESC
        """,
        ctx.deps.user_id,
    )

    if not invoices:
        return "No outstanding invoices. All paid up!"

    total = sum(inv["grand_total_pence"] for inv in invoices)

    lines = [f"Outstanding Invoices ({len(invoices)}):", f"Total Due: £{total / 100:.2f}", ""]
    for inv in invoices:
        deadline = ""
        if inv.get("payment_deadline"):
            dl = inv["payment_deadline"]
            deadline = f" (Due: {dl.strftime('%Y-%m-%d') if hasattr(dl, 'strftime') else str(dl)[:10]})"
        lines.append(
            f"[UNPAID] {inv['invoice_number']} | £{inv['grand_total_pence'] / 100:.2f} | "
            f"{inv['auction_name'][:30]}{deadline}"
        )

    return "\n".join(lines)


@johnpye_toolset.tool
async def get_awaiting_delivery(
    ctx: RunContext[BaseDeps],
) -> str:
    """Get invoices awaiting delivery (paid but not yet delivered).

    Args:
        ctx: Run context with dependencies.
    """
    invoices = await ctx.deps.db.fetch_all(
        """
        SELECT invoice_number, auction_name, delivery_status, delivery_type, delivery_method
        FROM johnpye_invoices
        WHERE user_id = $1
          AND invoice_status = 'paid'
          AND (delivery_status IS NULL OR delivery_status != 'delivered')
        ORDER BY created_at DESC
        """,
        ctx.deps.user_id,
    )

    if not invoices:
        return "No items awaiting delivery."

    lines = [f"Awaiting Delivery ({len(invoices)}):", ""]
    for inv in invoices:
        status = inv.get("delivery_status") or "pending"
        method = inv.get("delivery_method") or inv.get("delivery_type", "collection")
        lines.append(
            f"[{status.upper()}] {inv['invoice_number']} | {method} | {inv['auction_name'][:30]}"
        )

    return "\n".join(lines)


@johnpye_toolset.tool
async def export_invoices_csv(
    ctx: RunContext[BaseDeps],
) -> str:
    """Export all invoices to CSV format for accounting.

    Args:
        ctx: Run context with dependencies.
    """
    invoices = await ctx.deps.db.fetch_all(
        """
        SELECT invoice_number, created_at, auction_name,
               items_subtotal_pence, delivery_cost_pence, buyers_premium_pence,
               total_vat_pence, grand_total_pence, invoice_status, delivery_status
        FROM johnpye_invoices
        WHERE user_id = $1
        ORDER BY created_at DESC
        """,
        ctx.deps.user_id,
    )

    if not invoices:
        return "No invoices to export."

    lines = [
        "invoice_number,date,auction,items_subtotal,delivery,premium,vat,total,payment_status,delivery_status"
    ]
    for inv in invoices:
        created = inv["created_at"]
        date_str = created.strftime("%Y-%m-%d") if hasattr(created, "strftime") else str(created)[:10]
        lines.append(
            f"{inv['invoice_number']},"
            f"{date_str},"
            f"\"{inv['auction_name']}\","
            f"{inv.get('items_subtotal_pence', 0) / 100:.2f},"
            f"{inv.get('delivery_cost_pence', 0) / 100:.2f},"
            f"{inv.get('buyers_premium_pence', 0) / 100:.2f},"
            f"{inv.get('total_vat_pence', 0) / 100:.2f},"
            f"{inv.get('grand_total_pence', 0) / 100:.2f},"
            f"{inv['invoice_status']},"
            f"{inv.get('delivery_status') or ''}"
        )

    return "\n".join(lines)


# ============================================
# Web Sync
# ============================================


@johnpye_toolset.tool
async def sync_invoices_from_johnpye(
    ctx: RunContext[BaseDeps],
) -> str:
    """Sync all invoices from John Pye website to local database.

    This opens a browser window (required to bypass Cloudflare) and fetches
    all invoices from your John Pye account. Requires a saved browser session
    (run: python -m pydanticai_multiagent.services.johnpye_auth first).

    Args:
        ctx: Run context with dependencies.
    """
    import asyncio

    from pydanticai_multiagent.services.johnpye_scraper import JohnPyeScraper, ScrapedInvoice

    # Run sync scraper in thread pool (Playwright is sync)
    def do_scrape() -> list[ScrapedInvoice]:
        scraper = JohnPyeScraper.from_saved_state(headless=False)
        try:
            return scraper.sync_all_invoices()
        finally:
            scraper.close()

    loop = asyncio.get_event_loop()
    scraped_invoices = await loop.run_in_executor(None, do_scrape)

    # Store each invoice and its lot items
    total_items = 0
    new_invoices = 0
    updated_invoices = 0

    for inv in scraped_invoices:
        # Check if invoice exists
        existing = await ctx.deps.db.fetch_one(
            "SELECT id FROM johnpye_invoices WHERE user_id = $1 AND invoice_number = $2",
            ctx.deps.user_id,
            inv.invoice_number,
        )

        # Calculate items subtotal
        items_subtotal = sum(lot.subtotal for lot in inv.lot_items)

        if existing:
            # Update existing invoice
            await ctx.deps.db.execute(
                """
                UPDATE johnpye_invoices SET
                    invoice_status = $1,
                    delivery_status = $2,
                    items_subtotal_pence = $3,
                    delivery_cost_pence = $4,
                    buyers_premium_pence = $5,
                    total_vat_pence = $6,
                    grand_total_pence = $7
                WHERE id = $8
                """,
                inv.status,
                inv.delivery_status,
                items_subtotal,
                inv.delivery_pence,
                inv.buyers_premium_pence,
                inv.total_vat_pence,
                inv.grand_total_pence,
                existing["id"],
            )
            invoice_id = existing["id"]
            updated_invoices += 1
        else:
            # Insert new invoice
            result = await ctx.deps.db.fetch_one(
                """
                INSERT INTO johnpye_invoices (
                    user_id, invoice_number, created_at, auction_name,
                    delivery_type, items_subtotal_pence, delivery_cost_pence,
                    buyers_premium_pence, total_vat_pence, grand_total_pence,
                    invoice_status, delivery_status
                ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
                RETURNING id
                """,
                ctx.deps.user_id,
                inv.invoice_number,
                inv.date,
                inv.site,
                "parcel_delivery",
                items_subtotal,
                inv.delivery_pence,
                inv.buyers_premium_pence,
                inv.total_vat_pence,
                inv.grand_total_pence,
                inv.status,
                inv.delivery_status,
            )
            invoice_id = result["id"] if result else None
            new_invoices += 1

        # Insert/update lot items
        if invoice_id:
            for lot in inv.lot_items:
                await ctx.deps.db.execute(
                    """
                    INSERT INTO johnpye_lot_items (
                        invoice_id, lot_number, description,
                        hammer_price_pence, vat_pence, subtotal_pence, listing_id
                    ) VALUES ($1, $2, $3, $4, $5, $6, $7)
                    ON CONFLICT (invoice_id, lot_number) DO UPDATE SET
                        description = EXCLUDED.description,
                        hammer_price_pence = EXCLUDED.hammer_price_pence,
                        vat_pence = EXCLUDED.vat_pence,
                        subtotal_pence = EXCLUDED.subtotal_pence,
                        listing_id = EXCLUDED.listing_id
                    """,
                    invoice_id,
                    int(lot.lot_number),
                    lot.description,
                    lot.hammer_price,
                    lot.vat,
                    lot.subtotal,
                    lot.listing_url,
                )
                total_items += 1

    return f"""Sync Complete!
================
New invoices: {new_invoices}
Updated invoices: {updated_invoices}
Total lot items: {total_items}

Run 'list invoices' or 'spending summary' to see your data."""
