import request from 'supertest'
import { createApp } from '../../app'
import {
  authHeader,
  createAdminUser,
  createCompanyWithEmployee,
  createRenter,
  signAdminToken,
} from '../helpers/fixtures'

const app = createApp()

describe('Admin API', () => {
  let adminToken: string
  let protectedAdminToken: string
  let financeToken: string
  let viewerToken: string
  let adminId: string
  let companyId: string

  beforeAll(async () => {
    const admin = await createAdminUser({
      email:     'admin-login@test.com',
      firstName: 'Primary',
      lastName:  'Admin',
      role:      'ADMIN',
      password:  'AdminPass123!',
    })
    adminId = admin.id
    adminToken = signAdminToken(admin.id)

    const protectedAdmin = await createAdminUser({
      email:       'protected-admin@test.com',
      role:        'ADMIN',
      password:    'AdminPass123!',
      totpEnabled: true,
      totpSecret:  'JBSWY3DPEHPK3PXP',
    })
    protectedAdminToken = signAdminToken(protectedAdmin.id)

    const financeAdmin = await createAdminUser({
      email:       'finance-admin@test.com',
      role:        'FINANCE',
      password:    'FinancePass123!',
      totpEnabled: true,
      totpSecret:  'JBSWY3DPEHPK3PXP',
    })
    financeToken = signAdminToken(financeAdmin.id)

    const viewerAdmin = await createAdminUser({
      email:       'viewer-admin@test.com',
      role:        'VIEWER',
      password:    'ViewerPass123!',
      totpEnabled: true,
      totpSecret:  'JBSWY3DPEHPK3PXP',
    })
    viewerToken = signAdminToken(viewerAdmin.id)

    const { company } = await createCompanyWithEmployee()
    companyId = company.id
    await createRenter()
  })

  describe('POST /api/v1/admin/auth/login', () => {
    it('returns 401 for invalid credentials', async () => {
      const res = await request(app)
        .post('/api/v1/admin/auth/login')
        .send({
          email:    'admin-login@test.com',
          password: 'wrong-password',
        })

      expect(res.status).toBe(401)
      expect(res.body.error).toBe('invalid_credentials')
    })

    it('returns a token for valid credentials', async () => {
      const res = await request(app)
        .post('/api/v1/admin/auth/login')
        .send({
          email:    'admin-login@test.com',
          password: 'AdminPass123!',
        })

      expect(res.status).toBe(200)
      expect(typeof res.body.data.token).toBe('string')
      expect(res.body.data.admin.id).toBe(adminId)
    })
  })

  describe('GET /api/v1/admin/auth/me', () => {
    it('returns the authenticated admin profile', async () => {
      const res = await request(app)
        .get('/api/v1/admin/auth/me')
        .set(authHeader(adminToken))

      expect(res.status).toBe(200)
      expect(res.body.data.id).toBe(adminId)
      expect(res.body.data).not.toHaveProperty('passwordHash')
    })
  })

  describe('GET /api/v1/admin/companies', () => {
    it('returns paginated companies', async () => {
      const res = await request(app)
        .get('/api/v1/admin/companies')
        .set(authHeader(protectedAdminToken))

      expect(res.status).toBe(200)
      expect(Array.isArray(res.body.data.data)).toBe(true)
      expect(typeof res.body.data.total).toBe('number')
      expect(res.body.data.data.some((item: any) => item.id === companyId)).toBe(true)
    })
  })

  describe('GET /api/v1/admin/renters', () => {
    it('returns paginated renters', async () => {
      const res = await request(app)
        .get('/api/v1/admin/renters')
        .set(authHeader(protectedAdminToken))

      expect(res.status).toBe(200)
      expect(Array.isArray(res.body.data.data)).toBe(true)
      expect(typeof res.body.data.total).toBe('number')
      expect(res.body.data.total).toBeGreaterThanOrEqual(1)
    })
  })

  describe('GET /api/v1/admin/metrics', () => {
    it('returns 403 for VIEWER role', async () => {
      const res = await request(app)
        .get('/api/v1/admin/metrics')
        .set(authHeader(viewerToken))

      expect(res.status).toBe(403)
    })

    it('returns platform metrics for FINANCE role', async () => {
      const res = await request(app)
        .get('/api/v1/admin/metrics')
        .set(authHeader(financeToken))

      expect(res.status).toBe(200)
      expect(res.body.data).toEqual(
        expect.objectContaining({
          totalCompanies: expect.any(Number),
          totalRenters: expect.any(Number),
          totalReservations: expect.any(Number),
        }),
      )
    })
  })

  describe('Billing admin workflows', () => {
    let billingAccountId: string

    beforeAll(async () => {
      const res = await request(app)
        .get('/api/v1/admin/billing')
        .set(authHeader(financeToken))

      expect(res.status).toBe(200)
      const account = res.body.data.data.find((item: any) => item.company.id === companyId)
      expect(account).toBeTruthy()
      billingAccountId = account.id
    })

    it('returns billing account detail', async () => {
      const res = await request(app)
        .get(`/api/v1/admin/billing/${companyId}`)
        .set(authHeader(financeToken))

      expect(res.status).toBe(200)
      expect(res.body.data.id).toBe(billingAccountId)
      expect(res.body.data.company.id).toBe(companyId)
      expect(Array.isArray(res.body.data.invoices)).toBe(true)
    })

    it('supports create, finalize, pay, refund, and pdf download', async () => {
      const draftRes = await request(app)
        .post(`/api/v1/admin/billing/accounts/${billingAccountId}/invoices`)
        .set(authHeader(financeToken))
        .send({
          invoiceType: 'MANUAL',
          adminReason: 'Enterprise onboarding',
          lineItems: [
            {
              type: 'SETUP_FEE',
              description: 'Onboarding setup',
              quantity: 1,
              unitAmount: 50000,
            },
            {
              type: 'PROFESSIONAL_SERVICES',
              description: 'Migration assistance',
              quantity: 2,
              unitAmount: 15000,
            },
          ],
        })

      expect(draftRes.status).toBe(201)
      expect(draftRes.body.data.status).toBe('DRAFT')
      const invoiceId = draftRes.body.data.id as string

      const finalizeRes = await request(app)
        .post(`/api/v1/admin/billing/invoices/${invoiceId}/finalize`)
        .set(authHeader(financeToken))
        .send({})

      expect(finalizeRes.status).toBe(200)
      expect(finalizeRes.body.data.status).toBe('OPEN')
      expect(finalizeRes.body.data.invoiceNumber).toMatch(/^INV-\d{4}-\d{6}$/)
      expect(finalizeRes.body.data.totalAmount).toBe(80000)

      const pdfRes = await request(app)
        .get(`/api/v1/admin/billing/invoices/${invoiceId}/pdf`)
        .set(authHeader(financeToken))

      expect(pdfRes.status).toBe(200)
      expect(pdfRes.headers['content-type']).toContain('application/pdf')

      const payRes = await request(app)
        .post(`/api/v1/admin/billing/invoices/${invoiceId}/pay`)
        .set(authHeader(financeToken))
        .send({ amount: 80000, providerPaymentId: 'manual-payment-1' })

      expect(payRes.status).toBe(200)
      expect(payRes.body.data.status).toBe('PAID')
      expect(payRes.body.data.amountDue).toBe(0)

      const refundRes = await request(app)
        .post(`/api/v1/admin/billing/invoices/${invoiceId}/refunds`)
        .set(authHeader(financeToken))
        .send({ amount: 20000, reason: 'Service credit after go-live issue' })

      expect(refundRes.status).toBe(200)
      expect(refundRes.body.data.status).toBe('PARTIALLY_REFUNDED')
    })

    it('supports credit notes, dunning controls, voids, and write-offs', async () => {
      const pauseRes = await request(app)
        .post(`/api/v1/admin/billing/accounts/${billingAccountId}/pause-dunning`)
        .set(authHeader(financeToken))
        .send({})

      expect(pauseRes.status).toBe(200)
      expect(pauseRes.body.data.dunningPaused).toBe(true)

      const invoiceRes = await request(app)
        .post(`/api/v1/admin/billing/accounts/${billingAccountId}/invoices`)
        .set(authHeader(financeToken))
        .send({
          invoiceType: 'MANUAL',
          lineItems: [
            {
              type: 'MANUAL_ADJUSTMENT',
              description: 'Support package',
              quantity: 1,
              unitAmount: 30000,
            },
          ],
        })

      expect(invoiceRes.status).toBe(201)
      const invoiceId = invoiceRes.body.data.id as string

      const finalizeRes = await request(app)
        .post(`/api/v1/admin/billing/invoices/${invoiceId}/finalize`)
        .set(authHeader(financeToken))
        .send({})

      expect(finalizeRes.status).toBe(200)
      expect(finalizeRes.body.data.status).toBe('OPEN')

      const creditRes = await request(app)
        .post(`/api/v1/admin/billing/invoices/${invoiceId}/credit-notes`)
        .set(authHeader(financeToken))
        .send({ amount: 5000, reason: 'Manual goodwill credit' })

      expect(creditRes.status).toBe(200)
      expect(creditRes.body.data.amountDue).toBe(25000)

      const retryRes = await request(app)
        .post(`/api/v1/admin/billing/invoices/${invoiceId}/retry-payment`)
        .set(authHeader(financeToken))
        .send({})

      expect(retryRes.status).toBe(200)
      expect(retryRes.body.data.status).toBe('PAYMENT_PENDING')

      const voidRes = await request(app)
        .post(`/api/v1/admin/billing/invoices/${invoiceId}/void`)
        .set(authHeader(financeToken))
        .send({ reason: 'Customer rolled into replacement invoice' })

      expect(voidRes.status).toBe(200)
      expect(voidRes.body.data.status).toBe('VOID')

      const badDebtRes = await request(app)
        .post(`/api/v1/admin/billing/accounts/${billingAccountId}/invoices`)
        .set(authHeader(financeToken))
        .send({
          invoiceType: 'SUBSCRIPTION_RENEWAL',
          isSubscriptionBlocking: true,
          lineItems: [
            {
              type: 'SUBSCRIPTION_FEE',
              description: 'Renewal period',
              quantity: 1,
              unitAmount: 12000,
            },
          ],
        })

      expect(badDebtRes.status).toBe(201)
      const badDebtInvoiceId = badDebtRes.body.data.id as string

      const finalizeBadDebtRes = await request(app)
        .post(`/api/v1/admin/billing/invoices/${badDebtInvoiceId}/finalize`)
        .set(authHeader(financeToken))
        .send({})

      expect(finalizeBadDebtRes.status).toBe(200)

      const writeoffRes = await request(app)
        .post(`/api/v1/admin/billing/invoices/${badDebtInvoiceId}/uncollectible`)
        .set(authHeader(financeToken))
        .send({ reason: 'Customer account closed after failed recovery' })

      expect(writeoffRes.status).toBe(200)
      expect(writeoffRes.body.data.status).toBe('UNCOLLECTIBLE')

      const resumeRes = await request(app)
        .post(`/api/v1/admin/billing/accounts/${billingAccountId}/resume-dunning`)
        .set(authHeader(financeToken))
        .send({})

      expect(resumeRes.status).toBe(200)
      expect(resumeRes.body.data.dunningPaused).toBe(false)
    })
  })
})
