/**
 * Helpers purs des ecrans « Ajouter » / « Modifier » un fournisseur (M2
 * Commercial) — miroir de `clientEdit.ts` (M1). Deux responsabilites, toutes deux
 * testables unitairement (cf. supplierEdit.spec.ts) :
 *  1. Pre-remplissage : mapper le payload `GET /api/suppliers/{id}` (embed +
 *     scalaires) vers les brouillons « plats » edites par la page de modification.
 *  2. Scoping STRICT des payloads PATCH (mode strict RG-2.16 / ERP-74) : chaque
 *     onglet n'envoie QUE les champs de SON groupe de serialisation, jamais un
 *     payload mixte (un champ hors-permission = 403 sur l'integralite cote back).
 *
 * Ces helpers ne touchent ni a l'API ni a l'etat reactif.
 */

import {
    ADDRESS_REQUIRED_NON_NULLABLE_KEYS,
    blankEmptyRequired,
    MAIN_REQUIRED_NON_NULLABLE_KEYS,
    omitEmptyRequired,
    RIB_REQUIRED_NON_NULLABLE_KEYS,
} from '~/modules/commercial/utils/forms/supplierFormRules'
import { iriOf, type SupplierDetail } from '~/modules/commercial/utils/forms/supplierConsultation'
import type {
    SupplierAddressFormDraft,
    SupplierContactFormDraft,
    SupplierRibFormDraft,
} from '~/modules/commercial/types/supplierForm'

/** Etat « plat » du bloc principal (groupe supplier:write:main). */
export interface MainFormDraft {
    companyName: string | null
    /** IRI des categories rattachees (M2M, type FOURNISSEUR). */
    categoryIris: string[]
}

/** Etat « plat » de l'onglet Information (groupe supplier:write:information). */
export interface InformationFormDraft {
    description: string | null
    competitors: string | null
    /** Date de creation de l'entreprise au format YYYY-MM-DD (MalioDate). */
    foundedAt: string | null
    /**
     * Saisie brute invalide remontee par MalioDate (`@update:rawValue`) : '' tant
     * que la saisie est valide/vide, sinon le texte tel que tape. On l'envoie au
     * back en priorite sur `foundedAt` pour que la 422 (validation autoritaire du
     * format, ERP-101) porte sur le champ et s'affiche inline. Cf. MUI-44.
     */
    foundedAtRaw: string
    /** Nombre de salaries en chaine (saisie masquee), converti en number au PATCH. */
    employeesCount: string | null
    revenueAmount: string | null
    profitAmount: string | null
    directorName: string | null
    /** Volume previsionnel (entier >= 0, specifique fournisseur) en chaine. */
    volumeForecast: string | null
}

/** Etat « plat » de l'onglet Comptabilite (groupe supplier:write:accounting). */
export interface AccountingFormDraft {
    siren: string | null
    accountNumber: string | null
    nTva: string | null
    tvaModeIri: string | null
    paymentDelayIri: string | null
    paymentTypeIri: string | null
    bankIri: string | null
}

/** Permissions de l'utilisateur courant pertinentes pour l'edition d'un fournisseur. */
export interface SupplierEditAbilities {
    /** `commercial.suppliers.manage` : bloc principal + onglets metier. */
    canManage: boolean
    /** `commercial.suppliers.accounting.view` : visibilite de l'onglet Comptabilite. */
    canAccountingView: boolean
    /** `commercial.suppliers.accounting.manage` : edition de l'onglet Comptabilite. */
    canAccountingManage: boolean
}

/** Editabilite resolue par zone d'onglet (deduite des permissions). */
export interface TabEditability {
    /** Bloc principal + onglets Information / Contacts / Adresses editables. */
    businessEditable: boolean
    /** Onglet Comptabilite present (affiche). */
    accountingVisible: boolean
    /** Onglet Comptabilite editable. */
    accountingEditable: boolean
}

// ── Pre-remplissage (GET detail -> brouillons) ──────────────────────────────

