// Read-only access to data/clinics.db (built by scripts/import-clinics.mjs).
// Fail-soft by design: if the database is missing the site still renders, the
// autocomplete simply returns nothing and the form falls back to manual approval.
import fs from 'node:fs'
import path from 'node:path'
import Database from 'better-sqlite3'
import { registrableDomain, searchVariants } from '../../lib/domains.mjs'

export type ClinicRow = {
  id: number
  stoid: string
  name: string
  street: string
  plz: string
  city: string
  land: string
  url: string
  email: string
  phone: string
  traeger: string
  beds: number | null
  lat: number | null
  lon: number | null
  domain: string
  domains: string
}

export type Clinic = Omit<ClinicRow, 'domains'> & { domains: string[] }

let handle: any = null
let opened = false
let openError = ''

const resolveDbPath = (): string => {
  const configured = (useRuntimeConfig().clinicDbPath || process.env.NUXT_CLINIC_DB_PATH || '').trim()
  const rel = configured || 'data/clinics.db'
  return path.isAbsolute(rel) ? rel : path.resolve(process.cwd(), rel)
}

/**
 * Lazily opened, read-only handle. Returns null when the file is not (yet) built —
 * and retries on the next call once the file appears, so an import that runs after the
 * server started does not require a restart.
 */
export const getClinicDb = (): any => {
  if (handle) return handle
  const file = resolveDbPath()
  if (opened && !fs.existsSync(file)) return null
  opened = true
  try {
    if (!fs.existsSync(file)) {
      openError = `clinics.db not found at ${file} — run "npm run import:clinics"`
      console.warn('[blut24] ' + openError)
      return null
    }
    handle = new Database(file, { readonly: true, fileMustExist: true })
    handle.pragma('query_only = true')
  } catch (err: any) {
    openError = err?.message || String(err)
    console.error('[blut24] clinics.db konnte nicht geöffnet werden:', openError)
    handle = null
  }
  return handle
}

export const clinicDbStatus = () => ({ available: !!getClinicDb(), error: openError, path: resolveDbPath() })

const toClinic = (row: ClinicRow | undefined): Clinic | null => {
  if (!row) return null
  return { ...row, domains: String(row.domains || '').split(' ').filter(Boolean) }
}

/**
 * Build an FTS5 MATCH expression from free user input.
 * Every token becomes a prefix term, umlaut variants are OR-ed, tokens are AND-ed:
 *   "helios münch" -> ("helios"* ) AND ("münch"* OR "muench"* OR "munch"*)
 */
export const buildMatchQuery = (input: string): string => {
  const raw = String(input ?? '').toLowerCase()
  const tokens = raw.split(/[^\p{L}\p{N}]+/u).filter((t) => t.length >= 2 || /^\d+$/.test(t))
  const groups: string[] = []
  for (const token of tokens.slice(0, 6)) {
    const variants = searchVariants(token).map((v) => v.replace(/"/g, '')).filter(Boolean)
    if (!variants.length) continue
    groups.push('(' + variants.map((v) => `"${v}"*`).join(' OR ') + ')')
  }
  return groups.join(' AND ')
}

/** Full-text autocomplete over name / city / street / PLZ. */
export const searchClinics = (input: string, limit = 8): Clinic[] => {
  const db = getClinicDb()
  if (!db) return []
  const match = buildMatchQuery(input)
  if (!match) return []
  try {
    const rows = db.prepare(`
      SELECT c.* FROM clinics_fts f
      JOIN clinics c ON c.id = f.rowid
      WHERE clinics_fts MATCH ?
      ORDER BY rank, c.name
      LIMIT ?
    `).all(match, Math.max(1, Math.min(25, limit))) as ClinicRow[]
    return rows.map((r) => toClinic(r)!).filter(Boolean)
  } catch (err: any) {
    console.error('[blut24] Klinik-Suche fehlgeschlagen:', err?.message || err)
    return []
  }
}

/** One Standort by its Bundes-Klinik-Atlas id. */
export const clinicByStoid = (stoid: string): Clinic | null => {
  const db = getClinicDb()
  if (!db || !stoid) return null
  const row = db.prepare('SELECT * FROM clinics WHERE stoid = ?').get(String(stoid)) as ClinicRow | undefined
  return toClinic(row)
}

/**
 * All Standorte behind one registrable domain. Hospital groups (helios-gesundheit.de,
 * sana.de, asklepios.com, …) share a single domain across dozens of hospitals, so a
 * domain match must always offer the user the choice of their own Standort.
 */
export const clinicsByDomain = (domain: string, limit = 200): Clinic[] => {
  const db = getClinicDb()
  const key = registrableDomain(domain)
  if (!db || !key) return []
  const rows = db.prepare(`
    SELECT c.* FROM clinic_domains d
    JOIN clinics c ON c.stoid = d.stoid
    WHERE d.domain = ?
    ORDER BY c.name
    LIMIT ?
  `).all(key, limit) as ClinicRow[]
  return rows.map((r) => toClinic(r)!).filter(Boolean)
}

/** Public projection — never leaks the clinic's own contact e-mail address. */
export const publicClinic = (c: Clinic | null) => {
  if (!c) return null
  return {
    stoid: c.stoid,
    name: c.name,
    street: c.street,
    plz: c.plz,
    city: c.city,
    land: c.land,
    beds: c.beds,
    traeger: c.traeger,
    domain: c.domain,
    domains: c.domains,
    hasDomain: c.domains.length > 0,
  }
}
