import { Hono } from 'hono'
import { z } from 'zod'
import type { AppEnv } from '../../types'
import { authMiddleware } from '../../middleware/auth'
import { requirePermission } from '../../middleware/guards'
import {
  getInvoiceAdapterSettings,
  updateInvoiceAdapterSettings,
  listAdapterConfigs,
  upsertAdapterConfig,
  listSyncLogs,
  removeAdapterConfig,
  IL_INVOICE_PROVIDERS,
} from '@zync/db/queries'
import { getAdapter } from '../invoice-adapters/router'

const providerSchema = z.enum(IL_INVOICE_PROVIDERS)

const credentialsSchema = z.object({
  apiKey: z.string().min(1).optional(),
  apiSecret: z.string().min(1).optional(),
  companyId: z.string().min(1).optional(),
  metadata: z.object({
    accountEmail: z.string().email().optional(),
    companyName: z.string().min(1).optional(),
  }).optional().default({}),
})

const connectBodySchema = z.object({
  credentials: credentialsSchema,
  sync_on_sent: z.boolean().optional(),
  sync_on_tax_issued: z.boolean().optional(),
  sync_credit_notes: z.boolean().optional(),
})

const patchBodySchema = z.object({
  sync_on_sent: z.boolean().optional(),
  sync_on_tax_issued: z.boolean().optional(),
  sync_credit_notes: z.boolean().optional(),
})

const testBodySchema = z.object({
  credentials: credentialsSchema,
})

const logsQuerySchema = z.object({
  status: z.enum(['success', 'error']).optional(),
  entity_type: z.enum(['invoice', 'credit_note', 'payment']).optional(),
  from: z.string().optional(),
  to: z.string().optional(),
  page: z.coerce.number().int().min(1).optional().default(1),
  per_page: z.coerce.number().int().min(1).max(100).optional().default(50),
})

function mapSyncSettings(
  input: {
    sync_on_sent?: boolean
    sync_on_tax_issued?: boolean
    sync_credit_notes?: boolean
  },
) {
  return {
    autoSyncOnTaxIssue: input.sync_on_tax_issued,
    autoSendRetainerInvoice: input.sync_on_sent,
    autoDraftFromRetainer: input.sync_credit_notes,
  }
}

export const invoiceIntegrationsSettingsRoute = new Hono<AppEnv>()

invoiceIntegrationsSettingsRoute.use('*', authMiddleware)

invoiceIntegrationsSettingsRoute.get('/', requirePermission('settings:read'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }
  const tenantId = session.tid

  const db = c.get('db')
  const [settings, configs] = await Promise.all([
    getInvoiceAdapterSettings(db, tenantId),
    listAdapterConfigs(db, tenantId),
  ])

  const adapters = await Promise.all(
    configs.map(async (config) => {
      const latest = await listSyncLogs(db, tenantId, {
        provider: config.provider,
        limit: 1,
      })
      const last = latest.items[0]
      return {
        adapter_id: config.provider,
        status: settings.adapter === config.provider && config.configured ? 'active' : 'inactive',
        last_sync_at: last?.createdAt ?? null,
        last_sync_status: last ? (last.status === 'failure' ? 'error' : 'ok') : null,
        ...(config.metadata.companyName ? { company_name: config.metadata.companyName } : {}),
      }
    }),
  )

  return c.json({ adapters })
})

invoiceIntegrationsSettingsRoute.post('/:adapterId/test', requirePermission('settings:write'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const providerParse = providerSchema.safeParse(c.req.param('adapterId'))
  if (!providerParse.success) {
    return c.json({ error: 'Unknown adapter' }, 400)
  }

  const bodyParse = testBodySchema.safeParse(await c.req.json().catch(() => null))
  if (!bodyParse.success) {
    return c.json({ error: 'Validation error', issues: bodyParse.error.issues }, 400)
  }

  const provider = providerParse.data
  const candidateConfig = {
    provider,
    apiKey: bodyParse.data.credentials.apiKey,
    apiSecret: bodyParse.data.credentials.apiSecret,
    companyId: bodyParse.data.credentials.companyId,
    metadata: bodyParse.data.credentials.metadata ?? {},
  }

  try {
    const result = await (getAdapter(provider, candidateConfig) as {
      testConnection: () => Promise<unknown>
    }).testConnection()
    const resultData = result && typeof result === 'object' ? result as Record<string, unknown> : {}
    const checks = resultData.capabilities && typeof resultData.capabilities === 'object'
      ? resultData.capabilities as Record<string, boolean>
      : { createInvoice: true, createCreditNote: true }

    return c.json({
      ok: true,
      company_name:
        typeof resultData.companyName === 'string'
          ? resultData.companyName
          : candidateConfig.metadata.companyName,
      checks: {
        create_invoice: checks.createInvoice !== false,
        create_credit_note: checks.createCreditNote !== false,
      },
    })
  } catch (err) {
    return c.json({ ok: false, error: err instanceof Error ? err.message : 'Connection failed' }, 502)
  }
})