/** Mappe le detail fournisseur vers le brouillon du bloc principal. */
export function mapMainDraft(supplier: SupplierDetail): MainFormDraft {
    return {
        companyName: supplier.companyName ?? null,
        categoryIris: (supplier.categories ?? []).map(c => c['@id']),
    }
}

/** Mappe le detail fournisseur vers le brouillon de l'onglet Information. */
export function mapInformationDraft(supplier: SupplierDetail): InformationFormDraft {
    return {
        description: supplier.description ?? null,
        competitors: supplier.competitors ?? null,
        // MalioDate attend strictement YYYY-MM-DD : on tronque l'ISO datetime.
        foundedAt: supplier.foundedAt ? supplier.foundedAt.slice(0, 10) : null,
        // Aucune saisie brute invalide au chargement (la valeur stockee est valide).
        foundedAtRaw: '',
        employeesCount: supplier.employeesCount != null ? String(supplier.employeesCount) : null,
        revenueAmount: supplier.revenueAmount ?? null,
        profitAmount: supplier.profitAmount ?? null,
        directorName: supplier.directorName ?? null,
        // Volume previsionnel (entier, specifique fournisseur) en chaine pour la saisie.
        volumeForecast: supplier.volumeForecast != null ? String(supplier.volumeForecast) : null,
    }
}

/** Mappe les champs comptables du detail vers le brouillon de l'onglet (scalaires + IRI). */
export function mapAccountingFormDraft(supplier: SupplierDetail): AccountingFormDraft {
    return {
        siren: supplier.siren ?? null,
        accountNumber: supplier.accountNumber ?? null,
        nTva: supplier.nTva ?? null,
        tvaModeIri: iriOf(supplier.tvaMode),
        paymentDelayIri: iriOf(supplier.paymentDelay),
        paymentTypeIri: iriOf(supplier.paymentType),
        bankIri: iriOf(supplier.bank),
    }
}

/**
 * Resout l'editabilite par zone a partir des permissions (option 1 ERP-74,
 * miroir UI du re-gating champ-par-champ du SupplierProcessor) :
 *  - bloc principal + Information/Contacts/Adresses : editables ssi `manage` ;
 *  - Comptabilite : visible ssi `accounting.view`, editable ssi `accounting.manage`.
 *
 * Produit le comportement attendu :
 *  - Admin : tout editable.
 *  - Bureau / Commerciale (manage, sans accounting) : metier editable, Compta masquee.
 *  - Compta (accounting seul, sans manage) : metier readonly, Compta editable.
 */
export function resolveTabEditability(abilities: SupplierEditAbilities): TabEditability {
    return {
        businessEditable: abilities.canManage,
        accountingVisible: abilities.canAccountingView,
        accountingEditable: abilities.canAccountingManage,
    }
}

// ── Scoping strict des payloads PATCH/POST ──────────────────────────────────

/**
 * Options de construction d'un payload d'ecriture.
 *  - `forUpdate: false` (defaut, CREATION/POST) : les champs requis vides sont OMIS
 *    -> 422 NotBlank a l'insert (le back ne reçoit pas la cle).
 *  - `forUpdate: true` (EDITION/PATCH d'une ligne existante) : les champs requis
 *    vides sont envoyes en `''` -> 422 NotBlank (sinon une cle omise laisse la valeur
 *    serveur inchangee, faux 200 — cf. blankEmptyRequired).
 */
export interface BuildPayloadOptions {
    forUpdate?: boolean
}

/** Selectionne le finaliseur des champs requis selon création (omit) vs édition (blank). */
function finalizeRequired<T extends Record<string, unknown>>(
    payload: T,
    requiredKeys: readonly string[],
    options: BuildPayloadOptions,
): T {
    return options.forUpdate
        ? blankEmptyRequired(payload, requiredKeys)
        : omitEmptyRequired(payload, requiredKeys)
}

