Invoice = {}
Invoice.dir = g_currentModDirectory
Invoice.modName = g_currentModName
Invoice.__index = Invoice
Invoice_mt = Class(Invoice)

Invoice.STATUS = {
  OPEN = "OPEN",
  DRAFT = "DRAFT",
  SENT = "SENT",
  PAID = "PAID",
  CANCELLED = "CANCELLED",
  OVERDUE = "OVERDUE",
  REJECTED = "REJECTED"
}

function Invoice.new(id, creatorFarmId, receiverFarmId, status, title, description,
                     total, paid,
                     relatedContractId, autoGenerated)
  local self = setmetatable({}, Invoice_mt)

  self.id = id                                 -- General id
  self.number = string.format("CC-INV-%d", id) -- Generated user friendly id
  self.creatorFarmId = creatorFarmId           -- Who created the invoice
  self.receiverFarmId = receiverFarmId         -- Who must pay
  self.status = status                         -- Status of the invoice
  self.currency = "EUR"                        -- Currency (needs updating)
  self.title = title                           -- Title of the contract
  self.description = description               -- Optional description
  self.lines = {}                              -- Invoicelines (future)
  self.total = total                           -- Total amount that needs to be paid
  self.paid = paid                             -- Amount that is paid (needed for partial payment in the future)
  self.remaining = nil                         -- Partial payment (future)
  self.relatedContractId = relatedContractId   -- Link to a contract (optional)
  self.autoGenerated = autoGenerated           -- Boolean wether contract is autoGenerated from contract or not
  self.meta = nil                              -- Future-proof (optional)

  return self
end

function Invoice:writeStream(streamId)
  streamWriteInt32(streamId, self.id)
  streamWriteString(streamId, self.number)
  streamWriteInt32(streamId, self.creatorFarmId)
  streamWriteInt32(streamId, self.receiverFarmId)
  streamWriteString(streamId, self.status or Invoice.STATUS.DRAFT)
  streamWriteString(streamId, self.currency)
  streamWriteString(streamId, self.title)
  streamWriteString(streamId, self.description)
  self:writeLinesToStream(streamId)
  streamWriteInt32(streamId, self.total)
  streamWriteInt32(streamId, self.paid)
  streamWriteInt32(streamId, self.remaining or 0)
  streamWriteInt32(streamId, self.relatedContractId or -1)
  streamWriteBool(streamId, self.autoGenerated)
  streamWriteString(streamId, self.meta or "")
end

function Invoice.newFromStream(streamId)
  local id = streamReadInt32(streamId)
  local number = streamReadString(streamId)
  local creatorFarmId = streamReadInt32(streamId)
  local receiverFarmId = streamReadInt32(streamId)
  local status = streamReadString(streamId)
  local currency = streamReadString(streamId)
  local title = streamReadString(streamId)
  local description = streamReadString(streamId)
  local lines = Invoice.readLinesFromStream(streamId)
  local total = streamReadInt32(streamId)
  local paid = streamReadInt32(streamId)
  local remaining = streamReadInt32(streamId)
  local relatedContractId = streamReadInt32(streamId)
  local autoGenerated = streamReadBool(streamId)
  local meta = streamReadString(streamId)
  relatedContractId = relatedContractId ~= -1 and relatedContractId or nil

  local invoice = Invoice.new(
    id,
    creatorFarmId,
    receiverFarmId,
    status,
    title,
    description,
    total,
    paid,
    relatedContractId,
    autoGenerated
  )

  invoice.number = number
  invoice.currency = currency
  invoice.remaining = remaining
  invoice.meta = meta ~= "" and meta or nil
  invoice.lines = lines

  return invoice
end

function Invoice:writeLinesToStream(streamId)
  local lines = self.lines or {}
  streamWriteInt32(streamId, #lines)

  for _, l in ipairs(lines) do
    streamWriteString(streamId, l.title or "")
    streamWriteFloat32(streamId, tonumber(l.amount) or 0)
  end
end

function Invoice.readLinesFromStream(streamId)
  local count = streamReadInt32(streamId)
  local lines = {}

  for i = 1, count do
    local title = streamReadString(streamId)
    local amount = streamReadFloat32(streamId)
    table.insert(lines, { title = title, amount = amount })
  end

  return lines
end

-- Returns the status of the invoice from the perspective of the given farmId
function Invoice:getStatus(farmId)
  if self.status == Invoice.STATUS.SENT then
    if farmId == self.creatorFarmId then
      return self.status
    else
      return Invoice.STATUS.OPEN
    end
  end

  return self.status
end
