/**
 * CRM settings routes — settings-crm (wave-15).
 * Mounted at /api/settings/crm.
 *
 * GET  /             → CrmSettingsResponse     (requires settings:read)
 * PATCH /            → CrmSettingsResponse     (requires settings:write)
 * GET  /lost-reasons → string[]                (requires marketing:read)  [wave-14 compat]
 * PUT  /lost-reasons → string[]                (requires marketing:write) [wave-14 compat]
 */
import { Hono } from 'hono'
import { z } from 'zod'
import type { AppEnv } from '../../types'
import { authMiddleware } from '../../middleware/auth'
import { requirePermission } from '../../middleware/guards'
import { createDb, getLostReasons, updateLostReasons, getCrmSettings, updateCrmSettings } from '@zync/db/queries'

// ── Validators ────────────────────────────────────────────────────────────────

const updateLostReasonsSchema = z.object({
  reasons: z
    .array(z.string().min(1).max(200))
    .min(1)
    .max(20),
})

const pipelineStageSchema = z.object({
  id: z.string().min(1),
  label: z.string().min(1),
  win_probability: z.number().int().min(0).max(100),
  position: z.number().int().min(1),
  terminal: z.boolean(),
})

const leadScoringCriteriaSchema = z.object({
  estimated_value_set: z.number().int().min(0).max(100),
  high_value: z.number().int().min(0).max(100),
  stage_advancement: z.number().int().min(0).max(100),
  recent_activity: z.number().int().min(0).max(100),
  inbound_source: z.number().int().min(0).max(100),
  company_present: z.number().int().min(0).max(100),
  phone_present: z.number().int().min(0).max(100),
  proposal_viewed: z.number().int().min(0).max(100),
  high_value_threshold: z.number().int().min(0),
}).refine(
  (c) =>
    c.estimated_value_set + c.high_value + c.stage_advancement +
    c.recent_activity + c.inbound_source + c.company_present +
    c.phone_present + c.proposal_viewed === 100,
  { message: 'Scoring weights must sum to 100' },
)

const patchCrmSettingsSchema = z.object({
  lead_scoring_enabled: z.boolean().optional(),
  lead_scoring_criteria: leadScoringCriteriaSchema.optional(),
  pipeline_stages: z.array(pipelineStageSchema).min(2).optional(),
  lead_lost_reasons: z.array(z.string().min(1).max(200)).min(1).max(50).optional(),
})

// ── Router ────────────────────────────────────────────────────────────────────

export const crmSettingsRoute = new Hono<AppEnv>()

crmSettingsRoute.use('*', authMiddleware)

// GET /api/settings/crm
crmSettingsRoute.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 db = createDb(c.env)
  const settings = await getCrmSettings(db, session.tid)
  return c.json(settings, 200)
})

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

  const body = await c.req.json().catch(() => null)
  const parsed = patchCrmSettingsSchema.safeParse(body)
  if (!parsed.success) {
    return c.json({ error: 'Validation failed', issues: parsed.error.issues }, 422)
  }

  const data = parsed.data

  // Validate pipeline_stages terminal-stage constraint
  if (data.pipeline_stages !== undefined) {
    const stages = data.pipeline_stages
    const terminalIds = stages.filter((s) => s.terminal).map((s) => s.id)
    const requiredTerminals = ['won', 'lost']
    for (const tid of requiredTerminals) {
      if (!terminalIds.includes(tid)) {
        return c.json({
          error: 'Validation failed',
          issues: [{ message: `Terminal stage '${tid}' cannot be removed` }],
        }, 422)
      }
    }
    // Validate contiguous positions
    const positions = stages.map((s) => s.position).sort((a, b) => a - b)
    for (let i = 0; i < positions.length; i++) {
      if (positions[i] !== i + 1) {
        return c.json({
          error: 'Validation failed',
          issues: [{ message: 'Pipeline stage positions must be contiguous starting from 1' }],
        }, 422)
      }
    }
    // Validate unique ids
    const ids = stages.map((s) => s.id)
    if (new Set(ids).size !== ids.length) {
      return c.json({
        error: 'Validation failed',
        issues: [{ message: 'Pipeline stage ids must be unique' }],
      }, 422)
    }
  }

  // Validate lost_reasons de-duplicate
  let lostReasons = data.lead_lost_reasons
  if (lostReasons !== undefined) {
    lostReasons = [...new Set(lostReasons.map((r) => r.trim()).filter((r) => r.length > 0))]
    if (lostReasons.length === 0) {
      return c.json({
        error: 'Validation failed',
        issues: [{ message: 'At least one lost reason is required' }],
      }, 422)
    }
  }

  const db = createDb(c.env)
  const updated = await updateCrmSettings(db, session.tid, {
    lead_scoring_enabled: data.lead_scoring_enabled,
    lead_scoring_criteria: data.lead_scoring_criteria,
    pipeline_stages: data.pipeline_stages,
    lead_lost_reasons: lostReasons,
  })

  return c.json(updated, 200)
})

// GET /api/settings/crm/lost-reasons  [wave-14 compat]
crmSettingsRoute.get('/lost-reasons', requirePermission('marketing:read'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const db = c.get('db')
  const reasons = await getLostReasons(db, session.tid)
  return c.json({ reasons }, 200)
})

// PUT /api/settings/crm/lost-reasons  [wave-14 compat]
crmSettingsRoute.put('/lost-reasons', requirePermission('marketing:write'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

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

  const db = c.get('db')
  const reasons = await updateLostReasons(db, session.tid, parsedReasons.data.reasons)
  return c.json({ reasons }, 200)
})