/**
 * Payload du bloc principal — groupe supplier:write:main UNIQUEMENT.
 * companyName vide -> 422 NotBlank (omis a la creation, `''` en edition — ERP-119).
 */
export function buildMainPayload(main: MainFormDraft, options: BuildPayloadOptions = {}): Record<string, unknown> {
    return finalizeRequired({
        companyName: main.companyName,
        categories: main.categoryIris,
    }, MAIN_REQUIRED_NON_NULLABLE_KEYS, options)
}

/** Payload de l'onglet Information — groupe supplier:write:information UNIQUEMENT. */
export function buildInformationPayload(information: InformationFormDraft): Record<string, unknown> {
    return {
        description: information.description || null,
        competitors: information.competitors || null,
        // Saisie invalide (foundedAtRaw) prioritaire : on l'envoie telle quelle
        // pour que le back renvoie une 422 sur foundedAt (cf. foundedAtRaw).
        foundedAt: information.foundedAtRaw || information.foundedAt || null,
        employeesCount: information.employeesCount ? Number(information.employeesCount) : null,
        revenueAmount: information.revenueAmount || null,
        profitAmount: information.profitAmount || null,
        directorName: information.directorName || null,
        volumeForecast: information.volumeForecast ? Number(information.volumeForecast) : null,
    }
}

/**
 * Payload des scalaires de l'onglet Comptabilite — groupe supplier:write:accounting
 * UNIQUEMENT (les RIB passent par la sous-ressource /suppliers/{id}/ribs). La
 * banque n'a de sens que pour un Virement (RG-2.07) : forcee a null sinon.
 */
export function buildAccountingPayload(
    accounting: AccountingFormDraft,
    isBankRequired: boolean,
): Record<string, unknown> {
    return {
        siren: accounting.siren || null,
        accountNumber: accounting.accountNumber || null,
        tvaMode: accounting.tvaModeIri,
        nTva: accounting.nTva || null,
        paymentDelay: accounting.paymentDelayIri,
        paymentType: accounting.paymentTypeIri,
        bank: isBankRequired ? accounting.bankIri : null,
    }
}

/** Payload d'un contact (sous-ressource supplier_contact). */
export function buildContactPayload(contact: SupplierContactFormDraft): Record<string, unknown> {
    return {
        firstName: contact.firstName || null,
        lastName: contact.lastName || null,
        jobTitle: contact.jobTitle || null,
        phonePrimary: contact.phonePrimary || null,
        phoneSecondary: contact.hasSecondaryPhone ? (contact.phoneSecondary || null) : null,
        email: contact.email || null,
    }
}

/**
 * Payload d'une adresse (sous-ressource supplier_address). postalCode / city /
 * street omis si vides -> 422 NotBlank (ERP-119). Specifique fournisseur :
 * `bennes` (entier, 0 par defaut) + `triageProvider` (booleen). Pas d'email de
 * facturation (difference M1).
 */
export function buildAddressPayload(address: SupplierAddressFormDraft, options: BuildPayloadOptions = {}): Record<string, unknown> {
    return finalizeRequired({
        addressType: address.addressType,
        country: address.country,
        postalCode: address.postalCode || null,
        city: address.city || null,
        street: address.street || null,
        streetComplement: address.streetComplement || null,
        categories: address.categoryIris,
        sites: address.siteIris,
        contacts: address.contactIris,
        bennes: address.bennes !== null && address.bennes !== '' ? Number(address.bennes) : null,
        triageProvider: address.triageProvider,
    }, ADDRESS_REQUIRED_NON_NULLABLE_KEYS, options)
}

/** Payload d'un RIB (sous-ressource supplier_rib). */
export function buildRibPayload(rib: SupplierRibFormDraft, options: BuildPayloadOptions = {}): Record<string, unknown> {
    return finalizeRequired({
        label: rib.label,
        bic: rib.bic,
        iban: rib.iban,
    }, RIB_REQUIRED_NON_NULLABLE_KEYS, options)
}
