import type { TicketStatus } from '@/types/db'

/**
 * Moderation domain rules (moderator dashboard doc, 2026-09-07).
 * Feedback is a REVIEW TRIGGER, never truth — nothing here auto-updates any
 * knowledge. Moderators may add context but never erase what happened (§20):
 * no deleting questions/answers/audit history, no changing permit documents
 * or state provisions without approval, no billing/account overrides, no
 * publishing corrections without approval. The finer roles (Moderator /
 * State Expert / Knowledge Manager) are backend-later; admin covers all for
 * the MVP.
 */

export const TICKET_STATUS_LABELS: Record<TicketStatus, string> = {
  new: 'New',
  in_review: 'In Review',
  needs_more_context: 'Needs More Context',
  escalated: 'Escalated',
  correction_proposed: 'Correction Proposed',
  approved_for_knowledge_update: 'Approved for Knowledge Update',
  knowledge_updated: 'Knowledge Updated',
  no_change_needed: 'No Change Needed',
  closed: 'Closed',
  duplicate: 'Duplicate',
}

/** Thumbs-down follow-up options (§28) — one quick pick, never a long form. */
export const FEEDBACK_REASONS = [
  'Incorrect answer',
  'Missing information',
  'Confusing explanation',
  'Wrong state',
  'Wrong permit',
  'Wrong route',
  'Wrong curfew information',
  'Wrong escort information',
  'Other',
] as const

/** Ticket categories (§13). */
export const TICKET_CATEGORIES = [
  'Curfew',
  'Escort',
  'Night Travel',
  'Weekend Travel',
  'Daylight Travel',
  'Permit Validity',
  'Permit Dimensions',
  'Route Restriction',
  'Route Deviation',
  'Pilot Car',
  'Police Escort',
  'High Pole',
  'Bridge/Height',
  'Overweight',
  'State Provision Interpretation',
  'Permit Text Interpretation',
  'Internal Notes Conflict',
  'Missing Source',
  'Wrong State',
  'OCR/Extraction Error',
  'Translation Error',
  'Voice Transcription Error',
  'General Answer Quality',
  'User Confusion',
  'Other',
] as const

/** Moderator decision buttons (§11-12). "Create Knowledge Update" and
 *  "Create Developer Issue" only RECORD the decision in the MVP — the §24
 *  developer-issue payload and knowledge publishing are parked. */
export const TICKET_DECISIONS = [
  'Correct Answer',
  'Incorrect Answer',
  'Partially Correct',
  'Incomplete Answer',
  'Unclear Answer',
  'Wrong Source Used',
  'Needs Expert Review',
  'Create Knowledge Update',
  'Create Developer Issue',
  'Duplicate',
  'No Action Needed',
] as const

export const CONFIDENCE_REVIEWS: Array<[string, string]> = [
  ['appropriate', 'Confidence was appropriate'],
  ['too_high', 'Confidence was too high'],
  ['too_low', 'Confidence was too low'],
  ['should_have_escalated', 'Confidence should have triggered escalation'],
  ['missing', 'Confidence missing'],
]

/** Source-alignment options (§14) — set manually in the MVP. */
export const SOURCE_ALIGNMENTS: Array<[string, string]> = [
  ['permit_matches_provision', 'Permit matches provision'],
  ['permit_conflicts_provision', 'Permit conflicts with provision'],
  ['notes_add_exception', 'State notes add exception'],
  ['internal_qa_used', 'Internal Q&A used'],
  ['no_permit_available', 'No permit available'],
  ['low_source_confidence', 'Low source confidence'],
]

/** Preferred source hierarchy for client-facing permit answers (§14). */
export const SOURCE_HIERARCHY =
  '1. Uploaded permit → 2. State provisions → 3. Internal state notes → 4. Internal Q&A (used carefully for driver-facing regulatory answers)'

/**
 * Auto-classification at ticket creation (§10 priority + §13 category).
 * Simple keyword mapping for the MVP — TODO(backend): real classifier.
 */
const HIGH_RULES: Array<[RegExp, string]> = [
  [/curfew/i, 'Curfew'],
  [/night/i, 'Night Travel'],
  [/weekend/i, 'Weekend Travel'],
  [/police escort/i, 'Police Escort'],
  [/escort|pilot car|chase car/i, 'Escort'],
  [/high pole/i, 'High Pole'],
  [/valid|expire/i, 'Permit Validity'],
  [/dimension|width|height|length|weight/i, 'Permit Dimensions'],
  [/deviat/i, 'Route Deviation'],
  [/bridge|clearance/i, 'Bridge/Height'],
  [/overweight/i, 'Overweight'],
  [/prohibit|not allowed|forbidden/i, 'Route Restriction'],
]
const MEDIUM_RULES: Array<[RegExp, string]> = [
  [/provision/i, 'State Provision Interpretation'],
  [/permit/i, 'Permit Text Interpretation'],
  [/mileage|miles/i, 'Route Restriction'],
  [/route/i, 'Route Restriction'],
  [/restrict/i, 'Route Restriction'],
]

export function classifyTicket(text: string): {
  category: string
  priority: 'high' | 'medium' | 'low'
} {
  for (const [re, category] of HIGH_RULES) {
    if (re.test(text)) return { category, priority: 'high' }
  }
  for (const [re, category] of MEDIUM_RULES) {
    if (re.test(text)) return { category, priority: 'medium' }
  }
  return { category: 'General Answer Quality', priority: 'low' }
}

