/**
 * Refer: anchor-master/ts/src/idl.ts
 */

export type Idl = {
  version: string
  name: string
  instructions: IdlInstruction[]
  state?: IdlState
  accounts?: IdlAccountDef[]
  types?: IdlTypeDef[]
  events?: IdlEvent[]
  errors?: IdlErrorCode[]
  constants?: IdlConstant[]
  metadata?: IdlMetadata
}

export type IdlMetadata = any

export type IdlConstant = {
  name: string
  type: IdlType
  value: string
}

export type IdlEvent = {
  name: string
  fields: IdlEventField[]
}

export type IdlEventField = {
  name: string
  type: IdlType
  index: boolean
}

export type IdlInstruction = {
  name: string
  accounts: IdlAccountItem[]
  args: IdlField[]
  returns?: IdlType
}

export type IdlState = {
  struct: IdlTypeDef
  methods: IdlStateMethod[]
}

export type IdlStateMethod = IdlInstruction

export type IdlAccountItem = IdlAccount | IdlAccounts

export type IdlAccount = {
  name: string
  isMut: boolean
  isSigner: boolean
  pda?: IdlPda
}

export type IdlPda = {
  seeds: IdlSeed[]
  programId?: IdlSeed
}

export type IdlSeed = any // TODO

// A nested/recursive version of IdlAccount.
export type IdlAccounts = {
  name: string
  accounts: IdlAccountItem[]
}

export type IdlField = {
  name: string
  type: IdlType
}

export type IdlTypeDef = {
  name: string
  type: IdlTypeDefTy
}

export type IdlAccountDef = {
  name: string
  type: IdlTypeDefTyStruct
}

export type IdlTypeDefTyStruct = {
  kind: 'struct'
  fields: IdlTypeDefStruct
}

export type IdlTypeDefTyEnum = {
  kind: 'enum'
  variants: IdlEnumVariant[]
}

type IdlTypeDefTy = IdlTypeDefTyEnum | IdlTypeDefTyStruct

export type IdlTypeDefStruct = Array<IdlField>

export type IdlType =
  | 'bool'
  | 'u8'
  | 'i8'
  | 'u16'
  | 'i16'
  | 'u32'
  | 'i32'
  | 'f32'
  | 'u64'
  | 'i64'
  | 'f64'
  | 'u128'
  | 'i128'
  | 'bytes'
  | 'string'
  | 'publicKey'
  | IdlTypeDefined
  | IdlTypeOption
  | IdlTypeCOption
  | IdlTypeVec
  | IdlTypeArray

// User defined type.
export type IdlTypeDefined = {
  defined: string
}

export type IdlTypeOption = {
  option: IdlType
}

export type IdlTypeCOption = {
  coption: IdlType
}

export type IdlTypeVec = {
  vec: IdlType
}

export type IdlTypeArray = {
  array: [idlType: IdlType, size: number]
}

export type IdlEnumVariant = {
  name: string
  fields?: IdlEnumFields
}

type IdlEnumFields = IdlEnumFieldsNamed | IdlEnumFieldsTuple

type IdlEnumFieldsNamed = IdlField[]

type IdlEnumFieldsTuple = IdlType[]

export type IdlErrorCode = {
  code: number
  name: string
  msg?: string
}
