import { describe, it, expect } from 'vitest'
import {
    addressFlagsFromType,
    addressTypeFromFlags,
    applyProspectExclusivity,
    buildClientFormTabKeys,
    canSelectDeliveryOrBilling,
    canSelectProspect,
    hasAllRequiredAccountingFields,
    hasAtLeastOneInformationField,
    hasAtLeastOneValidContact,
    isAddressValid,
    isBankRequiredForPaymentType,
    isBillingEmailRequired,
    isBlankRow,
    isContactBlank,
    isContactNamed,
    isRibBlank,
    isRibComplete,
    isRibRequiredForPaymentType,
    lastFillableTabKey,
    omitEmptyRequired,
    showsRelationAndTriageFields,
    type AddressFlagsDraft,
    type AddressValidityDraft,
    type ContactDraft,
    type ContactFillableDraft,
} from '../clientFormRules'

/** Bloc contact totalement vide (amorce par defaut). */
function blankContact(): ContactFillableDraft {
    return {
        firstName: null,
        lastName: null,
        jobTitle: null,
        phonePrimary: null,
        phoneSecondary: null,
        email: null,
    }
}

describe('buildClientFormTabKeys (gating onglet Comptabilite + onglets edit-only)', () => {
    it('inclut l onglet accounting si l utilisateur a accounting.view', () => {
        expect(buildClientFormTabKeys(true)).toContain('accounting')
    })

    it('exclut l onglet accounting sinon (Bureau / Commerciale)', () => {
        expect(buildClientFormTabKeys(false)).not.toContain('accounting')
    })

    it('a la creation, exclut Statistiques / Rapports / Echanges', () => {
        const keys = buildClientFormTabKeys(true)
        expect(keys).toEqual(['information', 'contact', 'address', 'transport', 'accounting'])
        expect(keys).not.toContain('statistics')
        expect(keys).not.toContain('reports')
        expect(keys).not.toContain('exchanges')
    })

    it('en modification (includeEditOnlyTabs), ajoute les onglets edit-only en fin', () => {
        const keys = buildClientFormTabKeys(true, { includeEditOnlyTabs: true })
        expect(keys).toEqual([
            'information',
            'contact',
            'address',
            'transport',
            'accounting',
            'statistics',
            'reports',
            'exchanges',
        ])
    })
})

describe('lastFillableTabKey (redirection fin d\'ajout, role-aware)', () => {
    it('Adresse pour un role sans Comptabilite (Bureau / Commerciale)', () => {
        expect(lastFillableTabKey(buildClientFormTabKeys(false))).toBe('address')
    })

    it('Comptabilite pour un role avec accounting.view (Admin)', () => {
        expect(lastFillableTabKey(buildClientFormTabKeys(true))).toBe('accounting')
    })

    it('ignore les onglets placeholder (Transport en dernier ne compte pas)', () => {
        expect(lastFillableTabKey(['information', 'contact', 'address', 'transport'])).toBe('address')
    })

    it('undefined si aucun onglet remplissable (que des placeholders)', () => {
        expect(lastFillableTabKey(['transport', 'statistics'])).toBeUndefined()
    })
})

describe('isContactNamed (RG-1.05)', () => {
    it('vrai si le prenom est renseigne', () => {
        expect(isContactNamed({ firstName: 'Alice', lastName: null })).toBe(true)
    })

    it('vrai si le nom est renseigne', () => {
        expect(isContactNamed({ firstName: null, lastName: 'Martin' })).toBe(true)
    })

    it('faux si les deux sont vides ou espaces uniquement', () => {
        expect(isContactNamed({ firstName: null, lastName: null })).toBe(false)
        expect(isContactNamed({ firstName: '  ', lastName: '' })).toBe(false)
    })
})

describe('isBlankRow (primitive : toutes les valeurs vides)', () => {
    it('vrai si toutes les valeurs sont nulles / vides / espaces', () => {
        expect(isBlankRow([null, undefined, '', '  '])).toBe(true)
        expect(isBlankRow([])).toBe(true)
    })

    it('faux des qu une valeur porte un caractere non-espace', () => {
        expect(isBlankRow([null, 'x', ''])).toBe(false)
    })
})

describe('isRibBlank (bloc RIB totalement vide vs partiellement rempli)', () => {
    it('vrai si label / bic / iban sont tous vides', () => {
        expect(isRibBlank({ label: null, bic: null, iban: null })).toBe(true)
        expect(isRibBlank({ label: '  ', bic: '', iban: null })).toBe(true)
    })

    it('faux si un IBAN seul est saisi (bloc a soumettre -> 422 NotBlank inline)', () => {
        expect(isRibBlank({ label: null, bic: null, iban: 'FR1420041010050500013M02606' })).toBe(false)
    })

    it('faux si seul le libelle est saisi', () => {
        expect(isRibBlank({ label: 'Compte courant', bic: null, iban: null })).toBe(false)
    })
})

