"""SnelStart invoice push boundary."""

from __future__ import annotations

from app.services.invoice_workflow import (
    STATUS_SYNCED,
    STATUS_SYNC_FAILED,
    SYNCABLE_STATUSES,
    assert_transition,
    display_label,
    utc_now_iso,
    WorkflowError,
)
from app.services.snelstart.connection import integration_enabled, is_client_connected, record_sync_attempt
from app.services.snelstart.exceptions import SnelStartIntegrationDisabled, SnelStartNotConnected
from app.services.snelstart.models import InvoicePushResult
from app.store import Invoice, store


def _require_pushable(invoice: Invoice) -> None:
    if invoice.status not in SYNCABLE_STATUSES:
        raise WorkflowError(
            "Only approved documents can be synced to SnelStart."
        )


def push_invoice(invoice_id: str, *, actor: str) -> Invoice:
    """
    Push an approved invoice to SnelStart.

    Until real API credentials are configured, this always fails honestly and
    leaves the invoice in Approved (or Sync failed on a real API error later).
    """
    invoice = store._find_invoice(invoice_id)  # noqa: SLF001
    _require_pushable(invoice)

    client = store.get_client(invoice.clientId)
    legacy_flag = bool(client and client.snelstart)

    if not integration_enabled():
        record_sync_attempt(invoice.clientId, error="SnelStart integration is not enabled.")
        store._log(  # noqa: SLF001
            "sync-failed",
            f"SnelStart sync attempted — integration unavailable for {display_label(invoice.file, invoice.supplier)}",
            actor,
            invoice.clientId,
            invoice_id,
        )
        store._persist()
        raise SnelStartIntegrationDisabled()

    if not is_client_connected(invoice.clientId, legacy_snelstart=legacy_flag):
        record_sync_attempt(invoice.clientId, error="SnelStart is not connected for this client.")
        store._log(  # noqa: SLF001
            "sync-failed",
            f"SnelStart sync attempted — connection unavailable for {display_label(invoice.file, invoice.supplier)}",
            actor,
            invoice.clientId,
            invoice_id,
        )
        store._persist()
        raise SnelStartNotConnected()

    # Future: call SnelStart HTTP API here and map response → InvoicePushResult
    raise SnelStartIntegrationDisabled(
        "SnelStart invoice push is not implemented yet.",
        user_message="SnelStart invoice sync is not available yet.",
    )


def apply_push_success(invoice_id: str, result: InvoicePushResult, *, actor: str) -> Invoice:
    """Apply a successful SnelStart push — only called after a real API success."""
    invoice = store._find_invoice(invoice_id)  # noqa: SLF001
    assert_transition(invoice.status, STATUS_SYNCED)
    now = utc_now_iso()
    updated = invoice.model_copy(
        update={
            "status": STATUS_SYNCED,
            "syncError": None,
            "externalInvoiceId": result.externalInvoiceId,
            "syncedAt": result.syncedAt,
            "updatedAt": now,
        }
    )
    store._replace_invoice(invoice_id, updated, persist=False)  # noqa: SLF001
    store._log(  # noqa: SLF001
        "sync",
        f"Invoice {invoice.number or invoice.file} synced to SnelStart",
        actor,
        invoice.clientId,
        invoice_id,
    )
    record_sync_attempt(invoice.clientId, success=True)
    try:
        from app.services import notifications as notif

        notif.on_sync_success(
            client_id=invoice.clientId,
            invoice_id=invoice_id,
            number=invoice.number or invoice.file,
        )
    except Exception:
        pass
    store._persist()
    return updated


def apply_push_failure(invoice_id: str, *, actor: str, error: str) -> Invoice:
    """Record a failed push after a real API attempt."""
    invoice = store._find_invoice(invoice_id)  # noqa: SLF001
    if invoice.status == STATUS_APPROVED:
        assert_transition(invoice.status, STATUS_SYNC_FAILED)
    now = utc_now_iso()
    updated = invoice.model_copy(
        update={
            "status": STATUS_SYNC_FAILED,
            "syncError": error[:2000],
            "updatedAt": now,
        }
    )
    store._replace_invoice(invoice_id, updated, persist=False)  # noqa: SLF001
    store._log(  # noqa: SLF001
        "sync-failed",
        f"SnelStart sync failed for {invoice.number or invoice.file}",
        actor,
        invoice.clientId,
        invoice_id,
    )
    record_sync_attempt(invoice.clientId, error=error)
    try:
        from app.services import notifications as notif

        notif.on_sync_failed(
            client_id=invoice.clientId,
            invoice_id=invoice_id,
            number=invoice.number or invoice.file,
        )
    except Exception:
        pass
    store._persist()
    return updated


# Future interface stubs — no HTTP calls yet.
def create_invoice(*_args, **_kwargs):
    raise SnelStartIntegrationDisabled("create_invoice is not implemented yet.")


def update_invoice(*_args, **_kwargs):
    raise SnelStartIntegrationDisabled("update_invoice is not implemented yet.")


def get_invoice(*_args, **_kwargs):
    raise SnelStartIntegrationDisabled("get_invoice is not implemented yet.")


def get_invoice_status(*_args, **_kwargs):
    raise SnelStartIntegrationDisabled("get_invoice_status is not implemented yet.")
