/**
 * Pre-flight check against a real Magento (e.g. Znet staging) before the first sync:
 *   pnpm magento:check
 * Verifies the Integration credentials and every API permission mcp-znet uses, reports store views
 * and catalog size, and estimates full-sync duration. Read-only; prints no secrets.
 */
import crypto from 'node:crypto'
import { z } from 'zod'
import { baseEnv, featureEnv, loadEnv, magentoEnv } from '@znet/shared'
import { and, MagentoApiError, magentoFromEnv, type MagentoApi } from '@znet/magento'

const env = loadEnv(
  baseEnv.extend(magentoEnv.shape).extend(featureEnv.shape).extend({ MAGENTO_ADMIN_2FA: z.enum(['google', 'none']).default('google') }),
)
const api: MagentoApi = magentoFromEnv(env, undefined, 30_000)
const oauth1 = Boolean(env.MAGENTO_CONSUMER_KEY && env.MAGENTO_CONSUMER_SECRET && env.MAGENTO_ACCESS_TOKEN_SECRET)

type Row = { check: string; ok: boolean | null; detail: string }
const rows: Row[] = []
let failures = 0

/** Admin-menu path of the Integration "Resource Access" checkbox that grants each call. */
const ACL = {
  stores: 'Stores › Settings › All Stores',
  attributes: 'Stores › Attributes › Product',
  sets: 'Stores › Attributes › Attribute Set',
  categories: 'Catalog › Inventory › Categories',
  products: 'Catalog › Inventory › Products',
  sources: 'Stores › Inventory › Sources',
  orders: 'Sales › Operations › Orders › Actions › View',
  invoices: 'Sales › Operations › Invoices',
  shipments: 'Sales › Operations › Shipments',
}

function explain(err: unknown, acl?: string): string {
  if (err instanceof MagentoApiError) {
    if (err.path === '/graphql') return `${err.status} — storefront GraphQL not available; get_price live=true will not work (everything else does)`
    if (err.status === 401) return `401 unauthorised — ${oauth1 ? 'check the 4 OAuth values / Integration is Active' : 'bearer token rejected: use the 4 OAuth 1.0a values (see docs/magento-setup.md)'}`
    if (err.status === 403) return `403 — tick "${acl}" in the Integration's Resource Access, then re-activate it`
    if (err.status === 404) return '404 — endpoint not available on this Magento'
    return `${err.status} — ${err.message}`
  }
  const e = err as Error & { cause?: { code?: string } }
  return `${e.cause?.code ?? e.name}: ${e.message} — is MAGENTO_BASE_URL reachable from this machine (VPN / IP allow-list)?`
}

async function check<T>(name: string, fn: () => Promise<T>, ok: (v: T) => string, opts: { acl?: string; required?: boolean } = {}) {
  const started = performance.now()
  try {
    const v = await fn()
    rows.push({ check: name, ok: true, detail: `${ok(v)}  (${Math.round(performance.now() - started)} ms)` })
    return v
  } catch (err) {
    if (opts.required !== false) failures++
    rows.push({ check: name, ok: opts.required === false ? null : false, detail: explain(err, opts.acl) })
    return undefined
  }
}

console.log(`\nChecking ${env.MAGENTO_BASE_URL} (auth: ${oauth1 ? 'OAuth 1.0a' : 'Bearer token'})\n`)

const views = await check('Connect + store views', () => api.storeViews(), v => v.filter(s => s.id !== 0).map(s => `${s.code}${s.is_active ? '' : ' (inactive)'}`).join(', '), { acl: ACL.stores })
if (views) {
  await check('Store configs (locale, currency, media URL)', () => api.storeConfigs(), v => v.map(c => `${c.code}: ${c.locale}/${c.base_currency_code}`).join(', '), { acl: ACL.stores })
  await check('Websites', () => api.websites(), v => `${v.filter(w => w.id !== 0).length} website(s)`, { acl: ACL.stores })

  const configured = env.MAGENTO_STORE_CODES
  const unknown = configured.filter(c => !views.some(v => v.code === c))
  if (unknown.length) {
    failures++
    rows.push({ check: 'MAGENTO_STORE_CODES', ok: false, detail: `unknown store code(s): ${unknown.join(', ')}` })
  }
  if (!views.some(v => v.code === env.MAGENTO_DEFAULT_STORE)) {
    failures++
    rows.push({ check: 'MAGENTO_DEFAULT_STORE', ok: false, detail: `"${env.MAGENTO_DEFAULT_STORE}" is not a store code here — pick one of the codes above` })
  }
}