describe('isContactBlank (bloc totalement vide vs partiellement rempli)', () => {
    it('vrai si aucun champ saisissable n est rempli', () => {
        expect(isContactBlank(blankContact())).toBe(true)
        expect(isContactBlank({ ...blankContact(), firstName: '  ', email: '' })).toBe(true)
    })

    it('faux si un email seul est saisi (bloc a soumettre -> 422 RG-1.05 inline)', () => {
        expect(isContactBlank({ ...blankContact(), email: 'jean@acme.fr' })).toBe(false)
    })

    it('faux si seul un telephone, une fonction ou un nom est saisi', () => {
        expect(isContactBlank({ ...blankContact(), phonePrimary: '0612345678' })).toBe(false)
        expect(isContactBlank({ ...blankContact(), jobTitle: 'Directeur' })).toBe(false)
        expect(isContactBlank({ ...blankContact(), firstName: 'Alice' })).toBe(false)
    })
})

describe('hasAtLeastOneValidContact (RG-1.14)', () => {
    it('faux sur une liste vide', () => {
        expect(hasAtLeastOneValidContact([])).toBe(false)
    })

    it('faux si aucun contact n a de nom ni prenom', () => {
        const contacts: ContactDraft[] = [
            { firstName: null, lastName: null },
            { firstName: '', lastName: '  ' },
        ]
        expect(hasAtLeastOneValidContact(contacts)).toBe(false)
    })

    it('vrai des qu un contact a un nom ou un prenom', () => {
        const contacts: ContactDraft[] = [
            { firstName: null, lastName: null },
            { firstName: 'Bob', lastName: null },
        ]
        expect(hasAtLeastOneValidContact(contacts)).toBe(true)
    })
})

/** Drapeaux d'adresse complets (5 types) avec surcharge partielle. */
function flags(overrides: Partial<AddressFlagsDraft> = {}): AddressFlagsDraft {
    return {
        isProspect: false, isDelivery: false, isBilling: false, isBroker: false, isDistributor: false,
        ...overrides,
    }
}

describe('exclusivite Prospect / Livraison / Facturation (RG-1.06/07/08)', () => {
    it('Prospect est selectionnable tant que ni Livraison ni Facturation', () => {
        expect(canSelectProspect(flags())).toBe(true)
        expect(canSelectProspect(flags({ isDelivery: true }))).toBe(false)
        expect(canSelectProspect(flags({ isBilling: true }))).toBe(false)
    })

    it('Livraison / Facturation selectionnables tant que pas Prospect', () => {
        expect(canSelectDeliveryOrBilling(flags())).toBe(true)
        expect(canSelectDeliveryOrBilling(flags({ isProspect: true }))).toBe(false)
    })

    it('cocher Prospect efface Livraison et Facturation', () => {
        const next = applyProspectExclusivity(flags({ isDelivery: true, isBilling: true }), 'isProspect', true)
        expect(next).toEqual(flags({ isProspect: true }))
    })

    it('cocher Livraison efface Prospect', () => {
        const next = applyProspectExclusivity(flags({ isProspect: true }), 'isDelivery', true)
        expect(next).toEqual(flags({ isDelivery: true }))
    })

    it('cocher Facturation efface Prospect mais conserve Livraison', () => {
        const next = applyProspectExclusivity(flags({ isProspect: true, isDelivery: true }), 'isBilling', true)
        expect(next).toEqual(flags({ isDelivery: true, isBilling: true }))
    })

    it('decocher un drapeau ne reactive rien d autre', () => {
        const next = applyProspectExclusivity(flags({ isDelivery: true, isBilling: true }), 'isBilling', false)
        expect(next).toEqual(flags({ isDelivery: true }))
    })
})

describe('isBillingEmailRequired (RG-1.11)', () => {
    it('obligatoire uniquement si Facturation est coche', () => {
        expect(isBillingEmailRequired(flags({ isBilling: true }))).toBe(true)
        expect(isBillingEmailRequired(flags({ isDelivery: true }))).toBe(false)
    })
})

