import { defineTable } from 'convex/server';
import { v } from 'convex/values';

import { lifecycleStatusValidator } from '../governance/soft_delete_validators';
import {
  jsonRecordValidator,
  jsonValueValidator,
} from '../lib/validators/json';
import { stepConfigValidator } from '../workflow_engine/types/nodes';
import { executionStatusValidator } from './executions/validators';

const workflowStatusValidator = v.union(
  v.literal('draft'),
  v.literal('active'),
  v.literal('archived'),
);

/** @deprecated — Legacy table. New workflows use file-based JSON storage. Kept for backward compatibility. */
export const wfDefinitionsTable = defineTable({
  organizationId: v.string(),
  version: v.string(),
  versionNumber: v.number(),
  status: workflowStatusValidator,
  workflowType: v.literal('predefined'),
  name: v.string(),
  description: v.optional(v.string()),
  category: v.optional(v.string()),
  config: v.optional(
    v.object({
      timeout: v.optional(v.number()),
      retryPolicy: v.optional(
        v.object({
          maxRetries: v.number(),
          backoffMs: v.number(),
        }),
      ),
      variables: v.optional(v.record(v.string(), jsonValueValidator)),
      secrets: v.optional(
        v.record(
          v.string(),
          v.object({
            kind: v.literal('inlineEncrypted'),
            cipherText: v.string(),
            keyId: v.optional(v.string()),
          }),
        ),
      ),
      // Workflow-level fallback chain inherited by every LLM step that
      // defines neither `model` nor `models`. Step-level overrides win.
      models: v.optional(v.array(v.string())),
    }),
  ),
  rootVersionId: v.optional(v.id('wfDefinitions')),
  parentVersionId: v.optional(v.id('wfDefinitions')),
  publishedAt: v.optional(v.number()),
  publishedBy: v.optional(v.string()),
  changeLog: v.optional(v.string()),
  userRequirement: v.optional(
    v.object({
      goal: v.string(),
      context: v.optional(v.string()),
      constraints: v.optional(v.array(v.string())),
      expectedOutcome: v.optional(v.string()),
    }),
  ),
  availableAgentTypes: v.optional(v.array(v.string())),
  globalConfig: v.optional(jsonRecordValidator),
  plannerConfig: v.optional(
    v.object({
      model: v.optional(v.string()),
      temperature: v.optional(v.number()),
      maxSteps: v.optional(v.number()),
    }),
  ),
  metadata: v.optional(jsonRecordValidator),
})
  .index('by_org', ['organizationId'])
  .index('by_org_status', ['organizationId', 'status'])
  .index('by_status', ['status'])
  .index('by_org_and_name', ['organizationId', 'name'])
  .index('by_org_name_version', ['organizationId', 'name', 'versionNumber'])
  .index('by_org_name_status', ['organizationId', 'name', 'status'])
  .index('by_rootVersionId', ['rootVersionId'])
  .index('by_root_status', ['rootVersionId', 'status'])
  .index('by_org_versionNumber', ['organizationId', 'versionNumber']);

/** @deprecated — Legacy table. Steps are now stored in workflow JSON files. Kept for backward compatibility. */
export const wfStepDefsTable = defineTable({
  organizationId: v.string(),
  wfDefinitionId: v.id('wfDefinitions'),
  stepSlug: v.string(),
  name: v.string(),
  description: v.optional(v.string()),
  stepType: v.union(
    v.literal('start'),
    v.literal('trigger'),
    v.literal('llm'),
    v.literal('condition'),
    v.literal('action'),
    v.literal('loop'),
    v.literal('output'),
  ),
  order: v.number(),
  nextSteps: v.record(v.string(), v.string()),
  config: stepConfigValidator,
  // @deprecated — unused legacy fields, kept for schema compatibility
  inputMapping: v.optional(v.record(v.string(), jsonValueValidator)),
  outputMapping: v.optional(v.record(v.string(), jsonValueValidator)),
  metadata: v.optional(jsonRecordValidator),
})
  .index('by_definition', ['wfDefinitionId'])
  .index('by_definition_order', ['wfDefinitionId', 'order'])
  .index('by_step_slug', ['wfDefinitionId', 'stepSlug'])
  .index('by_organizationId_and_stepType_and_order', [
    'organizationId',
    'stepType',
    'order',
  ]);

/** @deprecated — Legacy table. Audit history is now file-based (.history/ snapshots). Kept for backward compatibility. */
export const wfStepAuditLogsTable = defineTable({
  stepId: v.id('wfStepDefs'),
  wfDefinitionId: v.id('wfDefinitions'),
  organizationId: v.string(),
  changedBy: v.string(),
  changedAt: v.number(),
  changeType: v.union(
    v.literal('created'),
    v.literal('updated'),
    v.literal('deleted'),
  ),
  editMode: v.union(v.literal('visual'), v.literal('json'), v.literal('ai')),
  before: v.optional(jsonRecordValidator),
  after: v.optional(jsonRecordValidator),
})
  .index('by_step', ['stepId'])
  .index('by_workflow', ['wfDefinitionId'])
  .index('by_org', ['organizationId'])
  .index('by_org_changedAt', ['organizationId', 'changedAt']);