invoiceIntegrationsSettingsRoute.post('/:adapterId/connect', requirePermission('settings:write'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const providerParse = providerSchema.safeParse(c.req.param('adapterId'))
  if (!providerParse.success) {
    return c.json({ error: 'Unknown adapter' }, 400)
  }

  const bodyParse = connectBodySchema.safeParse(await c.req.json().catch(() => null))
  if (!bodyParse.success) {
    return c.json({ error: 'Validation error', issues: bodyParse.error.issues }, 400)
  }

  const provider = providerParse.data
  const body = bodyParse.data
  const db = c.get('db')

  await upsertAdapterConfig(
    db,
    session.tid,
    provider,
    {
      apiKey: body.credentials.apiKey,
      apiSecret: body.credentials.apiSecret,
      companyId: body.credentials.companyId,
    },
    body.credentials.metadata ?? {},
    c.env.INTEGRATION_ENCRYPTION_KEY,
  )

  await updateInvoiceAdapterSettings(
    db,
    session.tid,
    session.sub,
    {
      adapter: provider,
      ...mapSyncSettings(body),
    },
  )

  return c.json({ ok: true })
})

invoiceIntegrationsSettingsRoute.patch('/:adapterId', requirePermission('settings:write'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const providerParse = providerSchema.safeParse(c.req.param('adapterId'))
  if (!providerParse.success) {
    return c.json({ error: 'Unknown adapter' }, 400)
  }

  const bodyParse = patchBodySchema.safeParse(await c.req.json().catch(() => null))
  if (!bodyParse.success) {
    return c.json({ error: 'Validation error', issues: bodyParse.error.issues }, 400)
  }

  await updateInvoiceAdapterSettings(
    c.get('db'),
    session.tid,
    session.sub,
    {
      adapter: providerParse.data,
      ...mapSyncSettings(bodyParse.data),
    },
  )

  return c.json({ ok: true })
})

invoiceIntegrationsSettingsRoute.delete('/:adapterId', requirePermission('settings:write'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const providerParse = providerSchema.safeParse(c.req.param('adapterId'))
  if (!providerParse.success) {
    return c.json({ error: 'Unknown adapter' }, 400)
  }

  await removeAdapterConfig(c.get('db'), session.tid, providerParse.data)
  await updateInvoiceAdapterSettings(c.get('db'), session.tid, session.sub, { adapter: null })
  return c.json({ ok: true })
})

invoiceIntegrationsSettingsRoute.get('/:adapterId/sync-logs', requirePermission('settings:read'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const providerParse = providerSchema.safeParse(c.req.param('adapterId'))
  if (!providerParse.success) {
    return c.json({ error: 'Unknown adapter' }, 400)
  }

  const queryParse = logsQuerySchema.safeParse(Object.fromEntries(new URL(c.req.url).searchParams))
  if (!queryParse.success) {
    return c.json({ error: 'Invalid query', issues: queryParse.error.issues }, 400)
  }

  const query = queryParse.data
  const page = await listSyncLogs(c.get('db'), session.tid, {
    provider: providerParse.data,
    entityType: query.entity_type,
    status: query.status === 'error' ? 'failure' : query.status,
    limit: query.per_page,
  })

  const filteredItems = page.items.filter((item) => {
    if (query.from && item.createdAt < query.from) return false
    if (query.to && item.createdAt > query.to) return false
    return true
  })

  return c.json({
    items: filteredItems.map((item) => ({
      id: item.id,
      created_at: item.createdAt,
      entity_type: item.entityType,
      entity_id: item.entityId,
      entity_number: null,
      direction: item.direction,
      status: item.status === 'failure' ? 'error' : item.status,
      external_id: item.externalId,
      error_message:
        item.errorPayload && typeof item.errorPayload.message === 'string'
          ? item.errorPayload.message
          : null,
      request_payload:
        item.errorPayload && typeof item.errorPayload.request_payload !== 'undefined'
          ? item.errorPayload.request_payload
          : null,
      response_payload:
        item.errorPayload && typeof item.errorPayload.response_payload !== 'undefined'
          ? item.errorPayload.response_payload
          : null,
    })),
    page: query.page,
    per_page: query.per_page,
    total: page.total,
    next_cursor: page.nextCursor,
  })
})