await check('Product attributes', () => api.client.search('/V1/products/attributes', { pageSize: 1 }), r => `${r.total_count} attributes`, { acl: ACL.attributes })
await check('Attribute sets', () => api.client.search('/V1/eav/attribute-sets/list', { filterGroups: and({ field: 'entity_type_id', value: 4 }), pageSize: 1 }), r => `${r.total_count} sets`, { acl: ACL.sets })
await check('Categories', () => api.client.search('/V1/categories/list', { pageSize: 1 }), r => `${r.total_count} categories`, { acl: ACL.categories })

const products = await check('Products', () => api.client.search('/V1/products', { pageSize: 1 }, { fields: 'items[sku],total_count' }), r => `${r.total_count.toLocaleString()} products`, { acl: ACL.products })
await check(
  'Products changed in the last 24 h (incremental filter)',
  () => api.client.search('/V1/products', { filterGroups: and({ field: 'updated_at', value: new Date(Date.now() - 86_400_000).toISOString().slice(0, 19).replace('T', ' '), condition: 'gteq' }), pageSize: 1 }, { fields: 'total_count' }),
  r => `${r.total_count.toLocaleString()} changed`,
  { acl: ACL.products },
)

// Time a realistic page to estimate a full sweep.
let estimate = ''
if (products) {
  const t = performance.now()
  const page = await check('Full product page (200, all fields)', () => api.client.search('/V1/products', { pageSize: 200, currentPage: 1 }), r => `${r.items?.length ?? 0} products`, { acl: ACL.products })
  if (page) {
    const ms = performance.now() - t
    const scopes = 1 + (env.MAGENTO_STORE_CODES.length || (views?.filter(v => v.id !== 0 && v.is_active).length ?? 1))
    const pages = Math.ceil(products.total_count / 200)
    const minutes = (pages * scopes * ms) / 60_000
    estimate = `~${minutes < 1 ? '<1' : Math.round(minutes)} min for a full sweep (${pages} pages × ${scopes} scopes × ${Math.round(ms)} ms/page)`
  }
}

if (env.CATALOG_STOCK_ENABLED) {
  await check('Inventory source items (MSI)', () => api.client.search('/V1/inventory/source-items', { pageSize: 1 }), r => `${r.total_count.toLocaleString()} source items`, { acl: ACL.sources, required: false })
} else {
  rows.push({ check: 'Inventory', ok: null, detail: 'skipped (CATALOG_STOCK_ENABLED=false)' })
}

await check('Orders (live)', () => api.searchOrders({ pageSize: 1, sortOrders: [{ field: 'created_at', direction: 'DESC' }] }), r => `${r.total_count.toLocaleString()} orders, latest ${r.items?.[0]?.created_at ?? '—'}`, { acl: ACL.orders })
await check('Invoices', () => api.client.search('/V1/invoices', { pageSize: 1 }), r => `${r.total_count.toLocaleString()} invoices`, { acl: ACL.invoices })
await check('Shipments', () => api.client.search('/V1/shipments', { pageSize: 1 }), r => `${r.total_count.toLocaleString()} shipments`, { acl: ACL.shipments })

await check(
  'Storefront GraphQL (live price)',
  () => api.client.graphql<{ storeConfig: { store_code: string } }>('{ storeConfig { store_code } }', {}, env.MAGENTO_DEFAULT_STORE),
  d => `store ${d.storeConfig.store_code}`,
  { required: false },
)

// Staff sign-in endpoint: probe with a random non-existent user so no real account is locked out.
const probeUser = `mcp-znet-probe-${crypto.randomBytes(4).toString('hex')}`
const loginPath = env.MAGENTO_ADMIN_2FA === 'google' ? '/V1/tfa/provider/google/authenticate' : '/V1/integration/admin/token'
await check(
  `Staff sign-in endpoint (${env.MAGENTO_ADMIN_2FA === 'google' ? 'Google Authenticator 2FA' : 'no 2FA'})`,
  async () => {
    try {
      await api.client.postAnonymous(loginPath, { username: probeUser, password: 'x', otp: '000000' })
      return 'unexpected success'
    } catch (err) {
      if (err instanceof MagentoApiError && (err.status === 401 || err.status === 400)) return 'present (rejected a dummy login as expected)'
      if (err instanceof MagentoApiError && err.status === 404) throw new Error(env.MAGENTO_ADMIN_2FA === 'google' ? 'not found — Google Authenticator 2FA provider not enabled; ask which 2FA Znet uses or set MAGENTO_ADMIN_2FA=none' : 'not found')
      throw err
    }
  },
  v => v,
)

const width = Math.max(...rows.map(r => r.check.length))
for (const r of rows) console.log(`${r.ok === true ? '✅' : r.ok === false ? '❌' : '➖'}  ${r.check.padEnd(width)}  ${r.detail}`)
if (estimate) console.log(`\n⏱  ${estimate}`)
console.log(failures ? `\n${failures} problem(s) — see docs/magento-setup.md\n` : '\nAll checks passed — ready for `pnpm sync:once sync-reference`.\n')
process.exitCode = failures ? 1 : 0
