import { isDefined } from '@prefecthq/prefect-design'
import { PrefectKind, PrefectKindJinja, PrefectKindJson, PrefectKindWorkspaceVariable, SchemaValue, getPrefectKindFromValue, isPrefectKindJinja, isPrefectKindJson, isPrefectKindNull, isPrefectKindWorkspaceVariable } from '@/schemas/types/schemaValues'
import { isValidJson, stringify } from '@/utilities/json'

export class InvalidSchemaValueTransformation extends Error {
  public constructor(from: PrefectKind, to: PrefectKind) {
    super(`Unable to convert prefect kind value from ${from} to ${to}`)
  }
}

export function isInvalidSchemaValueTransformationError(value: unknown): value is InvalidSchemaValueTransformation {
  return value instanceof InvalidSchemaValueTransformation
}

export function mapSchemaValue(value: SchemaValue, to: PrefectKind): SchemaValue {
  const from = getPrefectKindFromValue(value)

  if (isPrefectKindJinja(value)) {
    return mapSchemaValueJinja(value, to)
  }

  // we cannot map a workspace variable to any other kinds
  if (isPrefectKindWorkspaceVariable(value)) {
    throw new InvalidSchemaValueTransformation(from, to)
  }

  if (isPrefectKindJson(value)) {
    return mapSchemaValueJson(value, to)
  }


  if (from === 'none') {
    return mapSchemaValueNone(value, to)
  }

  throw new Error(`Unhandled prefect kind value in mapSchemaValue: ${from}`)
}

function mapSchemaValueJinja(jinja: PrefectKindJinja, to: PrefectKind): SchemaValue {
  switch (to) {
    case 'jinja':
      return jinja

    case 'workspace_variable':
      throw new InvalidSchemaValueTransformation('jinja', 'workspace_variable')

    case 'json':
      return {
        __prefect_kind: 'json',
        value: jinja.template,
      } satisfies PrefectKindJson

    case 'none':
      if (isValidJson(jinja.template)) {
        return JSON.parse(jinja.template)
      }

      throw new InvalidSchemaValueTransformation('jinja', 'none')
    default:
      throw new Error(`mapSchemaValueJson missing case for kind: ${to}`)
  }
}

function mapSchemaValueJson(json: PrefectKindJson, to: PrefectKind): SchemaValue {
  switch (to) {
    case 'jinja':
      return {
        __prefect_kind: 'jinja',
        template: json.value,
      } satisfies PrefectKindJinja

    case 'workspace_variable':
      throw new InvalidSchemaValueTransformation('json', 'workspace_variable')

    case 'json':
      return json

    case 'none':
      if (isDefined(json.value) && isValidJson(json.value)) {
        return JSON.parse(json.value)
      }

      throw new InvalidSchemaValueTransformation('json', 'none')

    default:
      throw new Error(`mapSchemaValueJson missing case for kind: ${to}`)
  }
}

function mapSchemaValueNone(none: unknown, to: PrefectKind): SchemaValue {
  const value = isPrefectKindNull(none) ? none.value : none

  switch (to) {
    case 'jinja':
      return {
        __prefect_kind: 'jinja',
        template: stringify(value),
      } satisfies PrefectKindJinja

    case 'workspace_variable':
      return {
        __prefect_kind: 'workspace_variable',
      } satisfies PrefectKindWorkspaceVariable

    case 'json':
      return {
        __prefect_kind: 'json',
        value: stringify(value),
      } satisfies PrefectKindJson

    case 'none':
      return none

    default:
      throw new Error(`mapSchemaValueNone missing case for kind: ${to}`)
  }
}