import { NextRequest, NextResponse } from 'next/server'
import { imChannels } from '@/lib/database/schema'
import { imChannelRepo } from '@/lib/database/repositories/im-channel.repo'
import { channelBindingRepo } from '@/lib/database/repositories/channel-binding.repo'
import { isAuthError, requireUser, requireWorkspaceWriteAccess } from '@/lib/auth/session'
import { deleteMemberCompletely } from '@/lib/invite/lifecycle'

type ChannelInsert = typeof imChannels.$inferInsert
type ChannelStatus = ChannelInsert['status']

/** Normalize a raw JSON value the way the original dynamic SQL did:
 *  objects → JSON string, booleans → 1/0, everything else stringified. */
function normalizeText(raw: unknown): string {
  if (typeof raw === 'object' && raw !== null) return JSON.stringify(raw)
  if (typeof raw === 'boolean') return raw ? '1' : '0'
  return String(raw)
}

export async function GET(_req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
  let user
  try { user = await requireUser() } catch (err) { if (isAuthError(err)) return err; throw err }
  const { id } = await params
  const row = imChannelRepo.findByIdAndOwner(id, user.id)
  if (!row) return NextResponse.json({ error: 'Not found' }, { status: 404 })
  return NextResponse.json(row)
}

export async function PATCH(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
  let user
  try { user = await requireUser() } catch (err) { if (isAuthError(err)) return err; throw err }
  const { id } = await params
  const body = await req.json()
  const existing = imChannelRepo.findDefaultWorkspaceIdAndOwner(id, user.id)
  if (!existing) return NextResponse.json({ error: 'Not found' }, { status: 404 })

  if (typeof body.default_workspace_id === 'string' && body.default_workspace_id) {
    try {
      await requireWorkspaceWriteAccess(body.default_workspace_id)
    } catch (err) {
      if (isAuthError(err)) return err
      throw err
    }
  }

  const fields: Partial<ChannelInsert> = {}

  // Mirror the original allow-list. Each column is typed; values are normalized
  // identically to the legacy dynamic SQL (object→JSON, boolean→0/1, else as-is).
  if (body.name !== undefined) fields.name = normalizeText(body.name)
  if (body.enabled !== undefined) {
    fields.enabled = typeof body.enabled === 'boolean' ? (body.enabled ? 1 : 0) : Number(body.enabled)
  }
  if (body.status !== undefined) fields.status = normalizeText(body.status) as ChannelStatus
  if (body.credentials !== undefined) fields.credentials = normalizeText(body.credentials)
  if (body.default_workspace_id !== undefined) fields.defaultWorkspaceId = normalizeText(body.default_workspace_id)
  if (body.default_model !== undefined) fields.defaultModel = normalizeText(body.default_model)
  if (body.dm_policy !== undefined) fields.dmPolicy = normalizeText(body.dm_policy)
  if (body.group_policy !== undefined) fields.groupPolicy = normalizeText(body.group_policy)
  if (body.trigger_mode !== undefined) fields.triggerMode = normalizeText(body.trigger_mode)
  if (body.group_whitelist !== undefined) fields.groupWhitelist = normalizeText(body.group_whitelist)
  if (body.sender_whitelist !== undefined) fields.senderWhitelist = normalizeText(body.sender_whitelist)

  if (Object.keys(fields).length === 0) {
    return NextResponse.json({ error: 'No fields to update' }, { status: 400 })
  }

  const updated = imChannelRepo.updateFieldsAndOwner(id, user.id, fields)
  if (
    typeof body.default_workspace_id === 'string'
    && body.default_workspace_id
    && body.default_workspace_id !== existing.defaultWorkspaceId
  ) {
    // Reset all bindings of this channel to the new workspace and force session
    // rebuild on next message (session_id set to NULL).
    channelBindingRepo.resetWorkspaceByChannel(id, body.default_workspace_id, body.default_workspace_id)
  }
  return NextResponse.json(updated)
}

export async function DELETE(_req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
  let user
  try { user = await requireUser() } catch (err) { if (isAuthError(err)) return err; throw err }
  const { id } = await params

  // Hard-delete the channel + (if provisioned via invitation) the invitation
  // row, the workspace row, and the invitee's user directory on disk. The
  // shared DB CASCADEs (channel_bindings, sessions, etc.) handle the rest.
  const result = await deleteMemberCompletely(id, user.id)
  if (!result.deleted) {
    if (result.reason === 'not_found') {
      return NextResponse.json({ error: 'Not found' }, { status: 404 })
    }
    return NextResponse.json({ error: 'Not authorized' }, { status: 403 })
  }

  return NextResponse.json({
    ok: true,
    userDir: result.userDir,
    dirRemoved: result.dirRemoved,
    dirError: result.dirError,
  })
}
