import { describe, expect, it } from 'vitest'
import { identifierToUserId } from '@/lib/auth/env-users'
// The import library is plain ESM so the scripts and the tests share one implementation.
import {
  isGenericMailbox, normEmail, parseCsv, phone10, profileIdForEmail, proposedRoles, resolvePeople, validEmail, matchCarrier,
} from '../scripts/synchron-people/lib.mjs'

/**
 * Synchron Carriers / Clients / Drivers import (Nash, 2026-09-25). The rules
 * that decide who becomes an unclaimed profile, with which roles, are pinned
 * here — §9, §14–§17, §32–§34, §50–§54.
 */
const person = (over: Record<string, unknown>) => ({
  source_file: 'clients', source_id: '1', first_name: 'John', last_name: 'Smith', full_name: 'John Smith', name_key: 'johnsmith',
  email_raw: 'john@gmail.com', email: 'john@gmail.com', email_invalid: false, phone: '5551234567', status_active: true, roles: ['OD CLIENT'],
  carrier_ids: ['10'], carrier_names: { '10': 'ABC Transport' }, carrier_link_types: { '10': ['user_id'] }, default_carrier_id: '10', cdl: '',
  created_at: '2024-01-01T00:00:00.000Z', ...over,
})
const carriersById = new Map([['10', { source_id: '10', name: 'ABC Transport' }], ['11', { source_id: '11', name: 'XYZ Transport' }]])
const hha = (over: Partial<{ profilesByEmail: Map<string, { id: string }>; leadEmails: Set<string> }> = {}) => ({ profilesByEmail: new Map(), leadEmails: new Set<string>(), ...over })

describe('the export parses despite its quoting quirk', () => {
  it('a backslash-escaped quote inside a quoted JSON field does not end the field', () => {
    const text = 'id,attrs,next\n1,"{""language"":""[\\"English\\"]"",""tz"":""UTC""}",after\n'
    const { rows, bad } = parseCsv(text)
    expect(bad).toEqual([])
    expect(rows[0].next).toBe('after')
    expect(JSON.parse(rows[0].attrs)).toEqual({ language: '["English"]', tz: 'UTC' })
  })
  it('reports rows whose field count still disagrees with the header instead of shifting columns', () => {
    const { rows, bad } = parseCsv('a,b\n1,2\n3,4,5\n')
    expect(rows).toHaveLength(1)
    expect(bad).toEqual([{ line: 3, fields: 3, id: '3' }])
  })
})

describe('normalisation (§15)', () => {
  it('lowercases and trims, nothing provider-specific', () => {
    expect(normEmail('  JOHN.Smith@Gmail.COM ')).toBe('john.smith@gmail.com')
    expect(validEmail('deezion83@gmail.com1')).toBe(false)
    expect(validEmail('CJACKAJSADKJ@GMAILCOM')).toBe(false)
    expect(validEmail('a@b.co')).toBe(true)
  })
  it('keeps only a full US number', () => {
    expect(phone10("'+1 702-635-6357")).toBe('7026356357')
    expect(phone10('555-1234')).toBe('')
  })
  it('recognises role mailboxes', () => {
    expect(isGenericMailbox('dispatch@abc.com')).toBe(true)
    expect(isGenericMailbox('info@allfreight.services')).toBe(true)
    expect(isGenericMailbox('john.smith@abc.com')).toBe(false)
  })
  it('derives the same id a self-signup with that email would get', () => {
    expect(profileIdForEmail('Mike@Gmail.com')).toBe(identifierToUserId('email:mike@gmail.com'))
  })
})

describe('roles come from the file the person is in (§7, §8)', () => {
  it('client → dispatcher, driver → driver, both → both', () => {
    expect(proposedRoles([person({ source_file: 'clients' })])).toEqual(['carrier_dispatcher'])
    expect(proposedRoles([person({ source_file: 'drivers' })])).toEqual(['carrier_driver'])
    expect(proposedRoles([person({ source_file: 'clients' }), person({ source_file: 'drivers' })])).toEqual(['carrier_dispatcher', 'carrier_driver'])
  })
})

