import { beforeEach, describe, expect, it, vi } from 'vitest'
import { AppError } from '../../http/errors'
import { prisma } from '../../lib/prisma'
import * as repo from './auth.company.repo'
import * as service from './auth.company.service'
import { sendNotification } from '../../services/notificationService'

vi.mock('bcryptjs', () => ({ default: { hash: vi.fn().mockResolvedValue('hashed-password') } }))
vi.mock('../../lib/prisma', () => ({ prisma: { $transaction: vi.fn() } }))
vi.mock('./auth.company.repo', () => ({
  findCompanyBySlug: vi.fn(),
  findCompanyByEmail: vi.fn(),
  findEmployeeByEmail: vi.fn(),
  createCompanySignup: vi.fn(),
}))
vi.mock('../../services/notificationService', () => ({ sendNotification: vi.fn() }))

const body = {
  firstName: 'Aya',
  lastName: 'Benali',
  email: 'owner@example.com',
  password: 'super-secret',
  companyName: 'Atlas & Desert Cars!!!',
  legalName: 'Atlas Cars SARL',
  legalForm: 'SARL',
  registrationNumber: 'REG123',
  iceNumber: 'ICE123',
  taxId: 'TAX123',
  operatingLicenseNumber: 'LIC123',
  operatingLicenseIssuedAt: '2026-01-01',
  operatingLicenseIssuedBy: 'Transport Authority',
  streetAddress: '1 Fleet Street',
  city: 'Casablanca',
  country: 'Morocco',
  zipCode: '20000',
  companyPhone: '+212600000000',
  companyEmail: 'contact@example.com',
  yearsActive: '5',
  responsibleName: 'Omar Alaoui',
  responsibleRole: 'Manager',
  responsibleIdentityNumber: 'ID123',
  responsiblePhone: '+212611111111',
  responsibleEmail: 'responsible@example.com',
  preferredLanguage: 'fr' as const,
  plan: 'PRO' as const,
  billingPeriod: 'MONTHLY' as const,
  currency: 'MAD' as const,
  paymentProvider: 'PAYPAL' as const,
}

describe('auth.company.service', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    vi.mocked(repo.findCompanyByEmail).mockResolvedValue(null)
    vi.mocked(repo.findEmployeeByEmail).mockResolvedValue(null)
    vi.mocked(repo.findCompanyBySlug).mockResolvedValue(null)
    vi.mocked(repo.createCompanySignup).mockResolvedValue({
      company: { id: 'company_1', name: 'Atlas & Desert Cars!!!', slug: 'atlas-desert-cars' },
      employee: { id: 'employee_1' },
    } as never)
    vi.mocked(prisma.$transaction).mockImplementation(async (fn: any) => fn({ tx: true }))
    vi.mocked(sendNotification).mockResolvedValue([{ channel: 'EMAIL', success: true }] as never)
  })

  it('rejects duplicate company contact email before hashing or transaction work', async () => {
    vi.mocked(repo.findCompanyByEmail).mockResolvedValue({ id: 'company_existing' } as never)

    await expect(service.signup(body)).rejects.toMatchObject({ statusCode: 409, error: 'company_email_taken' })
    expect(prisma.$transaction).not.toHaveBeenCalled()
  })

  it('rejects duplicate owner email before creating tenant data', async () => {
    vi.mocked(repo.findEmployeeByEmail).mockResolvedValue({ id: 'employee_existing' } as never)

    await expect(service.signup(body)).rejects.toMatchObject({ statusCode: 409, error: 'owner_email_taken' })
    expect(prisma.$transaction).not.toHaveBeenCalled()
  })

  it('generates a stable slug, creates the tenant signup, sends notification, and presents the workspace result', async () => {
    const result = await service.signup(body)

    expect(result).toEqual(expect.objectContaining({
      companyId: 'company_1',
      companyName: 'Atlas & Desert Cars!!!',
      slug: 'atlas-desert-cars',
      nextStep: 'workspace_created',
      emailDelivery: { channel: 'EMAIL', success: true },
    }))
    expect(result.trialEndsAt).toEqual(expect.any(String))
    expect(repo.findCompanyBySlug).toHaveBeenCalledWith('atlas-desert-cars')
    expect(repo.createCompanySignup).toHaveBeenCalledWith({ tx: true }, expect.objectContaining({
      slug: 'atlas-desert-cars',
      ownerEmail: 'owner@example.com',
      passwordHash: 'hashed-password',
      managerName: 'Aya Benali',
      trialEndAt: expect.any(Date),
    }))
    expect(sendNotification).toHaveBeenCalledWith(expect.objectContaining({
      type: 'ACCOUNT_CREATED',
      companyId: 'company_1',
      employeeId: 'employee_1',
      email: 'owner@example.com',
      channels: ['EMAIL', 'IN_APP'],
      locale: 'fr',
      templateVariables: expect.objectContaining({
        firstName: 'Aya',
        companyName: 'Atlas & Desert Cars!!!',
        paymentProvider: 'PAYPAL',
      }),
    }))
  })

  it('increments the slug when the clean base slug is already taken', async () => {
    vi.mocked(repo.findCompanyBySlug)
      .mockResolvedValueOnce({ id: 'existing' } as never)
      .mockResolvedValueOnce(null)

    await service.signup(body)

    expect(repo.findCompanyBySlug).toHaveBeenNthCalledWith(1, 'atlas-desert-cars')
    expect(repo.findCompanyBySlug).toHaveBeenNthCalledWith(2, 'atlas-desert-cars-2')
    expect(repo.createCompanySignup).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({ slug: 'atlas-desert-cars-2' }))
  })

  it('keeps signup successful when notification delivery fails', async () => {
    vi.mocked(sendNotification).mockRejectedValue(new Error('smtp down'))

    await expect(service.signup(body)).resolves.toMatchObject({
      companyId: 'company_1',
      emailDelivery: { attempted: false, success: false, error: null },
    })
  })

  it('exposes removed legacy auth endpoints as explicit gone errors', () => {
    expect(() => service.completeSignupDisabled()).toThrow(AppError)
    expect(() => service.verifyEmailDisabled()).toThrow(AppError)
  })
})
