import type { InjectionKey, Ref } from 'vue'
import type { KbxScreenPreference } from '@kbx/contracts'

const PREFIX = 'kbx.screen.preference.v2'
const MAX_STORED_BYTES = 32_000
const MAX_SCOPE_LENGTH = 160
const MAX_ID_LENGTH = 128
const MAX_VERSION_LENGTH = 40
const MAX_SEARCH_KEYS = 24
const MAX_STRING_LENGTH = 256

export const KbxScreenPreferenceScopeKey: InjectionKey<Readonly<Ref<string>>> = Symbol('KbxScreenPreferenceScope')

function bounded(value: unknown, max: number) {
  if (typeof value !== 'string') return ''
  const clean = value.trim()
  return clean.length <= max ? clean : ''
}
function normalizeScope(scope: unknown) { return bounded(scope, MAX_SCOPE_LENGTH) }
function normalizeId(value: unknown) { return bounded(value, MAX_ID_LENGTH) }
function normalizeVersion(value: unknown) { return bounded(value, MAX_VERSION_LENGTH) }
function storageKey(scope: string, screenId: string, screenVersion: string) {
  return `${PREFIX}:${encodeURIComponent(scope)}:${encodeURIComponent(screenId)}:${encodeURIComponent(screenVersion)}`
}
function safeScalar(value: unknown): string | number | boolean | null | undefined {
  if (value === null || typeof value === 'boolean') return value
  if (typeof value === 'number' && Number.isFinite(value)) return value
  if (typeof value === 'string') return value.slice(0, MAX_STRING_LENGTH)
  return undefined
}
function sanitizeJsonValue(value: unknown, depth=0): unknown {
  if (depth > 4) return undefined
  const scalar = safeScalar(value)
  if (scalar !== undefined) return scalar
  if (Array.isArray(value)) return value.slice(0, 100).map(item => sanitizeJsonValue(item, depth + 1)).filter(item => item !== undefined)
  if (!value || typeof value !== 'object') return undefined
  const result: Record<string, unknown> = {}
  for (const [key, raw] of Object.entries(value).slice(0, 100)) {
    const safeKey = bounded(key, 64)
    const safeValue = sanitizeJsonValue(raw, depth + 1)
    if (safeKey && safeValue !== undefined) result[safeKey] = safeValue
  }
  return result
}
function sanitizeSearchDefaults(value: unknown) {
  if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined
  const result: Record<string, string | number | boolean | null> = {}
  for (const [key, raw] of Object.entries(value).slice(0, MAX_SEARCH_KEYS)) {
    const safeKey = bounded(key, 64)
    const safeValue = safeScalar(raw)
    if (safeKey && safeValue !== undefined) result[safeKey] = safeValue
  }
  return Object.keys(result).length ? result : undefined
}
function normalizePreference(value: unknown, screenId: string, screenVersion: string): KbxScreenPreference {
  const raw = value && typeof value === 'object' ? value as Partial<KbxScreenPreference> : {}
  const density = ['compact', 'comfortable', 'touch'].includes(String(raw.density)) ? raw.density : undefined
  const pageSize = Number(raw.grid?.pageSize)
  const columnState = sanitizeJsonValue(raw.grid?.columnState)
  const searchDefaults = sanitizeSearchDefaults(raw.searchDefaults)
  const grid = raw.grid && (Number.isFinite(pageSize) || columnState !== undefined) ? {
    ...(columnState !== undefined ? { columnState } : {}),
    ...(Number.isFinite(pageSize) ? { pageSize:Math.min(500, Math.max(10, pageSize)) } : {}),
  } : undefined
  return {
    screenId,
    screenVersion,
    ...(density ? { density } : {}),
    ...(grid ? { grid } : {}),
    ...(searchDefaults ? { searchDefaults } : {}),
    rememberSearch: Boolean(raw.rememberSearch),
  }
}

export function loadScreenPreference(scopeValue: string, screenIdValue: string, screenVersionValue: string): KbxScreenPreference | null {
  const scope = normalizeScope(scopeValue)
  const screenId = normalizeId(screenIdValue)
  const screenVersion = normalizeVersion(screenVersionValue)
  if (!scope || !screenId || !screenVersion || typeof localStorage === 'undefined') return null
  let raw = ''
  try { raw = localStorage.getItem(storageKey(scope, screenId, screenVersion)) ?? '' } catch { return null }
  if (!raw || raw.length > MAX_STORED_BYTES) return null
  try { return normalizePreference(JSON.parse(raw), screenId, screenVersion) } catch { return null }
}

export function saveScreenPreference(scopeValue: string, preference: KbxScreenPreference) {
  const scope = normalizeScope(scopeValue)
  const screenId = normalizeId(preference.screenId)
  const screenVersion = normalizeVersion(preference.screenVersion)
  if (!scope || !screenId || !screenVersion || typeof localStorage === 'undefined') return false
  const normalized = normalizePreference(preference, screenId, screenVersion)
  const payload = JSON.stringify(normalized)
  if (payload.length > MAX_STORED_BYTES) return false
  try { localStorage.setItem(storageKey(scope, screenId, screenVersion), payload); return true } catch { return false }
}

export function clearScreenPreference(scopeValue: string, screenIdValue: string, screenVersionValue: string) {
  const scope = normalizeScope(scopeValue)
  const screenId = normalizeId(screenIdValue)
  const screenVersion = normalizeVersion(screenVersionValue)
  if (!scope || !screenId || !screenVersion || typeof localStorage === 'undefined') return false
  try { localStorage.removeItem(storageKey(scope, screenId, screenVersion)); return true } catch { return false }
}
