/**
 * Helpers purs des ecrans Consultation / Modification prestataire (M3 Technique,
 * ERP-145) — miroir SIMPLIFIE de `supplierConsultation.ts` (M2). Mappent le payload
 * `GET /api/providers/{id}` (relations embarquees, cf. groupes `provider:item:read`
 * + `provider:read:accounting`) vers les brouillons « plats » partages avec
 * `ProviderContactBlock` / `ProviderAddressBlock` et l'onglet Comptabilite.
 *
 * Ne touchent ni a l'API ni a l'etat reactif (testables unitairement).
 *
 * Rappels de contrat back (JSON reel fige — ERP-139, spec-back § 4.0.bis) :
 *  - categories / sites du prestataire et des adresses : OBJETS embarques (avec @id) ;
 *  - refs comptables (tvaMode/paymentDelay/paymentType/bank) : OBJETS embarques
 *    `{@id, id, label, (code pour paymentType)}` ;
 *  - champs nuls OMIS (skip_null_values) → toujours lire avec `?? null` ;
 *  - champs comptables + `ribs` TOTALEMENT ABSENTS sans permission accounting.view.
 *
 * Differences M2 : pas de type d'adresse / bennes / triage, pas d'onglet Information.
 */

import { formatPhoneFR } from '~/shared/utils/phone'
import type {
    ProviderAccountingDraft,
    ProviderAddressFormDraft,
    ProviderContactFormDraft,
    ProviderRibFormDraft,
} from '~/modules/technique/types/providerForm'
import type { RefOption } from '~/modules/technique/composables/useProviderReferentials'

/** Reference Hydra embarquee minimale (@id toujours present). */
export interface HydraRef {
    '@id': string
    [key: string]: unknown
}

/** Une relation peut etre embarquee (objet), un IRI nu (chaine) ou absente. */
export type Relation = HydraRef | string | null | undefined

/** Site embarque (groupe site:read). */
export interface SiteRead extends HydraRef {
    name?: string
    postalCode?: string
    color?: string
}

/** Categorie embarquee (groupe category:read). */
export interface CategoryRead extends HydraRef {
    code?: string
    name?: string
}

/** Contact embarque (groupe provider:item:read). */
export interface ContactRead extends HydraRef {
    id: number
    firstName?: string | null
    lastName?: string | null
    jobTitle?: string | null
    phonePrimary?: string | null
    phoneSecondary?: string | null
    email?: string | null
}

/** Adresse embarquee (groupe provider:item:read) — version simplifiee M3. */
export interface AddressRead extends HydraRef {
    id: number
    country?: string | null
    postalCode?: string | null
    city?: string | null
    street?: string | null
    streetComplement?: string | null
    sites?: SiteRead[]
    categories?: CategoryRead[]
    // L'embed M2M des contacts d'adresse peut etre un objet (partiel) ou un IRI nu.
    contacts?: Array<HydraRef | string>
}

/** RIB embarque (groupe provider:read:accounting, present ssi accounting.view). */
export interface RibRead extends HydraRef {
    id: number
    label?: string | null
    bic?: string | null
    iban?: string | null
}

/**
 * Detail d'un prestataire (`GET /api/providers/{id}`). Tous les champs sont
 * optionnels : skip_null_values + gating accounting peuvent omettre n'importe
 * quelle cle.
 */
export interface ProviderDetail extends HydraRef {
    id: number
    companyName?: string | null
    isArchived?: boolean
    categories?: CategoryRead[]
    sites?: SiteRead[]
    contacts?: ContactRead[]
    addresses?: AddressRead[]
    ribs?: RibRead[]
    // Onglet Comptabilite (present ssi accounting.view)
    siren?: string | null
    accountNumber?: string | null
    nTva?: string | null
    tvaMode?: Relation
    paymentDelay?: Relation
    paymentType?: Relation
    bank?: Relation
}

/** Extrait l'IRI d'une relation (objet embarque, IRI nu, ou null si absente). */
export function iriOf(relation: Relation): string | null {
    if (relation === null || relation === undefined) {
        return null
    }
    if (typeof relation === 'string') {
        return relation
    }
    return relation['@id'] ?? null
}

/** IRI des elements d'une collection embarquee (categories / sites du prestataire). */
export function irisOf(items: HydraRef[] | undefined): string[] {
    return (items ?? []).map(i => i['@id'])
}

/** Mappe un contact embarque vers un brouillon (telephones formates XX XX XX XX XX). */
export function mapContactToDraft(contact: ContactRead): ProviderContactFormDraft {
    const phoneSecondary = contact.phoneSecondary ?? null
    return {
        id: contact.id,
        iri: contact['@id'] ?? null,
        firstName: contact.firstName ?? null,
        lastName: contact.lastName ?? null,
        jobTitle: contact.jobTitle ?? null,
        phonePrimary: contact.phonePrimary ? formatPhoneFR(contact.phonePrimary) : null,
        phoneSecondary: phoneSecondary ? formatPhoneFR(phoneSecondary) : null,
        email: contact.email ?? null,
        hasSecondaryPhone: phoneSecondary !== null && phoneSecondary !== '',
    }
}

