{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RecordWildCards #-}

module Laskutin where

import Control.Concurrent (threadDelay)
import Control.Monad (forM_, forM)
import Data.Csv (encodeByName)
import Data.List.NonEmpty (nonEmpty)
import Data.MIME (Address, renderAddresses)
import Data.Maybe (catMaybes, mapMaybe)
import Data.Text.Encoding (decodeUtf8)
import System.Exit (exitFailure)
import System.IO (stderr, hPutStrLn)

import qualified Data.ByteString.Lazy as LBS
import qualified Data.Text.IO as T

import Laskutin.CSV
import Laskutin.Email
import Laskutin.Options
import Laskutin.Types

main :: IO ()
main = do
    Options {csv, command} <- parseOptions
    case command of
      Send sendOptions -> sendInvoicesMain csv sendOptions
      UpdateTable bankCsv -> updateTableMain csv bankCsv

sendInvoicesMain :: FilePath -> SendOptions -> IO ()
sendInvoicesMain csvPath options@SendOptions {email, sendmail} = do
    invoices <- readInvoices csvPath options
    forM_ invoices $ \invoice@(address, _) -> do
        sendEmail sendmail $ uncurry (renderEmail [email]) invoice
        putStr "Lähetetty osoitteeseen: "
        T.putStrLn $ decodeUtf8 $ renderAddresses address
        threadDelay (3 * 1000 * 1000)

updateTableMain :: FilePath -> FilePath -> IO ()
updateTableMain invoiceCsv transactionCsv = do
    (headers, csvInvoices) <- parseCsvFile invoiceCsv
    (_, csvTransactions) <- parseCsvFile transactionCsv
    let newInvoices = fmap (updateInvoices csvTransactions) csvInvoices
    LBS.writeFile invoiceCsv $ encodeByName headers newInvoices

updateInvoices :: [CsvTransaction] -> CsvInvoice -> CsvInvoice
updateInvoices transactions invoice@CsvInvoice {isPaid, reference}
  | isPaid = invoice 
  | otherwise = invoice {isPaid = reference `elem` transactionReferences}
      where transactionReferences = catMaybes $ transactionReference <$> transactions
            transactionReference CsvTransaction {referenceOrMessage = Message _} = Nothing
            transactionReference CsvTransaction {referenceOrMessage = Reference ref} = Just ref

readInvoices :: FilePath -> SendOptions -> IO [([Address], Invoice)]
readInvoices csv SendOptions {account, recipient, subject, message, due} = do
    (_, csvInvoices) <- parseCsvFile csv
    forM csvInvoices $ \CsvInvoice {..} -> do
        invoiceRows <- maybe (hPutStrLn stderr ("No invoice rows in invoice") >> exitFailure) pure $
            nonEmpty $ mapMaybe invoiceRowFromCsv rows
        pure ([invoiceRecipient], Invoice { rows = invoiceRows, ..})