/** Statuses that end a ticket's life — they stamp resolved_at (§21). */
export const TERMINAL_STATUSES: TicketStatus[] = [
  'closed',
  'no_change_needed',
  'knowledge_updated',
  'duplicate',
]

export const TRIGGER_LABELS: Record<string, string> = {
  thumbs_down: 'Thumbs down',
  reported: 'Reported / human review requested',
  low_confidence_repeat: 'Auto: low confidence, question repeated',
  no_source: 'Auto: no strong source cited',
}

/** Knowledge-update types (§17) — each routes to a different team. */
export const UPDATE_TYPES: Array<[string, string, string]> = [
  ['state_note', 'State Note Update', 'Internal state notes need improving'],
  ['qa_entry', 'State Q&A Entry', 'The same question appears often'],
  ['prompt_logic', 'Prompt Logic Update', 'The AI needs better instructions'],
  ['retrieval', 'Retrieval Issue', 'The right provision/permit section was not retrieved'],
  ['ocr_parsing', 'OCR / Parsing Improvement', 'Permit text was extracted incorrectly'],
  ['product_ui', 'Product / UI Issue', 'The user misunderstood the interface, not the answer'],
]

/** Visibility classes for internal Q&A entries (§18). */
export const QA_VISIBILITY: Array<[string, string]> = [
  ['internal_only', 'Internal Only'],
  ['broker_facing', 'Broker-Facing Allowed'],
  ['carrier_facing', 'Carrier-Facing Allowed'],
  ['driver_facing', 'Driver-Facing Allowed'],
  ['processing_team_only', 'Processing Team Only'],
  ['training_only', 'Training Only'],
  ['do_not_use', 'Do Not Use for AI Answering'],
]

/** Developer-issue categories (§24). */
export const DEVELOPER_ISSUE_CATEGORIES = [
  'AI used wrong state provision',
  'Permit OCR failed',
  'Permit was unreadable',
  'State was detected incorrectly',
  'Route information was missing',
  'Confidence score did not match source alignment',
  'AI answered from internal notes but ignored permit',
  'Voice transcription changed the meaning',
  'Translation caused incorrect interpretation',
  'User interface confused the user',
  'Chat context bug',
  'AI hallucination',
] as const

/** Default settings (§3 "configure feedback categories", §22 reviewer per state). */
export const DEFAULT_MODERATION_SETTINGS = {
  categories: [...TICKET_CATEGORIES] as string[],
  state_reviewers: {} as Record<string, string>,
}

const STOP = new Set(['the', 'a', 'an', 'in', 'on', 'at', 'to', 'of', 'is', 'it', 'i', 'my', 'do', 'can', 'for', 'with', 'this', 'that', 'and', 'or', 'are', 'be'])

function tokens(text: string): Set<string> {
  return new Set(
    text
      .toLowerCase()
      .replace(/[^a-z0-9\s]/g, ' ')
      .split(/\s+/)
      .filter((w) => w.length > 2 && !STOP.has(w)),
  )
}

/**
 * Jaccard similarity of two questions' meaningful words — the "user continues
 * asking the same question" signal (§5). 0..1.
 */
export function questionSimilarity(a: string, b: string): number {
  const ta = tokens(a)
  const tb = tokens(b)
  if (ta.size === 0 || tb.size === 0) return 0
  let inter = 0
  for (const w of ta) if (tb.has(w)) inter++
  return inter / (ta.size + tb.size - inter)
}

/** Same question asked again: similarity above this is a repeat. */
export const REPEAT_SIMILARITY = 0.6

/**
 * Repeated-issue detection (§23): open tickets that share a state + category
 * form one group ("Five users thumbs down answers about Illinois curfews").
 */
export function groupRepeatedIssues<T extends { id: string; state_code: string | null; category: string | null; status: string; duplicate_of: string | null }>(
  tickets: T[],
): Array<{ key: string; state_code: string | null; category: string | null; tickets: T[] }> {
  const groups = new Map<string, T[]>()
  for (const t of tickets) {
    if (TERMINAL_STATUSES.includes(t.status as TicketStatus) || t.duplicate_of) continue
    if (!t.category) continue
    const key = `${t.state_code ?? '—'}|${t.category}`
    groups.set(key, [...(groups.get(key) ?? []), t])
  }
  return [...groups.entries()]
    .filter(([, list]) => list.length >= 2)
    .map(([key, list]) => ({
      key,
      state_code: list[0].state_code,
      category: list[0].category,
      tickets: list,
    }))
    .sort((a, b) => b.tickets.length - a.tickets.length)
}

/** Average hours from creation to resolution over resolved tickets (§21). */
export function averageResolutionHours(
  tickets: Array<{ created_at: string; resolved_at: string | null }>,
): number | null {
  const done = tickets.filter((t) => t.resolved_at)
  if (done.length === 0) return null
  const total = done.reduce(
    (sum, t) => sum + (new Date(t.resolved_at!).getTime() - new Date(t.created_at).getTime()),
    0,
  )
  return total / done.length / 3_600_000
}

/** Decisions that mean the AI was right (for accuracy metrics, §21). */
export const CORRECT_DECISIONS = new Set(['Correct Answer', 'No Action Needed'])
/** Decisions that mean the AI was wrong or short. */
export const WRONG_DECISIONS = new Set([
  'Incorrect Answer',
  'Partially Correct',
  'Incomplete Answer',
  'Wrong Source Used',
])