/** Mappe une adresse embarquee vers un brouillon (IRI extraits des sous-collections). */
export function mapAddressToDraft(address: AddressRead): ProviderAddressFormDraft {
    return {
        id: address.id,
        country: address.country ?? 'France',
        postalCode: address.postalCode ?? null,
        city: address.city ?? null,
        street: address.street ?? null,
        streetComplement: address.streetComplement ?? null,
        categoryIris: (address.categories ?? []).map(c => c['@id']),
        siteIris: (address.sites ?? []).map(s => s['@id']),
        contactIris: (address.contacts ?? []).map(c => (typeof c === 'string' ? c : c['@id'])),
    }
}

/** Mappe un RIB embarque vers un brouillon. */
export function mapRibToDraft(rib: RibRead): ProviderRibFormDraft {
    return {
        id: rib.id,
        label: rib.label ?? null,
        bic: rib.bic ?? null,
        iban: rib.iban ?? null,
    }
}

/** Mappe les champs comptables (scalaires + IRI des referentiels embarques). */
export function mapAccountingDraft(provider: ProviderDetail): ProviderAccountingDraft {
    return {
        siren: provider.siren ?? null,
        accountNumber: provider.accountNumber ?? null,
        nTva: provider.nTva ?? null,
        tvaModeIri: iriOf(provider.tvaMode),
        paymentDelayIri: iriOf(provider.paymentDelay),
        paymentTypeIri: iriOf(provider.paymentType),
        bankIri: iriOf(provider.bank),
    }
}

/**
 * Options de categories (value=IRI, label=nom) construites depuis l'embed.
 * Source role-independante : evite de dependre de `GET /categories` (403 possible
 * pour un role metier), qui laisserait les libelles vides en consultation.
 */
export function categoryOptionsOf(categories: CategoryRead[] | undefined): RefOption[] {
    return (categories ?? []).map(c => ({
        value: c['@id'],
        label: c.name ?? c.code ?? c['@id'],
    }))
}

/** Options de sites (value=IRI, label=nom) construites depuis un embed. */
export function siteOptionsOf(sites: SiteRead[] | undefined): RefOption[] {
    return (sites ?? []).map(s => ({ value: s['@id'], label: s.name ?? s['@id'] }))
}

/** Options de contacts (value=IRI, label=nom complet ou email) depuis l'embed prestataire. */
export function contactOptionsOf(contacts: ContactRead[] | undefined): RefOption[] {
    return (contacts ?? []).map(c => ({
        value: c['@id'],
        label: [c.firstName, c.lastName].filter(Boolean).join(' ') || (c.email ?? c['@id']),
    }))
}

/**
 * Liste a une seule option (ou vide) construite depuis un referentiel embarque
 * (TvaMode / PaymentDelay / PaymentType / Bank) pour alimenter un MalioSelect en
 * lecture seule. Le libelle vient de l'embed, jamais d'un GET de referentiel —
 * l'affichage reste correct quel que soit le role.
 */
export function referentialOptionOf(relation: Relation): RefOption[] {
    if (!relation || typeof relation === 'string') {
        return []
    }
    const label = (relation.label as string | undefined)
        ?? (relation.name as string | undefined)
        ?? relation['@id']
    return [{ value: relation['@id'], label }]
}

/** Code metier d'un referentiel embarque (PaymentType.code = 'LCR' / 'VIREMENT'), ou null. */
export function paymentTypeCodeOf(relation: Relation): string | null {
    if (!relation || typeof relation === 'string') {
        return null
    }
    return (relation.code as string | undefined) ?? null
}

/**
 * Bouton « Modifier » : visible si l'utilisateur peut editer au moins un onglet —
 * `manage` (onglets metier) OU `accounting.manage` (le role Compta doit pouvoir
 * ouvrir l'edition pour son onglet Comptabilite). Le readonly fin par onglet est
 * gere sur l'ecran d'edition.
 */
export function canEditProvider(canAny: (codes: string[]) => boolean): boolean {
    return canAny(['technique.providers.manage', 'technique.providers.accounting.manage'])
}

/** Bouton « Archiver » : permission archive ET prestataire encore actif (Admin seul). */
export function showArchiveAction(can: (code: string) => boolean, isArchived: boolean): boolean {
    return can('technique.providers.archive') && !isArchived
}

/** Bouton « Restaurer » : permission archive ET prestataire deja archive (Admin seul). */
export function showRestoreAction(can: (code: string) => boolean, isArchived: boolean): boolean {
    return can('technique.providers.archive') && isArchived
}
