/**
 * Portal settings query helpers — customer-portal-settings-ui (spec 136, wave 11).
 *
 * Reads and writes portal branding/access/permission columns from tenant_settings.
 * Also provides portal-user helpers for the Active portal users list.
 *
 * Routes MUST NOT import raw Drizzle tables — they call these helpers.
 */
import { eq, and } from 'drizzle-orm'
import type { Db } from '../client'
import { tenantSettings } from '../schema/tenants'
import { customerPortalUsers, customerContacts, customers } from '../schema/customers'
import { auditLog } from './_audit-forward'
import type { PortalVisibility } from '@zync/types'

// ── Types ─────────────────────────────────────────────────────────────────────

export interface PortalSettingsDTO {
  portal_enabled: boolean
  portal_access_method: 'magic_link' | 'password'
  portal_name: string | null
  portal_welcome_text: string | null
  portal_primary_color_hex: string | null
  portal_logo_url: string | null
  portal_can_submit_tickets: boolean
  portal_can_upload_files: boolean
  portal_show_team_members: boolean
  portal_visibility: PortalVisibility
}

export const DEFAULT_PORTAL_VISIBILITY: PortalVisibility = {
  show_invoices: true,
  show_proposals: true,
  show_projects: true,
  show_tickets: false,
  show_contracts: false,
  show_files: false,
  show_time_summary: false,
}

export function resolvePortalVisibility(
  stored: Partial<PortalVisibility> | Record<string, boolean> | null | undefined,
): PortalVisibility {
  return {
    ...DEFAULT_PORTAL_VISIBILITY,
    ...(stored ?? {}),
  }
}

export interface UpdatePortalSettingsBody {
  portal_enabled?: boolean
  portal_access_method?: 'magic_link' | 'password'
  portal_name?: string | null
  portal_welcome_text?: string | null
  portal_primary_color_hex?: string | null
  portal_logo_url?: string | null
  portal_can_submit_tickets?: boolean
  portal_can_upload_files?: boolean
  portal_show_team_members?: boolean
  portal_visibility?: Record<string, boolean>
}

export interface PortalUserRow {
  id: string
  contactId: string
  customerId: string
  customerName: string
  customerCompany: string | null
  contactName: string
  contactEmail: string | null
  portalRole: string
  status: string
  lastLoginAt: string | null
  invitedAt: string | null
  acceptedAt: string | null
}

// ── getPortalSettings ─────────────────────────────────────────────────────────

export async function getPortalSettings(
  db: Db,
  tenantId: string,
): Promise<PortalSettingsDTO> {
  const [row] = await db
    .select({
      portalEnabled: tenantSettings.portalEnabled,
      portalAccessMethod: tenantSettings.portalAccessMethod,
      portalName: tenantSettings.portalName,
      portalWelcomeText: tenantSettings.portalWelcomeText,
      portalPrimaryColorHex: tenantSettings.portalPrimaryColorHex,
      portalLogoUrl: tenantSettings.portalLogoUrl,
      portalCanSubmitTickets: tenantSettings.portalCanSubmitTickets,
      portalCanUploadFiles: tenantSettings.portalCanUploadFiles,
      portalShowTeamMembers: tenantSettings.portalShowTeamMembers,
      portalVisibility: tenantSettings.portalVisibility,
    })
    .from(tenantSettings)
    .where(eq(tenantSettings.tenantId, tenantId))
    .limit(1)

  // Upsert a row if the tenant doesn't have one yet (backfill guard)
  if (!row) {
    await db
      .insert(tenantSettings)
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      .values({ tenantId } as any)
      .onConflictDoNothing()
    return getPortalSettings(db, tenantId)
  }

  return {
    portal_enabled: row.portalEnabled,
    portal_access_method: row.portalAccessMethod as 'magic_link' | 'password',
    portal_name: row.portalName,
    portal_welcome_text: row.portalWelcomeText,
    portal_primary_color_hex: row.portalPrimaryColorHex,
    portal_logo_url: row.portalLogoUrl,
    portal_can_submit_tickets: row.portalCanSubmitTickets,
    portal_can_upload_files: row.portalCanUploadFiles,
    portal_show_team_members: row.portalShowTeamMembers,
    portal_visibility: resolvePortalVisibility(
      (row.portalVisibility as Record<string, boolean>) ?? null,
    ),
  }
}

// ── upsertPortalSettings ──────────────────────────────────────────────────────