describe('type d\'adresse (Select front) <-> drapeaux back', () => {
    it('addressFlagsFromType mappe chaque type vers les bons drapeaux', () => {
        expect(addressFlagsFromType('prospect')).toEqual(flags({ isProspect: true }))
        expect(addressFlagsFromType('delivery')).toEqual(flags({ isDelivery: true }))
        expect(addressFlagsFromType('billing')).toEqual(flags({ isBilling: true }))
        expect(addressFlagsFromType('delivery_billing')).toEqual(flags({ isDelivery: true, isBilling: true }))
        expect(addressFlagsFromType('broker')).toEqual(flags({ isBroker: true }))
        expect(addressFlagsFromType('distributor')).toEqual(flags({ isDistributor: true }))
    })

    it('addressTypeFromFlags reconstruit le type (Prospect/Courtier/Distributeur autonomes, livraison+facturation groupes)', () => {
        expect(addressTypeFromFlags(flags({ isProspect: true }))).toBe('prospect')
        expect(addressTypeFromFlags(flags({ isDelivery: true }))).toBe('delivery')
        expect(addressTypeFromFlags(flags({ isBilling: true }))).toBe('billing')
        expect(addressTypeFromFlags(flags({ isDelivery: true, isBilling: true }))).toBe('delivery_billing')
        expect(addressTypeFromFlags(flags({ isBroker: true }))).toBe('broker')
        expect(addressTypeFromFlags(flags({ isDistributor: true }))).toBe('distributor')
    })

    it('addressTypeFromFlags retourne null quand aucun drapeau (amorce vierge -> bouton bloque)', () => {
        expect(addressTypeFromFlags(flags())).toBeNull()
    })

    it('aller-retour type -> drapeaux -> type stable pour les 6 types', () => {
        for (const type of ['prospect', 'delivery', 'billing', 'delivery_billing', 'broker', 'distributor'] as const) {
            expect(addressTypeFromFlags(addressFlagsFromType(type))).toBe(type)
        }
    })
})

describe('regles type de reglement (RG-1.12 / RG-1.13)', () => {
    it('banque obligatoire si VIREMENT', () => {
        expect(isBankRequiredForPaymentType('VIREMENT')).toBe(true)
        expect(isBankRequiredForPaymentType('LCR')).toBe(false)
        expect(isBankRequiredForPaymentType(null)).toBe(false)
    })

    it('RIB obligatoire si LCR', () => {
        expect(isRibRequiredForPaymentType('LCR')).toBe(true)
        expect(isRibRequiredForPaymentType('VIREMENT')).toBe(false)
        expect(isRibRequiredForPaymentType(null)).toBe(false)
    })
})

describe('hasAllRequiredAccountingFields (RG-1.30)', () => {
    const complete = {
        siren: '123456789',
        accountNumber: '00012345678',
        nTva: 'FR12345678901',
        tvaModeIri: '/api/tva_modes/1',
        paymentDelayIri: '/api/payment_delays/1',
        paymentTypeIri: '/api/payment_types/1',
    }

    it('vrai quand les six champs obligatoires sont remplis', () => {
        expect(hasAllRequiredAccountingFields(complete)).toBe(true)
    })

    it('faux si un champ est manquant (null ou vide apres trim)', () => {
        expect(hasAllRequiredAccountingFields({ ...complete, siren: null })).toBe(false)
        expect(hasAllRequiredAccountingFields({ ...complete, accountNumber: '   ' })).toBe(false)
        expect(hasAllRequiredAccountingFields({ ...complete, tvaModeIri: null })).toBe(false)
        expect(hasAllRequiredAccountingFields({ ...complete, paymentTypeIri: null })).toBe(false)
    })

    it('faux quand tout est vide (onglet non rempli)', () => {
        expect(hasAllRequiredAccountingFields({
            siren: null,
            accountNumber: null,
            nTva: null,
            tvaModeIri: null,
            paymentDelayIri: null,
            paymentTypeIri: null,
        })).toBe(false)
    })
})

describe('showsRelationAndTriageFields (affichage Relation + Triage selon categorie)', () => {
    it('faux par defaut (aucune categorie selectionnee)', () => {
        expect(showsRelationAndTriageFields([])).toBe(false)
    })

    it('faux si seules des categories Distributeur / Courtier sont selectionnees', () => {
        expect(showsRelationAndTriageFields(['DISTRIBUTEUR'])).toBe(false)
        expect(showsRelationAndTriageFields(['COURTIER'])).toBe(false)
        expect(showsRelationAndTriageFields(['DISTRIBUTEUR', 'COURTIER'])).toBe(false)
    })

    it('vrai des qu\'une categorie ordinaire est selectionnee', () => {
        expect(showsRelationAndTriageFields(['CLIENT'])).toBe(true)
        expect(showsRelationAndTriageFields(['DISTRIBUTEUR', 'CLIENT'])).toBe(true)
    })
})

