import { and, eq } from 'drizzle-orm'
import type { Db } from '../client'
import { shellLayouts } from '../schema/shell-layouts'
import { userPreferences } from '../schema/user-preferences'
import { tenantSettings } from '../schema/tenants'
import { auditLog } from './_audit-forward'

export type UiShell = 'classic' | 'os'
export type ShellDeviceClass = 'desktop' | 'mobile'

export interface ShellLayoutRecord {
  payload: unknown
  version: number
  updated_at: string
}

export interface ShellPreferences {
  ui_shell: UiShell
  force_shell: UiShell | null
}

export async function getShellLayout(db: Db, userId: string, tenantId: string, deviceClass: ShellDeviceClass): Promise<ShellLayoutRecord | null> {
  const [row] = await db.select({ payload: shellLayouts.payload, version: shellLayouts.version, updatedAt: shellLayouts.updatedAt })
    .from(shellLayouts)
    .where(and(eq(shellLayouts.userId, userId), eq(shellLayouts.tenantId, tenantId), eq(shellLayouts.deviceClass, deviceClass)))
    .limit(1)
  return row ? { payload: row.payload, version: row.version, updated_at: row.updatedAt.toISOString() } : null
}

export async function getShellPreferences(db: Db, userId: string, tenantId: string): Promise<ShellPreferences> {
  const [user] = await db.select({ uiShell: userPreferences.uiShell }).from(userPreferences)
    .where(and(eq(userPreferences.userId, userId), eq(userPreferences.tenantId, tenantId))).limit(1)
  const [tenant] = await db.select({ forceShell: tenantSettings.forceShell }).from(tenantSettings)
    .where(eq(tenantSettings.tenantId, tenantId)).limit(1)
  return {
    ui_shell: user?.uiShell === 'classic' ? 'classic' : 'os',
    force_shell: tenant?.forceShell === 'classic' || tenant?.forceShell === 'os' ? tenant.forceShell : null,
  }
}

export async function setShellLayout(db: Db, userId: string, tenantId: string, deviceClass: ShellDeviceClass, payload: unknown, expectedVersion: number | null): Promise<ShellLayoutRecord | null> {
  return db.transaction(async (tx) => {
    const expected = expectedVersion ?? 0
    const now = new Date()
    const [saved] = expected === 0
      ? await tx.insert(shellLayouts).values({ tenantId, userId, deviceClass, payload, version: 1, updatedAt: now })
        .onConflictDoNothing({ target: [shellLayouts.tenantId, shellLayouts.userId, shellLayouts.deviceClass] })
        .returning({ payload: shellLayouts.payload, version: shellLayouts.version, updatedAt: shellLayouts.updatedAt })
      : await tx.update(shellLayouts)
        .set({ payload, version: expected + 1, updatedAt: now })
        .where(and(
          eq(shellLayouts.userId, userId), eq(shellLayouts.tenantId, tenantId), eq(shellLayouts.deviceClass, deviceClass),
          eq(shellLayouts.version, expected),
        ))
        .returning({ payload: shellLayouts.payload, version: shellLayouts.version, updatedAt: shellLayouts.updatedAt })
    if (!saved) return null
    await tx.insert(auditLog).values({ tenantId, actorId: userId, actorType: 'user', entityType: 'shell_layout', entityId: userId, action: 'shell_layout.updated' })
    return { payload: saved.payload, version: saved.version, updated_at: saved.updatedAt.toISOString() }
  })
}

export async function deleteShellLayout(db: Db, userId: string, tenantId: string, deviceClass: ShellDeviceClass): Promise<boolean> {
  return db.transaction(async (tx) => {
    const [deleted] = await tx.delete(shellLayouts)
      .where(and(eq(shellLayouts.userId, userId), eq(shellLayouts.tenantId, tenantId), eq(shellLayouts.deviceClass, deviceClass)))
      .returning({ userId: shellLayouts.userId })
    if (!deleted) return false
    await tx.insert(auditLog).values({ tenantId, actorId: userId, actorType: 'user', entityType: 'shell_layout', entityId: userId, action: 'shell_layout.deleted' })
    return true
  })
}
