import { apiFetch, type ApiSettings } from "@govoplan/core-webui"; export type AddressBookScope = "user" | "group" | "tenant" | "system"; export type AddressBook = { id: string; tenant_id?: string | null; scope_type: AddressBookScope; scope_id?: string | null; name: string; description?: string | null; source_kind: string; source_ref?: string | null; read_only: boolean; sync_status?: string | null; sync_error?: string | null; contact_count: number; deleted_at?: string | null; created_at: string; updated_at: string; }; export type AddressList = { id: string; tenant_id?: string | null; address_book_id: string; name: string; description?: string | null; source_kind: string; source_ref?: string | null; read_only: boolean; entry_count: number; deleted_at?: string | null; created_at: string; updated_at: string; }; export type ContactEmail = { id?: string; label?: string | null; email: string; is_primary: boolean; }; export type ContactPhone = { id?: string; label?: string | null; phone: string; is_primary: boolean; }; export type ContactPostalAddress = { id?: string; label?: string | null; street?: string | null; postal_code?: string | null; locality?: string | null; region?: string | null; country?: string | null; is_primary: boolean; }; export type Contact = { id: string; tenant_id?: string | null; address_book_id: string; display_name: string; given_name?: string | null; family_name?: string | null; organization?: string | null; role_title?: string | null; note?: string | null; tags: string[]; source_kind: string; source_ref?: string | null; source_payload_kind?: string | null; source_revision?: string | null; provenance: Record; emails: ContactEmail[]; phones: ContactPhone[]; postal_addresses: ContactPostalAddress[]; deleted_at?: string | null; created_at: string; updated_at: string; }; export type AddressBookCreatePayload = { scope_type: AddressBookScope; group_id?: string | null; name: string; description?: string | null; }; export type AddressBookUpdatePayload = { name?: string | null; description?: string | null; }; export type AddressListPayload = { name: string; description?: string | null; }; export type AddressListEntryPayload = { contact_id: string; contact_email_id?: string | null; contact_postal_address_id?: string | null; label?: string | null; }; export type AddressListEntry = { id: string; address_list_id: string; contact_id: string; contact_email_id?: string | null; contact_postal_address_id?: string | null; target_kind: string; label?: string | null; order_index: number; contact_display_name: string; email?: string | null; postal_address?: string | null; created_at: string; updated_at: string; }; export type ContactPayload = { display_name?: string | null; given_name?: string | null; family_name?: string | null; organization?: string | null; role_title?: string | null; note?: string | null; tags?: string[]; emails?: Array>; phones?: Array>; postal_addresses?: Array>; provenance?: Record; }; export type AddressBookWriteOperation = "create_contact" | "update_contact" | "delete_contact"; export type AddressBookWriteDecision = { address_book_id: string; address_book_label?: string | null; operation: string; allowed: boolean; reason: string; message: string; scope_type?: AddressBookScope | null; scope_id?: string | null; tenant_id?: string | null; source_kind?: string | null; read_only: boolean; required_scopes: string[]; provenance: Record; }; type AddressBookListResponse = { address_books: AddressBook[]; }; type AddressListListResponse = { address_lists: AddressList[]; }; type AddressListEntryListResponse = { entries: AddressListEntry[]; }; type ContactListResponse = { contacts: Contact[]; }; type AddressBookWriteTargetsResponse = { targets: AddressBookWriteDecision[]; }; export type VCardImportResult = { imported: number; skipped: number; contacts: Contact[]; issues: Array<{ index: number; message: string; severity: "warning" | "error"; field?: string | null; line?: number | null }>; }; export type AddressSyncSource = { id: string; tenant_id?: string | null; address_book_id: string; connector_type: string; display_name: string; external_account_ref?: string | null; external_address_book_ref?: string | null; sync_direction: string; read_only: boolean; enabled: boolean; status: string; sync_token?: string | null; etag?: string | null; remote_revision?: string | null; last_attempted_at?: string | null; last_success_at?: string | null; last_error?: string | null; last_diagnostic?: Record | null; metadata: Record; created_at: string; updated_at: string; }; export type AddressCardDavAddressBook = { collection_url: string; href: string; display_name?: string | null; description?: string | null; ctag?: string | null; sync_token?: string | null; }; export type AddressCredentialEnvelope = { id: string; scope_type: string; scope_id?: string | null; name: string; description?: string | null; credential_kind: string; public_data: Record; secret_keys: string[]; secret_configured: boolean; allowed_modules: string[]; inherit_to_lower_scopes: boolean; is_active: boolean; revision: string; }; export type AddressSyncPlanStats = { created: number; updated: number; deleted: number; conflicts: number; unchanged: number; errors: number; fetched: number; full_sync: boolean; used_sync_token: boolean; sync_token?: string | null; etag?: string | null; remote_revision?: string | null; }; export type AddressSyncPlanItem = { action: "create" | "update" | "delete" | "remote_create" | "remote_update" | "remote_delete" | "conflict" | "unchanged" | "error"; href?: string | null; remote_uid?: string | null; contact_id?: string | null; display_name?: string | null; etag?: string | null; message?: string | null; }; export type AddressSyncPlan = { sync_source: AddressSyncSource; stats: AddressSyncPlanStats; items: AddressSyncPlanItem[]; }; export type AddressSyncDiagnostic = { id: string; tenant_id?: string | null; sync_source_id: string; severity: string; code: string; message: string; details: Record; created_at: string; updated_at: string; }; export type AddressSyncTombstone = { id: string; tenant_id?: string | null; sync_source_id: string; address_book_id: string; contact_id?: string | null; remote_uid?: string | null; resource_href?: string | null; local_deleted_at?: string | null; remote_deleted_at?: string | null; synced_at?: string | null; metadata: Record; created_at: string; updated_at: string; }; export type AddressSyncConflict = { id: string; tenant_id?: string | null; sync_source_id: string; address_book_id: string; contact_id?: string | null; remote_uid?: string | null; resource_href?: string | null; field_path: string; local_value?: Record | null; remote_value?: Record | null; local_updated_at?: string | null; remote_updated_at?: string | null; status: string; resolution?: string | null; resolved_at?: string | null; resolved_by_account_id?: string | null; metadata: Record; created_at: string; updated_at: string; }; type AddressSyncSourceListResponse = { sync_sources: AddressSyncSource[]; }; type AddressCardDavDiscoveryResponse = { address_books: AddressCardDavAddressBook[]; }; type AddressCredentialEnvelopeListResponse = { credentials: AddressCredentialEnvelope[]; }; type AddressSyncDiagnosticListResponse = { diagnostics: AddressSyncDiagnostic[]; }; type AddressSyncTombstoneListResponse = { tombstones: AddressSyncTombstone[]; }; type AddressSyncConflictListResponse = { conflicts: AddressSyncConflict[]; }; function queryString(params: Record): string { const search = new URLSearchParams(); for (const [key, value] of Object.entries(params)) { if (value !== null && value !== undefined && value !== "") search.set(key, String(value)); } const serialized = search.toString(); return serialized ? `?${serialized}` : ""; } export async function listAddressBooks(settings: ApiSettings, options: {includeDeleted?: boolean;} = {}): Promise { const response = await apiFetch( settings, `/api/v1/addresses/address-books${queryString({ include_deleted: options.includeDeleted ? "true" : null })}` ); return response.address_books; } export function createAddressBook(settings: ApiSettings, payload: AddressBookCreatePayload): Promise { return apiFetch(settings, "/api/v1/addresses/address-books", { method: "POST", body: JSON.stringify(payload) }); } export function updateAddressBook(settings: ApiSettings, bookId: string, payload: AddressBookUpdatePayload): Promise { return apiFetch(settings, `/api/v1/addresses/address-books/${bookId}`, { method: "PATCH", body: JSON.stringify(payload) }); } export function deleteAddressBook(settings: ApiSettings, bookId: string): Promise { return apiFetch(settings, `/api/v1/addresses/address-books/${bookId}`, { method: "DELETE" }); } export function restoreAddressBook(settings: ApiSettings, bookId: string): Promise { return apiFetch(settings, `/api/v1/addresses/address-books/${bookId}/restore`, { method: "POST" }); } export async function listAddressLists( settings: ApiSettings, options: { addressBookId?: string | null; includeDeleted?: boolean } = {} ): Promise { const response = await apiFetch( settings, `/api/v1/addresses/address-lists${queryString({ address_book_id: options.addressBookId, include_deleted: options.includeDeleted ? "true" : null })}` ); return response.address_lists; } export function createAddressList(settings: ApiSettings, addressBookId: string, payload: AddressListPayload): Promise { return apiFetch(settings, `/api/v1/addresses/address-books/${addressBookId}/address-lists`, { method: "POST", body: JSON.stringify(payload) }); } export function updateAddressList(settings: ApiSettings, addressListId: string, payload: Partial): Promise { return apiFetch(settings, `/api/v1/addresses/address-lists/${addressListId}`, { method: "PATCH", body: JSON.stringify(payload) }); } export function deleteAddressList(settings: ApiSettings, addressListId: string): Promise { return apiFetch(settings, `/api/v1/addresses/address-lists/${addressListId}`, { method: "DELETE" }); } export function restoreAddressList(settings: ApiSettings, addressListId: string): Promise { return apiFetch(settings, `/api/v1/addresses/address-lists/${addressListId}/restore`, { method: "POST" }); } export async function listAddressListEntries(settings: ApiSettings, addressListId: string): Promise { const response = await apiFetch(settings, `/api/v1/addresses/address-lists/${addressListId}/entries`); return response.entries; } export function createAddressListEntry( settings: ApiSettings, addressListId: string, payload: AddressListEntryPayload ): Promise { return apiFetch(settings, `/api/v1/addresses/address-lists/${addressListId}/entries`, { method: "POST", body: JSON.stringify(payload) }); } export function deleteAddressListEntry(settings: ApiSettings, entryId: string): Promise { return apiFetch(settings, `/api/v1/addresses/address-list-entries/${entryId}`, { method: "DELETE" }); } export async function listAddressBookWriteTargets( settings: ApiSettings, operation: AddressBookWriteOperation = "create_contact" ): Promise { const response = await apiFetch( settings, `/api/v1/addresses/write-targets${queryString({ operation })}` ); return response.targets; } export function getAddressBookWriteDecision( settings: ApiSettings, addressBookId: string, operation: AddressBookWriteOperation = "create_contact" ): Promise { return apiFetch( settings, `/api/v1/addresses/address-books/${addressBookId}/write-decision${queryString({ operation })}` ); } export async function listAddressSyncSources( settings: ApiSettings, options: { addressBookId?: string | null; includeDisabled?: boolean } = {} ): Promise { const response = await apiFetch( settings, `/api/v1/addresses/sync-sources${queryString({ address_book_id: options.addressBookId, include_disabled: options.includeDisabled ? "true" : null })}` ); return response.sync_sources; } export function discoverCardDavAddressBooks( settings: ApiSettings, payload: { url: string; auth_type: "none" | "basic" | "bearer"; username?: string | null; password?: string | null; bearer_token?: string | null; credential_ref?: string | null; } ): Promise { return apiFetch(settings, "/api/v1/addresses/carddav/discover", { method: "POST", body: JSON.stringify(payload) }).then((response) => response.address_books); } export function createCardDavSyncSource( settings: ApiSettings, addressBookId: string, payload: { collection_url: string; display_name?: string | null; auth_type: "none" | "basic" | "bearer"; username?: string | null; password?: string | null; bearer_token?: string | null; credential_ref?: string | null; sync_direction: "read_only" | "import" | "export" | "two_way"; read_only?: boolean | null; sync_token?: string | null; etag?: string | null; remote_revision?: string | null; } ): Promise { return apiFetch(settings, `/api/v1/addresses/address-books/${addressBookId}/carddav/sources`, { method: "POST", body: JSON.stringify(payload) }); } export async function listAddressCredentials( settings: ApiSettings, sourceId?: string | null ): Promise { const response = await apiFetch( settings, `/api/v1/addresses/credentials${queryString({ source_id: sourceId })}` ); return response.credentials; } export function updateAddressSyncSource( settings: ApiSettings, syncSourceId: string, payload: Partial<{ display_name: string | null; external_account_ref: string | null; external_address_book_ref: string | null; sync_direction: "read_only" | "import" | "export" | "two_way"; read_only: boolean | null; enabled: boolean | null; sync_token: string | null; etag: string | null; remote_revision: string | null; metadata: Record | null; }> ): Promise { return apiFetch(settings, `/api/v1/addresses/sync-sources/${syncSourceId}`, { method: "PATCH", body: JSON.stringify(payload) }); } export function deleteAddressSyncSource(settings: ApiSettings, syncSourceId: string): Promise { return apiFetch(settings, `/api/v1/addresses/sync-sources/${syncSourceId}`, { method: "DELETE" }); } export function previewAddressSyncSource(settings: ApiSettings, syncSourceId: string, options: { forceFull?: boolean } = {}): Promise { return apiFetch(settings, `/api/v1/addresses/sync-sources/${syncSourceId}/preview`, { method: "POST", body: JSON.stringify({ force_full: Boolean(options.forceFull) }) }); } export function runAddressSyncSource(settings: ApiSettings, syncSourceId: string, options: { forceFull?: boolean } = {}): Promise { return apiFetch(settings, `/api/v1/addresses/sync-sources/${syncSourceId}/run`, { method: "POST", body: JSON.stringify({ force_full: Boolean(options.forceFull) }) }); } export async function listAddressSyncDiagnostics(settings: ApiSettings, syncSourceId: string): Promise { const response = await apiFetch(settings, `/api/v1/addresses/sync-sources/${syncSourceId}/diagnostics`); return response.diagnostics; } export async function listAddressSyncTombstones(settings: ApiSettings, syncSourceId: string): Promise { const response = await apiFetch(settings, `/api/v1/addresses/sync-sources/${syncSourceId}/tombstones`); return response.tombstones; } export async function listAddressSyncConflicts(settings: ApiSettings, syncSourceId: string, status = "open"): Promise { const response = await apiFetch(settings, `/api/v1/addresses/sync-sources/${syncSourceId}/conflicts${queryString({ status })}`); return response.conflicts; } export function resolveAddressSyncConflict( settings: ApiSettings, conflictId: string, resolution = "manual", status = "resolved", options: { mergedPayload?: Record | null } = {} ): Promise { return apiFetch(settings, `/api/v1/addresses/sync-conflicts/${conflictId}/resolve`, { method: "POST", body: JSON.stringify({ resolution, status, merged_payload: options.mergedPayload ?? null }) }); } export async function listContacts(settings: ApiSettings, options: {addressBookId?: string | null;query?: string | null;limit?: number;includeDeleted?: boolean;} = {}): Promise { const response = await apiFetch( settings, `/api/v1/addresses/contacts${queryString({ address_book_id: options.addressBookId, query: options.query, limit: options.limit, include_deleted: options.includeDeleted ? "true" : null })}` ); return response.contacts; } export function createContact(settings: ApiSettings, addressBookId: string, payload: ContactPayload): Promise { return apiFetch(settings, `/api/v1/addresses/address-books/${addressBookId}/contacts`, { method: "POST", body: JSON.stringify(payload) }); } export function updateContact(settings: ApiSettings, contactId: string, payload: ContactPayload): Promise { return apiFetch(settings, `/api/v1/addresses/contacts/${contactId}`, { method: "PATCH", body: JSON.stringify(payload) }); } export function deleteContact(settings: ApiSettings, contactId: string): Promise { return apiFetch(settings, `/api/v1/addresses/contacts/${contactId}`, { method: "DELETE" }); } export function restoreContact(settings: ApiSettings, contactId: string): Promise { return apiFetch(settings, `/api/v1/addresses/contacts/${contactId}/restore`, { method: "POST" }); } export function importAddressBookVcards(settings: ApiSettings, addressBookId: string, content: string): Promise { return apiFetch(settings, `/api/v1/addresses/address-books/${addressBookId}/vcards/import`, { method: "POST", body: JSON.stringify({ content }) }); } export function exportAddressBookVcards(settings: ApiSettings, addressBookId: string): Promise { return apiFetch(settings, `/api/v1/addresses/address-books/${addressBookId}/vcards/export`); } export function exportContactVcard(settings: ApiSettings, contactId: string): Promise { return apiFetch(settings, `/api/v1/addresses/contacts/${contactId}/vcard`); }