describe('hasAtLeastOneInformationField (pas de validation a vide a la creation)', () => {
    const blank = {
        description: null,
        competitors: null,
        foundedAt: null,
        employeesCount: null,
        revenueAmount: null,
        profitAmount: null,
        directorName: null,
    }

    it('faux quand aucun champ n\'est rempli (onglet vierge)', () => {
        expect(hasAtLeastOneInformationField(blank)).toBe(false)
        expect(hasAtLeastOneInformationField({ ...blank, description: '   ' })).toBe(false)
    })

    it('vrai des qu\'un champ porte une valeur', () => {
        expect(hasAtLeastOneInformationField({ ...blank, description: 'Acteur majeur' })).toBe(true)
        expect(hasAtLeastOneInformationField({ ...blank, employeesCount: '42' })).toBe(true)
        expect(hasAtLeastOneInformationField({ ...blank, foundedAt: '2020-01-01' })).toBe(true)
    })
})

describe('isAddressValid (gating « + Adresse » + validation onglet)', () => {
    /** Adresse de livraison valide (type + site + categorie ; pas de facturation). */
    function validDelivery(): AddressValidityDraft {
        return {
            isProspect: false,
            isDelivery: true,
            isBilling: false,
            isBroker: false,
            isDistributor: false,
            categoryIris: ['/api/client_categories/1'],
            siteIris: ['/api/sites/1'],
            billingEmail: null,
        }
    }

    it('vrai quand type + >= 1 site + >= 1 categorie (sans facturation)', () => {
        expect(isAddressValid(validDelivery())).toBe(true)
    })

    it('faux si aucun drapeau (type d\'adresse non renseigne, amorce vierge)', () => {
        expect(isAddressValid({ ...validDelivery(), isDelivery: false })).toBe(false)
    })

    it('faux si aucun site (RG-1.10)', () => {
        expect(isAddressValid({ ...validDelivery(), siteIris: [] })).toBe(false)
    })

    it('faux si aucune categorie', () => {
        expect(isAddressValid({ ...validDelivery(), categoryIris: [] })).toBe(false)
    })

    it('RG-1.11 : email de facturation obligatoire quand l\'adresse est de facturation', () => {
        const billing: AddressValidityDraft = { ...validDelivery(), isBilling: true }
        expect(isAddressValid({ ...billing, billingEmail: null })).toBe(false)
        expect(isAddressValid({ ...billing, billingEmail: '   ' })).toBe(false)
        expect(isAddressValid({ ...billing, billingEmail: 'facture@acme.fr' })).toBe(true)
    })
})

describe('isRibComplete (gating « + RIB » + RG-1.13)', () => {
    it('vrai quand label + BIC + IBAN sont remplis', () => {
        expect(isRibComplete({ label: 'Compte courant', bic: 'BNPAFRPP', iban: 'FR1420041010050500013M02606' })).toBe(true)
    })

    it('faux si un champ manque (null ou vide apres trim)', () => {
        expect(isRibComplete({ label: null, bic: 'BNPAFRPP', iban: 'FR14...' })).toBe(false)
        expect(isRibComplete({ label: 'Compte', bic: '  ', iban: 'FR14...' })).toBe(false)
        expect(isRibComplete({ label: 'Compte', bic: 'BNPAFRPP', iban: null })).toBe(false)
    })

    it('faux pour un bloc totalement vide (amorce)', () => {
        expect(isRibComplete({ label: null, bic: null, iban: null })).toBe(false)
    })
})

describe('omitEmptyRequired (ERP-119 : 422 NotBlank au lieu de 400 de type)', () => {
    it('retire les cles requises vides (null / vide / undefined)', () => {
        const payload = omitEmptyRequired(
            { companyName: null, label: '', iban: undefined, categories: ['/api/categories/1'] },
            ['companyName', 'label', 'iban'],
        )
        expect('companyName' in payload).toBe(false)
        expect('label' in payload).toBe(false)
        expect('iban' in payload).toBe(false)
        // Les cles hors liste ne sont jamais touchees.
        expect(payload.categories).toEqual(['/api/categories/1'])
    })

    it('conserve les cles requises renseignees', () => {
        const payload = omitEmptyRequired({ companyName: 'ACME', bic: 'BNPAFRPP' }, ['companyName', 'bic'])
        expect(payload).toEqual({ companyName: 'ACME', bic: 'BNPAFRPP' })
    })

    it('ne retire jamais une cle hors de la liste requise, meme vide', () => {
        const payload = omitEmptyRequired({ streetComplement: null }, ['street'])
        expect('streetComplement' in payload).toBe(true)
        expect(payload.streetComplement).toBeNull()
    })

    it('false / 0 ne sont pas consideres vides (booleens / nombres preserves)', () => {
        const payload = omitEmptyRequired({ isDelivery: false, position: 0 }, ['isDelivery', 'position'])
        expect(payload).toEqual({ isDelivery: false, position: 0 })
    })
})
