/**
 * Billing Service
 *
 * Handles all billing-related API calls.
 * Hooks should use this service instead of calling the API directly.
 */

import { apiClient, getDataOrDefault, getDataOrThrow, type QueryParams } from "@/core/api";
import {
  type Invoice,
  type InvoiceList,
  type InvoiceSsoLink,
  type InvoiceQueryParams,
  invoiceSchema,
  invoiceListSchema,
} from "@customer-portal/domain/billing";
import { type PaymentMethodList } from "@customer-portal/domain/payments";

// ============================================================================
// Constants
// ============================================================================

const EMPTY_INVOICE_LIST: InvoiceList = {
  invoices: [],
  pagination: {
    page: 1,
    totalItems: 0,
    totalPages: 0,
  },
};

const EMPTY_PAYMENT_METHODS: PaymentMethodList = {
  paymentMethods: [],
  totalCount: 0,
};

// ============================================================================
// Helpers
// ============================================================================

function toQueryParams(params: InvoiceQueryParams): QueryParams {
  const query: QueryParams = {};
  for (const [key, value] of Object.entries(params)) {
    if (value === undefined) {
      continue;
    }
    if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
      query[key] = value;
    }
  }
  return query;
}

// ============================================================================
// API Functions
// ============================================================================

async function getInvoices(params?: InvoiceQueryParams): Promise<InvoiceList> {
  const query = params ? toQueryParams(params) : undefined;
  const response = await apiClient.GET<InvoiceList>(
    "/api/invoices",
    query ? { params: { query } } : undefined
  );
  const data = getDataOrDefault(response, EMPTY_INVOICE_LIST);
  // Defense-in-depth: Re-validate BFF response to ensure type safety
  // even if BFF contract changes. Minimal overhead, maximum safety.
  return invoiceListSchema.parse(data);
}

async function getInvoice(id: string): Promise<Invoice> {
  const response = await apiClient.GET<Invoice>("/api/invoices/{id}", {
    params: { path: { id } },
  });
  const invoice = getDataOrThrow(response, "Invoice not found");
  // Defense-in-depth: Re-validate BFF response to ensure type safety
  // even if BFF contract changes. Minimal overhead, maximum safety.
  return invoiceSchema.parse(invoice);
}

async function getPaymentMethods(): Promise<PaymentMethodList> {
  const response = await apiClient.GET<PaymentMethodList>("/api/invoices/payment-methods");
  return getDataOrDefault(response, EMPTY_PAYMENT_METHODS);
}

async function createInvoiceSsoLink(
  invoiceId: number,
  target?: "view" | "download" | "pay"
): Promise<InvoiceSsoLink> {
  const response = await apiClient.POST<InvoiceSsoLink>("/api/invoices/{id}/sso-link", {
    params: target ? { path: { id: invoiceId }, query: { target } } : { path: { id: invoiceId } },
  });
  return getDataOrThrow(response, "Failed to create SSO link");
}

async function createPaymentMethodsSsoLink(): Promise<InvoiceSsoLink> {
  const response = await apiClient.POST<InvoiceSsoLink>("/api/auth/sso-link", {
    body: { destination: "index.php?rp=/account/paymentmethods" },
  });
  return getDataOrThrow(response, "Failed to create payment methods SSO link");
}

// ============================================================================
// Service Export
// ============================================================================

export const billingService = {
  getInvoices,
  getInvoice,
  getPaymentMethods,
  createInvoiceSsoLink,
  createPaymentMethodsSsoLink,
} as const;

// Re-export constants for use in hooks
export { EMPTY_INVOICE_LIST, EMPTY_PAYMENT_METHODS };