export const wfExecutionsTable = defineTable({
  organizationId: v.string(),
  wfDefinitionId: v.union(v.id('wfDefinitions'), v.string(), v.null()),
  rootWfDefinitionId: v.optional(v.id('wfDefinitions')),
  workflowSlug: v.optional(v.string()),
  workflowVersion: v.optional(v.string()),
  status: executionStatusValidator,
  currentStepSlug: v.string(),
  currentStepName: v.optional(v.string()),
  loopProgress: v.optional(
    v.object({
      current: v.number(),
      total: v.number(),
    }),
  ),
  waitingFor: v.optional(v.string()),
  startedAt: v.number(),
  updatedAt: v.number(),
  completedAt: v.optional(v.number()),
  componentWorkflowId: v.optional(v.string()),
  shardIndex: v.optional(v.number()),
  userId: v.optional(v.string()),
  threadId: v.optional(v.string()),
  variables: v.optional(v.string()),
  variablesStorageId: v.optional(v.id('_storage')),
  input: v.optional(jsonValueValidator),
  output: v.optional(jsonValueValidator),
  outputStorageId: v.optional(v.id('_storage')),
  workflowConfig: v.optional(v.string()),
  stepsConfig: v.optional(v.string()),
  stepsConfigStorageId: v.optional(v.id('_storage')),
  triggeredBy: v.optional(v.string()),
  triggerData: v.optional(jsonValueValidator),
  error: v.optional(v.string()),
  // Coarse failure classification (see ExecutionErrorCode in executions/types.ts).
  // Kept a plain string in the schema so adding codes never needs a migration.
  errorCode: v.optional(v.string()),
  metadata: v.optional(v.string()),
  lifecycleStatus: v.optional(lifecycleStatusValidator),
  statusChangedAt: v.optional(v.number()),
  // Set once the first transition-to-failed has emitted a `workflow.failed`
  // notification, so the various failure paths (engine callback, stuck-recovery
  // watchdog, start-failure, dynamic next-step) notify exactly once.
  failureNotifiedAt: v.optional(v.number()),
})
  .index('by_org', ['organizationId'])
  .index('by_org_lifecycleStatus', ['organizationId', 'lifecycleStatus'])
  .index('by_definition', ['wfDefinitionId'])
  .index('by_definition_status', ['wfDefinitionId', 'status'])
  .index('by_definition_startedAt', ['wfDefinitionId', 'startedAt'])
  .index('by_definition_triggeredBy_startedAt', [
    'wfDefinitionId',
    'triggeredBy',
    'startedAt',
  ])
  .index('by_status', ['status'])
  .index('by_org_status', ['organizationId', 'status'])
  .index('by_org_triggeredBy', ['organizationId', 'triggeredBy'])
  .index('by_component_workflow', ['componentWorkflowId'])
  .index('by_org_workflowSlug', ['organizationId', 'workflowSlug'])
  // Subject-scoped scan for GDPR Art 17 erasure (`eraseSubjectWfExecutions`).
  // Walks rows where the subject was the executing user. Combined with
  // `by_org_triggeredBy` for the trigger-author scope.
  .index('by_org_user', ['organizationId', 'userId']);

export const wfInstallationsTable = defineTable({
  organizationId: v.string(),
  workflowSlug: v.string(),
  installedAt: v.number(),
  installedBy: v.string(),
  contentHash: v.string(),
})
  .index('by_org', ['organizationId'])
  .index('by_org_slug', ['organizationId', 'workflowSlug']);

export const workflowProcessingRecordsTable = defineTable({
  organizationId: v.string(),
  tableName: v.string(),
  recordId: v.string(),
  wfDefinitionId: v.string(),
  recordCreationTime: v.number(),
  processedAt: v.number(),
  status: v.optional(v.union(v.literal('in_progress'), v.literal('completed'))),
  metadata: v.optional(jsonRecordValidator),
})
  .index('by_org_table_wfDefinition', [
    'organizationId',
    'tableName',
    'wfDefinitionId',
  ])
  .index('by_org_table_wfDefinition_creationTime', [
    'organizationId',
    'tableName',
    'wfDefinitionId',
    'recordCreationTime',
  ])
  .index('by_org_table_wfDefinition_processedAt', [
    'organizationId',
    'tableName',
    'wfDefinitionId',
    'processedAt',
  ])
  .index('by_record', ['tableName', 'recordId', 'wfDefinitionId']);
