// DB-backed persistence (Wertstrom P6, Baustein 3/4b/5). No SECURITY DEFINER
// RPC (unlike QVS's `create_value_stream_from_qaf`) — that would need a
// migration, which is out of scope (brief: "ohne DDL lösen"). Instead: N
// SEQUENTIAL plain inserts against the RLS-scoped client, same pattern
// lib/qaf-value-stream/internal/creation.ts's `createValueStreamsForVariants`
// already uses for "je Variante ein Wertstrom" (each insert its own
// transaction; a mid-sequence failure is reported per stream, never hidden;
// see README.md "Bekannte Grenze: keine RPC-Transaktion" for the accepted
// trade-off this implies).
//
// Idempotency (§14.2/12–13, no partial-unique-index available without DDL):
// a `confirmationSignature` (signature.ts) is written onto EVERY row a
// single confirm call creates, under `layout.simvsmSource.importSignature`.
// Re-confirming the exact same (file content, stream selection, project)
// looks this up FIRST and returns the original result with zero writes. This
// is an application-level, best-effort guard — see README.md for the
// documented TOCTOU race window between the lookup and the inserts below,
// which a real unique index would close and this cannot.

import type { SupabaseClient } from '@supabase/supabase-js'
import { logger } from '@/lib/logger'
import { VsmConnectionSchema, VsmNodeSchema } from '@/lib/api/schemas'
import type { ScenarioKind } from '@/lib/vsm-types'
import type { MappedStream } from './streams'
import { computeConfirmationSignature } from './signature'
import { findSimvsmImportByConfirmationSignature } from './duplicates'
import { PARSER_VERSION } from './parser'
import { MAPPING_VERSION } from './mapping-registry'

export interface SimvsmSourceMetadata {
  sourceSignature: string
  importSignature: string
  fileName: string
  modelName: string
  createdWithVersion: string | null
  mainVersion: string | null
  parserVersion: string
  mappingVersion: string
  importedAt: string
  streamIndex: number
  streamName: string
}

export interface PersistSimvsmSelection {
  streamIndices: number[]
  projectId: string | null
  titleOverride?: string
}

export interface PersistSimvsmInput {
  sourceSignature: string
  fileName: string
  modelName: string
  createdWithVersion: string | null
  mainVersion: string | null
  /** ALL mapped streams for the file (not just the selected ones) — needed
   * to find the family's designated "current" stream even when the caller
   * only selected alternatives. See streams.ts / README.md for the
   * current-resolution + standalone-fallback rules. */
  allStreams: readonly MappedStream[]
  selection: PersistSimvsmSelection
  createdBy: string
}

export interface PersistedStream {
  streamIndex: number
  streamName: string
  /** The actual DB row title (buildTitle output — e.g. "Werk Nord
   * Testmodell · Ist-Zustand"), NOT the raw SimVSM stream name. Added for
   * §14.4's "imported value streams (count+title)" result field (Review-Fix
   * C8, adversarial review PR #360) — this is what the user will actually
   * see in their Wertstrom list, `streamName` alone wasn't that. */
  title?: string
  ok: boolean
  valueStreamId?: string
  scenarioKind?: ScenarioKind
  error?: string
}

export type PersistSimvsmOutcome =
  | { ok: true; idempotentHit: boolean; currentValueStreamId: string | null; createdStreams: PersistedStream[] }
  | { ok: false; error: string }

function buildTitle(baseTitle: string, streamName: string, totalStreamsInFile: number): string {
  if (totalStreamsInFile <= 1) return baseTitle
  return `${baseTitle} · ${streamName}`
}