export async function upsertPortalSettings(
  db: Db,
  tenantId: string,
  actorId: string,
  patch: UpdatePortalSettingsBody,
): Promise<PortalSettingsDTO> {
  const changedKeys = Object.keys(patch)

  await db.transaction(async (tx) => {
    await tx
      .update(tenantSettings)
      .set({
        ...(patch.portal_enabled !== undefined && { portalEnabled: patch.portal_enabled }),
        ...(patch.portal_access_method !== undefined && { portalAccessMethod: patch.portal_access_method }),
        ...(patch.portal_name !== undefined && { portalName: patch.portal_name }),
        ...(patch.portal_welcome_text !== undefined && { portalWelcomeText: patch.portal_welcome_text }),
        ...(patch.portal_primary_color_hex !== undefined && { portalPrimaryColorHex: patch.portal_primary_color_hex }),
        ...(patch.portal_logo_url !== undefined && { portalLogoUrl: patch.portal_logo_url }),
        ...(patch.portal_can_submit_tickets !== undefined && { portalCanSubmitTickets: patch.portal_can_submit_tickets }),
        ...(patch.portal_can_upload_files !== undefined && { portalCanUploadFiles: patch.portal_can_upload_files }),
        ...(patch.portal_show_team_members !== undefined && { portalShowTeamMembers: patch.portal_show_team_members }),
        ...(patch.portal_visibility !== undefined && { portalVisibility: patch.portal_visibility }),
        updatedAt: new Date(),
      })
      .where(eq(tenantSettings.tenantId, tenantId))

    await tx.insert(auditLog).values({
      tenantId,
      actorId,
      actorType: 'user',
      entityType: 'portal_settings',
      entityId: tenantId,
      action: 'settings.portal.updated',
      changes: Object.fromEntries(changedKeys.map((k) => [k, [null, patch[k as keyof UpdatePortalSettingsBody]]])) as Record<string, [unknown, unknown]>,
    })
  })

  return getPortalSettings(db, tenantId)
}

// ── listTenantPortalUsers ─────────────────────────────────────────────────────

export async function listTenantPortalUsers(
  db: Db,
  tenantId: string,
): Promise<PortalUserRow[]> {
  const rows = await db
    .select({
      id: customerPortalUsers.id,
      contactId: customerPortalUsers.contactId,
      customerId: customerPortalUsers.customerId,
      customerName: customers.name,
      customerCompany: customers.company,
      contactName: customerContacts.name,
      contactEmail: customerContacts.email,
      portalRole: customerPortalUsers.portalRole,
      status: customerPortalUsers.status,
      lastLoginAt: customerPortalUsers.lastLoginAt,
      invitedAt: customerPortalUsers.invitedAt,
      acceptedAt: customerPortalUsers.acceptedAt,
    })
    .from(customerPortalUsers)
    .innerJoin(customers, eq(customers.id, customerPortalUsers.customerId))
    .innerJoin(customerContacts, eq(customerContacts.id, customerPortalUsers.contactId))
    .where(eq(customerPortalUsers.tenantId, tenantId))
    .orderBy(customers.name)

  return rows.map((row) => ({
    id: row.id,
    contactId: row.contactId,
    customerId: row.customerId,
    customerName: row.customerName,
    customerCompany: row.customerCompany,
    contactName: row.contactName,
    contactEmail: row.contactEmail,
    portalRole: row.portalRole,
    status: row.status,
    lastLoginAt: row.lastLoginAt?.toISOString() ?? null,
    invitedAt: row.invitedAt?.toISOString() ?? null,
    acceptedAt: row.acceptedAt?.toISOString() ?? null,
  }))
}

// ── revokePortalUser ──────────────────────────────────────────────────────────

export async function revokePortalUser(
  db: Db,
  tenantId: string,
  contactId: string,
): Promise<void> {
  await db
    .update(customerPortalUsers)
    .set({ status: 'frozen' })
    .where(
      and(
        eq(customerPortalUsers.tenantId, tenantId),
        eq(customerPortalUsers.contactId, contactId),
      ),
    )
}

// ── touchPortalUserLogin ──────────────────────────────────────────────────────

export async function touchPortalUserLogin(
  db: Db,
  tenantId: string,
  portalUserId: string,
): Promise<void> {
  await db
    .update(customerPortalUsers)
    .set({ lastLoginAt: new Date() })
    .where(
      and(
        eq(customerPortalUsers.tenantId, tenantId),
        eq(customerPortalUsers.id, portalUserId),
      ),
    )
}
