/**
 * Regles metier pures de l'ecran « Ajouter un client » (M1 Commercial).
 *
 * Centralisees ici (hors composant) pour rester testables unitairement et
 * partagees entre la page de creation et les futurs ecrans d'edition (1.11/1.12).
 * Ces helpers ne touchent ni a l'API ni a l'etat reactif : ils prennent des
 * brouillons « plats » et retournent des booleens / nouveaux objets.
 *
 * Le back reste la source de verite (les RG sont re-validees serveur) ; ces
 * regles ne servent qu'au feedback UI immediat (gating de boutons, visibilite).
 *
 * NOTE RG-1.04 (Information obligatoire pour la Commerciale) : volontairement
 * NON miroite cote front pour l'instant. Le payload /api/me ne porte pas le code
 * de role (roles = IRIs opaques) et Bureau partage les memes permissions que
 * Commerciale : aucun signal fiable pour distinguer le role cote front. Le back
 * (ClientProcessor, via BusinessRoleAware) applique la regle de maniere fiable ;
 * a rebrancher ici des qu'un code de role sera expose dans /api/me.
 */

/**
 * Onglets « coquille » (non encore implementes) : frame vide, passage
 * automatique a l'onglet suivant (decision Tristan 28/05).
 */
export const CLIENT_FORM_PLACEHOLDER_TABS = ['transport', 'statistics', 'reports', 'exchanges'] as const

/**
 * Onglets affiches uniquement en MODIFICATION (selon le role), jamais a la
 * creation : Statistiques / Rapports / Echanges. A rebrancher dans les ecrans
 * d'edition (1.11/1.12) via l'option `includeEditOnlyTabs`.
 */
export const CLIENT_FORM_EDIT_ONLY_TABS = ['statistics', 'reports', 'exchanges'] as const

/**
 * Construit l'ordre des onglets du formulaire client.
 *  - L'onglet Comptabilite n'est present que si l'utilisateur a `accounting.view`
 *    (Bureau / Commerciale ne le voient pas).
 *  - Les onglets edit-only (Statistiques / Rapports / Echanges) sont exclus par
 *    defaut (creation) ; passer `includeEditOnlyTabs: true` pour les afficher en
 *    modification.
 * Ordre aligne sur la spec M1 § Ecran « Ajouter un client ».
 */
export function buildClientFormTabKeys(
    canAccountingView: boolean,
    options: { includeEditOnlyTabs?: boolean } = {},
): string[] {
    const keys = ['information', 'contact', 'address', 'transport']
    if (canAccountingView) {
        keys.push('accounting')
    }
    if (options.includeEditOnlyTabs) {
        keys.push(...CLIENT_FORM_EDIT_ONLY_TABS)
    }
    return keys
}

/** Sous-ensemble d'un contact necessaire aux regles de nommage (RG-1.05/1.14). */
export interface ContactDraft {
    firstName: string | null
    lastName: string | null
}

/** Drapeaux d'usage d'une adresse (RG-1.06/07/08/11). */
export interface AddressFlagsDraft {
    isProspect: boolean
    isDelivery: boolean
    isBilling: boolean
}

/** Vrai si une chaine porte au moins un caractere non-espace. */
function isFilled(value: string | null | undefined): boolean {
    return value !== null && value !== undefined && value.trim() !== ''
}

/**
 * RG-1.05 : un contact est valide des qu'il porte un nom OU un prenom.
 */
export function isContactNamed(contact: ContactDraft): boolean {
    return isFilled(contact.firstName) || isFilled(contact.lastName)
}

/**
 * RG-1.14 : l'onglet Contact ne peut etre finalise que s'il reste au moins un
 * contact nomme (nom ou prenom).
 */
export function hasAtLeastOneValidContact(contacts: ContactDraft[]): boolean {
    return contacts.some(isContactNamed)
}

/**
 * Primitive reutilisable : vrai si TOUTES les valeurs fournies sont vides (null /
 * undefined / espaces uniquement). Sert a detecter un bloc de collection
 * totalement vide (amorce non remplie). Un bloc qui porte la moindre donnee
 * n'est PAS « blank » : il doit etre soumis pour declencher sa 422 inline plutot
 * que d'etre saute silencieusement.
 */
export function isBlankRow(values: (string | null | undefined)[]): boolean {
    return values.every(value => !isFilled(value))
}

/** Champs saisissables d'un bloc contact (pour detecter un bloc totalement vide). */
export interface ContactFillableDraft extends ContactDraft {
    jobTitle: string | null
    phonePrimary: string | null
    phoneSecondary: string | null
    email: string | null
}

/**
 * Vrai si AUCUN champ saisissable du bloc contact n'est rempli. Distingue un bloc
 * d'amorce vide (a ignorer au submit) d'un bloc partiellement rempli sans nom
 * (email / telephone / fonction seul) : ce dernier doit etre soumis pour
 * declencher la 422 RG-1.05 (« prenom ou nom obligatoire ») affichee inline.
 */
export function isContactBlank(contact: ContactFillableDraft): boolean {
    return isBlankRow([
        contact.firstName,
        contact.lastName,
        contact.jobTitle,
        contact.phonePrimary,
        contact.phoneSecondary,
        contact.email,
    ])
}

/** Champs saisissables d'un bloc RIB (pour detecter un bloc totalement vide). */
export interface RibFillableDraft {
    label: string | null
    bic: string | null
    iban: string | null
}

/**
 * Vrai si AUCUN champ du bloc RIB n'est rempli. Un RIB partiellement rempli (ex.
 * IBAN seul) n'est PAS « blank » : il doit etre soumis pour declencher les 422
 * NotBlank (label / bic / iban) inline plutot que d'etre saute silencieusement.
 */
