import type { Attachment } from './attachment'
import { ApiClient } from './base'
import type { Currency } from './data'
import { type Page, type PageRequest } from './pagination'

export interface TransactionVendor {
  id: number
  name: string
  description: string | null
}

export interface TransactionVendorPayload {
  name: string
  description: string | null
}

export interface TransactionCategory {
  id: number
  parentId: number | null
  name: string
  description: string | null
  color: string
}

export interface TransactionCategoryTree {
  id: number
  parentId: number | null
  name: string
  description: string
  color: string
  children: TransactionCategoryTree[]
  depth: number
}

export interface TransactionsListItem {
  id: number
  timestamp: string
  addedAt: string
  amount: number
  currency: Currency
  description: string
  internalTransfer: boolean
  vendor: TransactionsListItemVendor | null
  category: TransactionsListItemCategory | null
  creditedAccount: TransactionsListItemAccount | null
  debitedAccount: TransactionsListItemAccount | null
  tags: string[]
}

export interface TransactionsListItemVendor {
  id: number
  name: string
}

export interface TransactionsListItemCategory {
  id: number
  name: string
  color: string
}

export interface TransactionsListItemAccount {
  id: number
  name: string
  type: string
  numberSuffix: string
}

export interface TransactionDetail {
  id: number
  timestamp: string
  addedAt: string
  amount: number
  currency: Currency
  description: string
  internalTransfer: boolean
  vendor: TransactionVendor | null
  category: TransactionCategory | null
  creditedAccount: TransactionDetailAccount | null
  debitedAccount: TransactionDetailAccount | null
  tags: string[]
  lineItems: TransactionDetailLineItem[]
  attachments: Attachment[]
}

export interface TransactionDetailAccount {
  id: number
  name: string
  type: string
  numberSuffix: string
}

export interface TransactionDetailLineItem {
  idx: number
  valuePerItem: number
  quantity: number
  description: string
  category: TransactionCategory | null
}

export interface AddTransactionPayload {
  timestamp: string
  amount: number
  currencyCode: string
  description: string
  internalTransfer: boolean
  vendorId: number | null
  categoryId: number | null
  creditedAccountId: number | null
  debitedAccountId: number | null
  tags: string[]
  lineItems: AddTransactionPayloadLineItem[]
  attachmentIdsToRemove: number[]
}

export interface AddTransactionPayloadLineItem {
  valuePerItem: number
  quantity: number
  description: string
  categoryId: number | null
}

export interface CreateCategoryPayload {
  name: string
  description: string | null
  color: string
  parentId: number | null
}

export class TransactionApiClient extends ApiClient {
  readonly path: string

  constructor(profileName: string) {
    super()
    this.path = `/profiles/${profileName}`
  }

  getVendors(): Promise<TransactionVendor[]> {
    return super.getJson(this.path + '/vendors')
  }

  getVendor(id: number): Promise<TransactionVendor> {
    return super.getJson(this.path + '/vendors/' + id)
  }

  createVendor(data: TransactionVendorPayload): Promise<TransactionVendor> {
    return super.postJson(this.path + '/vendors', data)
  }

  updateVendor(id: number, data: TransactionVendorPayload): Promise<TransactionVendor> {
    return super.putJson(this.path + '/vendors/' + id, data)
  }

  deleteVendor(id: number): Promise<void> {
    return super.delete(this.path + '/vendors/' + id)
  }

  getCategories(): Promise<TransactionCategoryTree[]> {
    return super.getJson(this.path + '/categories')
  }

  async getCategoriesFlattened(): Promise<TransactionCategoryTree[]> {
    const categories = await this.getCategories()
    const flat: TransactionCategoryTree[] = []
    await this.flattenCategories(flat, categories)
    return flat
  }

  private flattenCategories(arr: TransactionCategoryTree[], tree: TransactionCategoryTree[]) {
    for (const category of tree) {
      arr.push(category)
      this.flattenCategories(arr, category.children)
    }
  }

  getCategory(id: number): Promise<TransactionCategory> {
    return super.getJson(this.path + '/categories/' + id)
  }

  createCategory(data: CreateCategoryPayload): Promise<TransactionCategory> {
    return super.postJson(this.path + '/categories', data)
  }

  updateCategory(id: number, data: CreateCategoryPayload): Promise<TransactionCategory> {
    return super.putJson(this.path + '/categories/' + id, data)
  }

  deleteCategory(id: number): Promise<void> {
    return super.delete(this.path + '/categories/' + id)
  }

  getTransactions(
    paginationOptions: PageRequest | undefined = undefined,
  ): Promise<Page<TransactionsListItem>> {
    return super.getJsonPage(this.path + '/transactions', paginationOptions)
  }

  searchTransactions(
    params: URLSearchParams,
    paginationOptions: PageRequest | undefined = undefined,
  ): Promise<Page<TransactionsListItem>> {
    if (paginationOptions !== undefined) {
      params.append('page', paginationOptions.page + '')
      params.append('size', paginationOptions.size + '')
      for (const sort of paginationOptions.sorts) {
        params.append('sort', sort.attribute + ',' + sort.dir)
      }
    }
    return super.getJson(this.path + '/transactions/search?' + params.toString())
  }

  getTransaction(id: number): Promise<TransactionDetail> {
    return super.getJson(this.path + '/transactions/' + id)
  }

  addTransaction(data: AddTransactionPayload, files: File[] = []): Promise<TransactionDetail> {
    const formData = new FormData()
    formData.append('payload', JSON.stringify(data))
    for (const file of files) {
      formData.append('file', file)
    }
    return super.postFormData(this.path + '/transactions', formData)
  }

  updateTransaction(
    id: number,
    data: AddTransactionPayload,
    files: File[] = [],
  ): Promise<TransactionDetail> {
    const formData = new FormData()
    formData.append('payload', JSON.stringify(data))
    for (const file of files) {
      formData.append('file', file)
    }
    return super.putFormData(this.path + '/transactions/' + id, formData)
  }

  deleteTransaction(id: number): Promise<void> {
    return super.delete(this.path + '/transactions/' + id)
  }

  getAllTags(): Promise<string[]> {
    return super.getJson(this.path + '/transaction-tags')
  }
}
