import { type RouteLocation } from 'vue-router'
import { ApiClient } from './base'
import type { Currency } from './data'
import type { Page, PageRequest } from './pagination'
import { getSelectedProfile } from './profile'
import type { Attachment } from './attachment'

export interface AccountType {
  id: string
  name: string
  debitsPositive: boolean
  emoji: string
}

export abstract class AccountTypes {
  public static readonly CHECKING: AccountType = {
    id: 'CHECKING',
    name: 'Checking',
    debitsPositive: true,
    emoji: '💵',
  }
  public static readonly SAVINGS: AccountType = {
    id: 'SAVINGS',
    name: 'Savings',
    debitsPositive: true,
    emoji: '💰',
  }
  public static readonly CREDIT_CARD: AccountType = {
    id: 'CREDIT_CARD',
    name: 'Credit Card',
    debitsPositive: false,
    emoji: '💳',
  }
  public static readonly BROKERAGE: AccountType = {
    id: 'BROKERAGE',
    name: 'Brokerage',
    debitsPositive: true,
    emoji: '📈',
  }

  public static of(id: string): AccountType {
    if (id === 'CHECKING') return AccountTypes.CHECKING
    if (id === 'SAVINGS') return AccountTypes.SAVINGS
    if (id === 'CREDIT_CARD') return AccountTypes.CREDIT_CARD
    if (id === 'BROKERAGE') return AccountTypes.BROKERAGE
    throw new Error('Unknown account type: ' + id)
  }
}

export interface Account {
  id: number
  createdAt: string
  archived: boolean
  type: string
  numberSuffix: string
  name: string
  currency: Currency
  description: string
  currentBalance: number | null
}

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

export interface AccountCreationPayload {
  type: string
  numberSuffix: string
  name: string
  currency: string
  description: string
}

export enum AccountJournalEntryType {
  CREDIT = 'CREDIT',
  DEBIT = 'DEBIT',
}

export interface AccountJournalEntry {
  id: number
  timestamp: string
  accountId: number
  transactionId: number
  amount: number
  type: AccountJournalEntryType
  currency: Currency
}

export enum AccountValueRecordType {
  BALANCE = 'BALANCE',
}

export interface AccountValueRecord {
  id: number
  timestamp: string
  accountId: number
  type: AccountValueRecordType
  value: number
  currency: Currency
  attachments: Attachment[]
}

export interface AccountValueRecordCreationPayload {
  timestamp: string
  type: AccountValueRecordType
  value: number
}

// History:
export enum AccountHistoryItemType {
  TEXT = 'TEXT',
  PROPERTY_CHANGE = 'PROPERTY_CHANGE',
  VALUE_RECORD = 'VALUE_RECORD',
  JOURNAL_ENTRY = 'JOURNAL_ENTRY',
}

export function accountHistoryItemTypeToDisplayName(type: AccountHistoryItemType) {
  if (type === AccountHistoryItemType.TEXT) return 'Text'
  if (type === AccountHistoryItemType.PROPERTY_CHANGE) return 'Property Change'
  if (type === AccountHistoryItemType.VALUE_RECORD) return 'Value Record'
  if (type === AccountHistoryItemType.JOURNAL_ENTRY) return 'Journal Entry'
  return 'Unknown'
}

export interface AccountHistoryItem {
  timestamp: string
  type: AccountHistoryItemType
}

export interface AccountHistoryValueRecordItem extends AccountHistoryItem {
  valueRecordId: number
  valueRecordType: AccountValueRecordType
  value: number
  currency: Currency
}

export interface AccountHistoryJournalEntryItem extends AccountHistoryItem {
  journalEntryType: AccountJournalEntryType
  amount: number
  currency: Currency
  transactionId: number
  transactionDescription: string
}

export interface CurrencyBalance {
  currency: Currency
  balance: number
}

export class AccountApiClient extends ApiClient {
  readonly path: string
  readonly profileName: string

  constructor(route: RouteLocation) {
    super()
    this.profileName = getSelectedProfile(route)
    this.path = `/profiles/${getSelectedProfile(route)}/accounts`
  }

  getAccounts(): Promise<Account[]> {
    return super.getJson(this.path)
  }

  getAccount(id: number): Promise<Account> {
    return super.getJson(this.path + '/' + id)
  }

  createAccount(data: AccountCreationPayload): Promise<Account> {
    return super.postJson(this.path, data)
  }

  updateAccount(id: number, data: AccountCreationPayload): Promise<Account> {
    return super.putJson(this.path + '/' + id, data)
  }

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

  async getBalance(id: number, timestamp: Date): Promise<number | undefined> {
    const result: { balance: number | null } = await super.getJson(
      `${this.path}/${id}/balance?timestamp=${timestamp.toISOString()}`,
    )
    if (result.balance === null) return undefined
    return result.balance
  }

  getHistory(id: number, pageRequest: PageRequest): Promise<Page<AccountHistoryItem>> {
    return super.getJsonPage(`${this.path}/${id}/history`, pageRequest)
  }

  getTotalBalances(): Promise<CurrencyBalance[]> {
    return super.getJson(`/profiles/${this.profileName}/account-balances`)
  }

  getValueRecords(accountId: number, pageRequest: PageRequest): Promise<Page<AccountValueRecord>> {
    return super.getJsonPage(this.path + '/' + accountId + '/value-records', pageRequest)
  }

  getValueRecord(accountId: number, valueRecordId: number): Promise<AccountValueRecord> {
    return super.getJson(this.path + '/' + accountId + '/value-records/' + valueRecordId)
  }

  createValueRecord(
    accountId: number,
    payload: AccountValueRecordCreationPayload,
    attachments: File[],
  ): Promise<AccountValueRecord> {
    const formData = new FormData()
    formData.append('payload', JSON.stringify(payload))
    for (const file of attachments) {
      formData.append('file', file)
    }
    return super.postFormData(`${this.path}/${accountId}/value-records`, formData)
  }

  deleteValueRecord(accountId: number, valueRecordId: number): Promise<void> {
    return super.delete(this.path + '/' + accountId + '/value-records/' + valueRecordId)
  }

  downloadAccountsExport(): Promise<void> {
    return super.getFile(this.path + '/export')
  }
}
