import { describe, expect, it } from 'vitest'
import { filterInviteContacts, inviteContactGroups, mergeInviteContacts, type DerivedPerson } from '@/lib/domain/contacts'

/**
 * "Invite from contacts" (Nash, 2026-09-18): "if I already have him in
 * contacts, if I worked with him before, why should I do the whole
 * invitation from scratch?… their contacts is anyone that they previously
 * worked with — any people that had relations to any of the trips where
 * they were a part of. They automatically become a contact."
 */

const saved = [
  { id: 'c1', name: 'Dana Dispatch', email: 'dana@carrier.example', phone: '555-0100', phone_ext: '12', role: 'dispatcher' as const, last_used_at: '2026-09-10T00:00:00Z', created_at: '2026-08-01T00:00:00Z' },
  { id: 'c2', name: 'Drew Driver', email: 'drew@carrier.example', phone: '555-0101', phone_ext: null, role: 'driver' as const, last_used_at: null, created_at: '2026-08-02T00:00:00Z' },
]
const derived: DerivedPerson[] = [
  // Same person as saved c2 but the trip row has an older phone — saved must win.
  { name: 'Drew Driver', email: 'DREW@carrier.example', phone: '555-9999', phone_ext: null, role: 'driver', created_at: '2026-09-15T00:00:00Z' },
  { name: 'Pat Pilot', email: 'pat@escort.example', phone: '555-0102', phone_ext: null, role: 'pilot', created_at: '2026-09-12T00:00:00Z' },
  { name: 'Pat Pilot', email: 'pat@escort.example', phone: '555-0102', phone_ext: null, role: 'pilot', created_at: '2026-09-16T00:00:00Z' },
  { name: 'Bo Broker', email: 'bo@broker.example', phone: null, phone_ext: null, role: 'broker', created_at: '2026-09-01T00:00:00Z' },
  { name: 'Me', email: 'me@carrier.example', phone: null, phone_ext: null, role: 'dispatcher', created_at: '2026-09-17T00:00:00Z' },
  { name: 'Shipper Co', email: 'ship@x.example', phone: null, phone_ext: null, role: 'shipper', created_at: '2026-09-17T00:00:00Z' },
]

describe('mergeInviteContacts', () => {
  const all = mergeInviteContacts(saved, derived, 'ME@carrier.example')

  it('everyone I shared a trip with becomes a contact, without saving anything', () => {
    const pat = all.find((c) => c.email === 'pat@escort.example')!
    expect(pat).toMatchObject({ role: 'pilot', source: 'trip', name: 'Pat Pilot' })
    expect(pat.id.startsWith('trip:')).toBe(true)
  })

  it('a saved entry wins over the same person derived from a trip', () => {
    const drew = all.filter((c) => c.email.toLowerCase() === 'drew@carrier.example')
    expect(drew).toHaveLength(1)
    expect(drew[0]).toMatchObject({ id: 'c2', source: 'saved', phone: '555-0101' })
  })

  it('I am never my own contact — matched case-insensitively', () => {
    expect(all.some((c) => c.email.toLowerCase() === 'me@carrier.example')).toBe(false)
  })

  it('only invitable roles count; a shipper on the trip is not offered', () => {
    expect(all.some((c) => c.email === 'ship@x.example')).toBe(false)
  })

  it('the same trip-derived person on two trips is one contact, dated by the newest trip', () => {
    const pat = all.filter((c) => c.email === 'pat@escort.example')
    expect(pat).toHaveLength(1)
    expect(pat[0].last_used_at).toBe('2026-09-16T00:00:00Z')
  })

  it('orders by most recently worked with, then name', () => {
    expect(all.map((c) => c.name)).toEqual(['Pat Pilot', 'Dana Dispatch', 'Bo Broker', 'Drew Driver'])
  })
})

describe('inviteContactGroups', () => {
  const all = mergeInviteContacts(saved, derived, 'me@carrier.example')

  it('one group per role the inviter may invite, in role order, drivers and brokers kept apart', () => {
    const groups = inviteContactGroups(all, ['driver', 'pilot', 'broker'])
    expect(groups.map((g) => g.role)).toEqual(['driver', 'broker', 'pilot'])
    expect(groups.find((g) => g.role === 'driver')!.contacts.map((c) => c.name)).toEqual(['Drew Driver'])
    expect(groups.find((g) => g.role === 'broker')!.contacts.map((c) => c.name)).toEqual(['Bo Broker'])
  })

  it('a role the inviter may not use is not offered, even with contacts in it', () => {
    // A pilot may invite drivers only (INVITE_RULES) — the dispatcher contact is not shown.
    const groups = inviteContactGroups(all, ['driver'])
    expect(groups.map((g) => g.role)).toEqual(['driver'])
  })

  it('an invitable role with nobody in it is still a (empty) group, not a missing tab', () => {
    const groups = inviteContactGroups([], ['dispatcher', 'driver'])
    expect(groups).toEqual([
      { role: 'driver', contacts: [] },
      { role: 'dispatcher', contacts: [] },
    ])
  })
})

