import { beforeEach, describe, expect, it, vi } from 'vitest'

vi.mock('../../lib/prisma', () => ({ prisma: {} }))
vi.mock('../../lib/redis', () => ({
  redis: { on: vi.fn(), get: vi.fn(), set: vi.fn(), del: vi.fn(), quit: vi.fn() },
}))

vi.mock('../../middleware/requireRenterAuth', () => ({
  requireRenterAuth: (req: any, _res: any, next: any) => {
    req.renterId = 'renter_1'
    next()
  },
  optionalRenterAuth: (_req: any, _res: any, next: any) => next(),
}))

vi.mock('../../modules/auth/auth.company.service', () => ({
  signup: vi.fn(),
  completeSignupDisabled: vi.fn(() => {
    const err = new Error('gone') as Error & { statusCode?: number; code?: string }
    err.statusCode = 410
    err.code = 'disabled'
    throw err
  }),
  verifyEmailDisabled: vi.fn(() => {
    const err = new Error('gone') as Error & { statusCode?: number; code?: string }
    err.statusCode = 410
    err.code = 'disabled'
    throw err
  }),
}))

vi.mock('../../modules/auth/auth.account.service', () => ({
  startAccount: vi.fn(),
}))

vi.mock('../../modules/auth/auth.renter.service', () => ({
  signupDisabled: vi.fn(() => {
    const err = new Error('renter disabled') as Error & { statusCode?: number; code?: string }
    err.statusCode = 403
    err.code = 'renter_signup_disabled'
    throw err
  }),
  loginDisabled: vi.fn(() => {
    const err = new Error('login disabled') as Error & { statusCode?: number; code?: string }
    err.statusCode = 403
    err.code = 'renter_login_disabled'
    throw err
  }),
  getMe: vi.fn(),
  updateMe: vi.fn(),
  updateFcmToken: vi.fn(),
}))

import request from 'supertest'
import { createApp } from '../../app'
import * as accountService from '../../modules/auth/auth.account.service'
import * as companyService from '../../modules/auth/auth.company.service'
import * as renterService from '../../modules/auth/auth.renter.service'

const app = createApp()

const signupPayload = {
  firstName: 'Aya',
  lastName: 'Benali',
  email: 'owner@example.com',
  password: 'super-secret',
  companyName: 'Atlas 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',
  plan: 'STARTER',
  billingPeriod: 'MONTHLY',
  currency: 'MAD',
  paymentProvider: 'PAYPAL',
}

describe('auth API boundaries', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    vi.mocked(accountService.startAccount).mockResolvedValue({
      message: 'Account created. Please check your email to verify your address before signing in.',
      email: 'owner@example.com',
    } as never)
    vi.mocked(companyService.signup).mockResolvedValue({ companyId: 'company_1', nextStep: 'workspace_created' } as never)
    vi.mocked(renterService.getMe).mockResolvedValue({ id: 'renter_1', savedCompanies: [] } as never)
    vi.mocked(renterService.updateMe).mockResolvedValue({ id: 'renter_1', firstName: 'Nora' } as never)
    vi.mocked(renterService.updateFcmToken).mockResolvedValue({ success: true } as never)
  })

  it('POST /auth/company/signup validates the full onboarding contract and returns 201', async () => {
    const res = await request(app).post('/api/v1/auth/company/signup').send(signupPayload)

    expect(res.status).toBe(201)
    expect(res.body).toEqual({ data: { companyId: 'company_1', nextStep: 'workspace_created' } })
    expect(companyService.signup).toHaveBeenCalledWith(expect.objectContaining({
      companyName: 'Atlas Cars',
      currency: 'MAD',
      preferredLanguage: 'fr',
      paymentProvider: 'PAYPAL',
    }))
  })

  it('POST /auth/company/signup rejects malformed onboarding payloads before service execution', async () => {
    const res = await request(app).post('/api/v1/auth/company/signup').send({ ...signupPayload, companyEmail: 'not-email' })

    expect(res.status).toBe(400)
    expect(res.body.error).toBe('validation_error')
    expect(companyService.signup).not.toHaveBeenCalled()
  })

  it('POST /auth/account/start validates minimal signup and returns 201', async () => {
    const res = await request(app).post('/api/v1/auth/account/start').send({
      email: 'owner@example.com',
      password: 'super-secret',
      preferredLanguage: 'fr',
    })

    expect(res.status).toBe(201)
    expect(res.body).toEqual({
      data: {
        message: 'Account created. Please check your email to verify your address before signing in.',
        email: 'owner@example.com',
      },
    })
    expect(accountService.startAccount).toHaveBeenCalledWith({
      email: 'owner@example.com',
      password: 'super-secret',
      preferredLanguage: 'fr',
    })
  })

  it('POST /auth/account/start rejects malformed minimal signup payloads before service execution', async () => {
    const res = await request(app).post('/api/v1/auth/account/start').send({
      email: 'not-email',
      password: 'short',
    })

    expect(res.status).toBe(400)
    expect(res.body.error).toBe('validation_error')
    expect(accountService.startAccount).not.toHaveBeenCalled()
  })

  it('preserves removed company auth compatibility endpoints as explicit 410 responses', async () => {
    const complete = await request(app).post('/api/v1/auth/company/complete-signup').send({})
    const verify = await request(app).post('/api/v1/auth/company/verify-email').send({})

    expect(complete.status).toBe(410)
    expect(complete.body.error).toBe('disabled')
    expect(verify.status).toBe(410)
    expect(verify.body.error).toBe('disabled')
  })

  it('keeps public renter signup and login disabled at the router boundary', async () => {
    const signup = await request(app).post('/api/v1/auth/renter/signup').send({})
    const login = await request(app).post('/api/v1/auth/renter/login').send({})

    expect(signup.status).toBe(403)
    expect(signup.body.error).toBe('renter_signup_disabled')
    expect(login.status).toBe(403)
    expect(login.body.error).toBe('renter_login_disabled')
  })

  it('GET /auth/renter/me uses authenticated renter identity', async () => {
    const res = await request(app).get('/api/v1/auth/renter/me')

    expect(res.status).toBe(200)
    expect(res.body).toEqual({ data: { id: 'renter_1', savedCompanies: [] } })
    expect(renterService.getMe).toHaveBeenCalledWith('renter_1')
  })

  it('PATCH /auth/renter/me validates renter profile updates', async () => {
    const res = await request(app).patch('/api/v1/auth/renter/me').send({ firstName: '  Nora  ', preferredCurrency: 'MAD' })

    expect(res.status).toBe(200)
    expect(renterService.updateMe).toHaveBeenCalledWith('renter_1', { firstName: 'Nora', preferredCurrency: 'MAD' })
  })

  it('POST /auth/renter/me/fcm-token updates only the token value for the authenticated renter', async () => {
    const res = await request(app).post('/api/v1/auth/renter/me/fcm-token').send({ fcmToken: 'token_123' })

    expect(res.status).toBe(200)
    expect(res.body).toEqual({ data: { success: true } })
    expect(renterService.updateFcmToken).toHaveBeenCalledWith('renter_1', 'token_123')
  })
})