export function isRibBlank(rib: RibFillableDraft): boolean {
    return isBlankRow([rib.label, rib.bic, rib.iban])
}

/**
 * RG-1.06/07/08 : une adresse de prospection est exclusive d'une adresse de
 * livraison/facturation. Prospect n'est selectionnable que si ni Livraison ni
 * Facturation ne sont coches.
 */
export function canSelectProspect(flags: AddressFlagsDraft): boolean {
    return !flags.isDelivery && !flags.isBilling
}

/**
 * RG-1.06/07/08 : Livraison et Facturation ne sont selectionnables que si
 * Prospect n'est pas coche.
 */
export function canSelectDeliveryOrBilling(flags: AddressFlagsDraft): boolean {
    return !flags.isProspect
}

/**
 * Applique l'exclusivite Prospect / (Livraison|Facturation) au changement d'un
 * drapeau. Cocher Prospect efface Livraison + Facturation ; cocher Livraison ou
 * Facturation efface Prospect. Decocher n'a aucun effet de bord. Retourne un
 * nouvel objet (pas de mutation de l'entree).
 */
export function applyProspectExclusivity(
    flags: AddressFlagsDraft,
    field: keyof AddressFlagsDraft,
    value: boolean,
): AddressFlagsDraft {
    const next: AddressFlagsDraft = { ...flags, [field]: value }

    if (value && field === 'isProspect') {
        next.isDelivery = false
        next.isBilling = false
    }
    else if (value && (field === 'isDelivery' || field === 'isBilling')) {
        next.isProspect = false
    }

    return next
}

/**
 * RG-1.11 : l'email de facturation n'est visible/obligatoire que si l'adresse
 * est une adresse de facturation.
 */
export function isBillingEmailRequired(flags: AddressFlagsDraft): boolean {
    return flags.isBilling
}

/**
 * Type d'adresse expose a l'utilisateur (Select unique remplacant les trois
 * cases a cocher). Sucre purement front : le back continue de recevoir les
 * drapeaux isProspect / isDelivery / isBilling (aucune RG modifiee). Les seules
 * combinaisons proposees respectent l'exclusivite Prospect (RG-1.06/07/08).
 */
export type AddressType = 'prospect' | 'delivery' | 'billing' | 'delivery_billing'

/**
 * Mappe le type d'adresse choisi vers les trois drapeaux back.
 * « Adresse + Facturation » = livraison ET facturation sur la meme adresse.
 */
export function addressFlagsFromType(type: AddressType): AddressFlagsDraft {
    switch (type) {
        case 'prospect':
            return { isProspect: true, isDelivery: false, isBilling: false }
        case 'delivery':
            return { isProspect: false, isDelivery: true, isBilling: false }
        case 'billing':
            return { isProspect: false, isDelivery: false, isBilling: true }
        case 'delivery_billing':
            return { isProspect: false, isDelivery: true, isBilling: true }
    }
}

/**
 * Reconstruit le type d'adresse a partir des drapeaux (consultation / edition
 * d'une adresse persistee, ou amorce vierge). Retourne null si aucun drapeau
 * n'est positionne — le Select reste alors a saisir (et bloque la validation).
 */
export function addressTypeFromFlags(flags: AddressFlagsDraft): AddressType | null {
    if (flags.isProspect) return 'prospect'
    if (flags.isDelivery && flags.isBilling) return 'delivery_billing'
    if (flags.isDelivery) return 'delivery'
    if (flags.isBilling) return 'billing'

    return null
}

/** Code stable du type de reglement « virement » (cf. PaymentType.code, RG-1.12). */
const PAYMENT_TYPE_TRANSFER = 'VIREMENT'

/** Code stable du type de reglement « lettre de change » (RG-1.13). */
const PAYMENT_TYPE_LCR = 'LCR'

/**
 * RG-1.12 : la banque est obligatoire lorsque le type de reglement est un
 * virement.
 */
export function isBankRequiredForPaymentType(code: string | null | undefined): boolean {
    return code === PAYMENT_TYPE_TRANSFER
}

/**
 * RG-1.13 : au moins un RIB complet est obligatoire lorsque le type de reglement
 * est une LCR.
 */
export function isRibRequiredForPaymentType(code: string | null | undefined): boolean {
    return code === PAYMENT_TYPE_LCR
}

/** Sous-ensemble du brouillon comptable portant les six champs obligatoires. */
export interface AccountingRequiredDraft {
    siren: string | null
    accountNumber: string | null
    nTva: string | null
    tvaModeIri: string | null
    paymentDelayIri: string | null
    paymentTypeIri: string | null
}

/**
 * RG-1.30 : les six champs scalaires de l'onglet Comptabilite sont obligatoires
 * pour valider l'onglet (SIREN, N de compte, Mode de TVA, N de TVA, Delai de
 * reglement, Type de reglement). bank / RIB restent conditionnels (RG-1.12 /
 * RG-1.13) et sont evalues a part. Miroir front du
 * ClientAccountingCompletenessValidator : meme gate que les onglets Contact /
 * Adresse (bouton « Valider » desactive tant que l'onglet n'est pas complet).
 */
export function hasAllRequiredAccountingFields(accounting: AccountingRequiredDraft): boolean {
    const filled = (v: string | null): boolean => v !== null && v.trim() !== ''

    return filled(accounting.siren)
        && filled(accounting.accountNumber)
        && filled(accounting.nTva)
        && filled(accounting.tvaModeIri)
        && filled(accounting.paymentDelayIri)
        && filled(accounting.paymentTypeIri)
}