export async function persistSimvsmImport(supabase: SupabaseClient, input: PersistSimvsmInput): Promise<PersistSimvsmOutcome> {
  const { streamIndices, projectId, titleOverride } = input.selection
  if (streamIndices.length === 0) return { ok: false, error: 'no_stream_selected' }

  const byIndex = new Map(input.allStreams.map((s) => [s.index, s]))
  for (const idx of streamIndices) {
    if (!byIndex.has(idx)) return { ok: false, error: 'invalid_stream_index' }
  }

  const confirmationSignature = computeConfirmationSignature(input.sourceSignature, streamIndices, projectId)

  const existing = await findSimvsmImportByConfirmationSignature(supabase, confirmationSignature)
  if (existing.ok && existing.rows.length > 0) {
    const currentRow = existing.rows.find((r) => r.scenario_kind === 'current' && r.parent_value_stream_id === null)
    return {
      ok: true,
      idempotentHit: true,
      currentValueStreamId: currentRow?.id ?? existing.rows[0].id,
      createdStreams: existing.rows.map((r) => ({
        streamIndex: -1,
        streamName: r.title,
        title: r.title,
        ok: true,
        valueStreamId: r.id,
        scenarioKind: r.scenario_kind as ScenarioKind,
      })),
    }
  }

  const baseTitle = titleOverride?.trim() || input.modelName
  const designatedCurrent = input.allStreams.find((s) => s.scenarioRole === 'current')
  const currentSelected = designatedCurrent !== undefined && streamIndices.includes(designatedCurrent.index)

  // Designated-current first (when selected) so every alternative insert
  // below has a real parent id to point at. See streams.ts module header for
  // why an alternative selected WITHOUT its file's current stream becomes
  // its own standalone 'current' row instead (never an invented parent).
  const orderedIndices = currentSelected
    ? [designatedCurrent!.index, ...streamIndices.filter((i) => i !== designatedCurrent!.index)]
    : [...streamIndices]

  const createdStreams: PersistedStream[] = []
  let currentValueStreamId: string | null = null
  const importedAt = new Date().toISOString()

  for (const idx of orderedIndices) {
    const stream = byIndex.get(idx)!
    const isDesignatedCurrent = designatedCurrent !== undefined && idx === designatedCurrent.index
    const scenarioKind: ScenarioKind = isDesignatedCurrent || !currentSelected ? 'current' : 'alternative'
    const parentValueStreamId: string | null = scenarioKind === 'alternative' ? currentValueStreamId : null
    // Only the very FIRST processed stream (the designated-current when
    // selected, otherwise the first standalone selection) is an "anchor" —
    // every later 'alternative' insert needs currentValueStreamId to be
    // real, so an anchor failure must abort the whole confirm rather than
    // insert alternatives with a null/wrong parent. A later, non-anchor
    // failure is reported per-stream and does not roll back the rows
    // already created (same posture as createValueStreamsForVariants).
    const isAnchor = idx === orderedIndices[0]

    const nodesCheck = VsmNodeSchema.array().safeParse(stream.nodes)
    const connectionsCheck = VsmConnectionSchema.array().safeParse(stream.connections)
    if (!nodesCheck.success || !connectionsCheck.success) {
      logger.warn('simvsm_import.mapper_output_invalid', { streamIndex: idx })
      createdStreams.push({ streamIndex: idx, streamName: stream.name, ok: false, error: 'mapper_output_invalid' })
      if (isAnchor) return { ok: false, error: 'mapper_output_invalid' }
      continue
    }

    const sourceMetadata: SimvsmSourceMetadata = {
      sourceSignature: input.sourceSignature,
      importSignature: confirmationSignature,
      fileName: input.fileName,
      modelName: input.modelName,
      createdWithVersion: input.createdWithVersion,
      mainVersion: input.mainVersion,
      parserVersion: PARSER_VERSION,
      mappingVersion: MAPPING_VERSION,
      importedAt,
      streamIndex: stream.index,
      streamName: stream.name,
    }

    const rowTitle = buildTitle(baseTitle, stream.name, input.allStreams.length)

    const insertResponse = await supabase
      .from('value_stream_maps')
      .insert({
        title: rowTitle,
        description: null,
        project_id: projectId,
        nodes: stream.nodes,
        connections: stream.connections,
        layout: { simvsmSource: sourceMetadata },
        is_demo: false,
        created_by: input.createdBy,
        scenario_kind: scenarioKind,
        parent_value_stream_id: parentValueStreamId,
      })
      .select('id')
      .single()
    const insertError: { message: string } | null = insertResponse.error
    const insertedRow: { id: string } | null = insertResponse.data

    if (insertError || !insertedRow) {
      logger.warn('simvsm_import.insert_failed', { streamIndex: idx, message: insertError?.message })
      createdStreams.push({ streamIndex: idx, streamName: stream.name, title: rowTitle, ok: false, error: 'db_error' })
      if (isAnchor) return { ok: false, error: 'db_error' }
      continue
    }

    const valueStreamId: string = insertedRow.id
    createdStreams.push({ streamIndex: idx, streamName: stream.name, title: rowTitle, ok: true, valueStreamId, scenarioKind })
    if (scenarioKind === 'current' && currentValueStreamId === null) currentValueStreamId = valueStreamId
  }

  if (createdStreams.every((c) => !c.ok)) return { ok: false, error: 'db_error' }

  return { ok: true, idempotentHit: false, currentValueStreamId, createdStreams }
}