describe('identity resolution', () => {
  it('§50 owner-operator: one person, one company, two roles', () => {
    const out = resolvePeople({ clients: [person({})], drivers: [person({ source_file: 'drivers' })], carriersById, hha: hha() })
    expect(out).toHaveLength(1)
    expect(out[0].decision).toBe('unclaimed')
    expect(out[0].proposed_roles).toEqual(['carrier_dispatcher', 'carrier_driver'])
    expect(out[0].carrier_ids).toEqual(['10'])
    expect(out[0].profile_id).toBe(profileIdForEmail('john@gmail.com'))
  })
  it('§51 / §52 single-file people get exactly one role', () => {
    const maria = person({ source_id: '2', full_name: 'Maria Lopez', name_key: 'marialopez', email: 'maria@x.com' })
    const alex = person({ source_id: '3', source_file: 'drivers', full_name: 'Alex Kim', name_key: 'alexkim', email: 'alex@x.com' })
    const out = resolvePeople({ clients: [maria], drivers: [alex], carriersById, hha: hha() })
    expect(out.find((p) => p.source_id === '2')!.proposed_roles).toEqual(['carrier_dispatcher'])
    expect(out.find((p) => p.source_id === '3')!.proposed_roles).toEqual(['carrier_driver'])
  })
  it('§53 an existing HHA user is linked, never duplicated', () => {
    const out = resolvePeople({ clients: [person({})], drivers: [], carriersById, hha: hha({ profilesByEmail: new Map([['john@gmail.com', { id: 'existing-1' }]]) }) })
    expect(out[0].decision).toBe('link_existing')
    expect(out[0].profile_id).toBe('existing-1')
    expect(out[0].reasons).toContain('existing_hha_profile:existing-1')
  })
  it('§54 the same email on two different names is never merged', () => {
    const john = person({ source_id: '1', email: 'dispatch@abc.com' })
    const mike = person({ source_id: '2', full_name: 'Mike Smith', name_key: 'mikesmith', email: 'dispatch@abc.com' })
    const out = resolvePeople({ clients: [john, mike], drivers: [], carriersById, hha: hha() })
    expect(out.every((p) => p.decision === 'manual_review')).toBe(true)
    expect((out[0].reasons as string[]).some((r) => r.startsWith('shared_email_different_people:2'))).toBe(true)
    expect(out[0].profile_id).toBeNull()
  })
  it('§32 no usable email → held, never guessed', () => {
    const out = resolvePeople({ clients: [], drivers: [person({ source_file: 'drivers', email: '', email_raw: '', email_invalid: false })], carriersById, hha: hha() })
    expect(out[0].decision).toBe('manual_review')
    expect(out[0].reasons).toContain('missing_email')
  })
  it('§34 a generic mailbox used by one person is still held by default', () => {
    const out = resolvePeople({ clients: [person({ email: 'info@abc.com' })], drivers: [], carriersById, hha: hha() })
    expect(out[0].decision).toBe('manual_review')
    expect(out[0].reasons).toContain('generic_mailbox')
  })
  it('Synchron staff and test rows are skipped; a person with no carrier is deferred', () => {
    const staff = person({ source_id: '9', email: 'nash.t@synchrontms.com' })
    const test = person({ source_id: '8', full_name: 'Test Driver', name_key: 'testdriver', email: 'testdriver@gmail.com' })
    const loose = person({ source_id: '7', email: 'loose@x.com', carrier_ids: [], carrier_names: {}, carrier_link_types: {} })
    const out = resolvePeople({ clients: [staff, test, loose], drivers: [], carriersById, hha: hha() })
    expect(out.find((p) => p.source_id === '9')!.decision).toBe('skip')
    expect(out.find((p) => p.source_id === '8')!.decision).toBe('skip')
    expect(out.find((p) => p.source_id === '7')!.decision).toBe('defer')
  })
  it('a person whose only carrier is missing from the export is held', () => {
    const out = resolvePeople({ clients: [person({ carrier_ids: ['99'], carrier_names: { '99': 'Ghost' } })], drivers: [], carriersById, hha: hha() })
    expect(out[0].decision).toBe('manual_review')
    expect(out[0].reasons).toContain('carrier_not_in_export:99')
  })
})

describe('carrier matching order (§13)', () => {
  const c = { source_id: '5', mc: '123456', dot: '7654321', name_key: 'abctransport' }
  it('source id beats MC beats DOT beats name', () => {
    const bySource = { id: 's' }, byMc = { id: 'm' }, byDot = { id: 'd' }, byName = { id: 'n' }
    const base = { companiesBySource: new Map(), companiesByMc: new Map(), companiesByDot: new Map(), companiesByName: new Map() }
    expect(matchCarrier(c, { ...base, companiesBySource: new Map([['5', bySource]]), companiesByMc: new Map([['123456', byMc]]) })).toEqual({ company: bySource, via: 'synchron_id' })
    expect(matchCarrier(c, { ...base, companiesByMc: new Map([['123456', byMc]]), companiesByDot: new Map([['7654321', byDot]]) })).toEqual({ company: byMc, via: 'mc' })
    expect(matchCarrier(c, { ...base, companiesByDot: new Map([['7654321', byDot]]), companiesByName: new Map([['abctransport', byName]]) })).toEqual({ company: byDot, via: 'dot' })
    expect(matchCarrier(c, { ...base, companiesByName: new Map([['abctransport', byName]]) })).toEqual({ company: byName, via: 'name' })
    expect(matchCarrier(c, base)).toBeNull()
  })
})