describe('telling people apart (Nash: "14 different Andres")', () => {
  const andres: DerivedPerson[] = [
    { name: 'Andre Popescu', email: 'heavyhaul@panlogistics.net', phone: '555-0001', phone_ext: null, role: 'dispatcher', created_at: '2026-09-01T00:00:00Z', company: 'PAN LOGISTICS INC', trip_ref: 'HH-70305660' },
    { name: 'Andre Popescu', email: 'andre@atlas.example', phone: '555-0002', phone_ext: null, role: 'dispatcher', created_at: '2026-09-02T00:00:00Z', company: 'ATLAS LOGISTICS GROUP INC', trip_ref: 'HH-95595781' },
    { name: 'Andre Popescu', email: 'ap@nowhere.example', phone: null, phone_ext: null, role: 'dispatcher', created_at: '2026-09-03T00:00:00Z' },
  ]
  const all = mergeInviteContacts([], andres, 'me@x.example')

  it('same name, different emails → three distinct contacts, each carrying company and last trip', () => {
    expect(all).toHaveLength(3)
    expect(all.find((c) => c.email === 'heavyhaul@panlogistics.net')).toMatchObject({ company: 'PAN LOGISTICS INC', last_trip_ref: 'HH-70305660' })
    expect(all.find((c) => c.email === 'ap@nowhere.example')).toMatchObject({ company: null })
  })

  it('search matches name, email, company, phone or trip — every word must hit', () => {
    expect(filterInviteContacts(all, 'andre').length).toBe(3)
    expect(filterInviteContacts(all, 'andre pan').map((c) => c.email)).toEqual(['heavyhaul@panlogistics.net'])
    expect(filterInviteContacts(all, 'atlas').map((c) => c.email)).toEqual(['andre@atlas.example'])
    expect(filterInviteContacts(all, '555-0002').map((c) => c.email)).toEqual(['andre@atlas.example'])
    expect(filterInviteContacts(all, 'HH-955').map((c) => c.email)).toEqual(['andre@atlas.example'])
    expect(filterInviteContacts(all, 'nowhere')).toHaveLength(1)
    expect(filterInviteContacts(all, 'zzz')).toHaveLength(0)
    expect(filterInviteContacts(all, '   ')).toHaveLength(3)
  })

  it('the same person seen on two trips keeps the NEWEST trip and gains a company later learned', () => {
    const merged = mergeInviteContacts([], [
      { name: 'Sam', email: 'sam@x.example', phone: null, phone_ext: null, role: 'driver', created_at: '2026-09-01T00:00:00Z', trip_ref: 'HH-1' },
      { name: 'Sam', email: 'sam@x.example', phone: null, phone_ext: null, role: 'driver', created_at: '2026-09-05T00:00:00Z', trip_ref: 'HH-2', company: 'Sam Trucking' },
    ], 'me@x.example')
    expect(merged).toHaveLength(1)
    expect(merged[0]).toMatchObject({ last_trip_ref: 'HH-2', company: 'Sam Trucking', last_used_at: '2026-09-05T00:00:00Z' })
  })

  it('a saved contact keeps its details but picks up a company from an account', () => {
    const merged = mergeInviteContacts(
      [{ id: 'c9', name: 'Dana', email: 'dana@x.example', phone: '1', phone_ext: null, role: 'dispatcher', last_used_at: null, created_at: '2026-08-01T00:00:00Z', company: 'Dana Freight' }],
      [{ name: 'Dana', email: 'dana@x.example', phone: '2', phone_ext: null, role: 'dispatcher', created_at: '2026-09-01T00:00:00Z', trip_ref: 'HH-3' }],
      'me@x.example',
    )
    expect(merged[0]).toMatchObject({ id: 'c9', phone: '1', company: 'Dana Freight', last_trip_ref: 'HH-3' })
  })
})
