import System.IO
import System.Directory
import Data.List
import System.Environment
import Data.Time
import Data.Maybe
import Utils

type Note = String

getNotefilePath :: IO (String)
getNotefilePath = getEnv "NOTE_FILE"

main :: IO ()
main = do
  args <- getArgs
  runWith args
    where
      runWith :: [String] -> IO ()
      runWith [] = helpMode []
      runWith args@(('-':cmd:_):_)
        | cmd == 'w' = writeMode args
        | cmd == 'p' = pipeMode args
        | cmd == 'r' = readMode args
        | cmd == 'f' = findMode args
        | otherwise = helpMode args
      runWith args = writeMode args

writeMode :: [String] -> IO ()
writeMode (('-':_):nonCommandArgs) = writeMode nonCommandArgs
writeMode args = do
  notefilePath <- getNotefilePath
  time <- getZonedTime
  let note = makeNote args time
  appendFile notefilePath note
  putStr note

pipeMode :: [String] -> IO ()
pipeMode (('-':_):nonCommandArgs) = pipeMode nonCommandArgs
pipeMode args = do
  notefilePath <- getNotefilePath
  time <- getZonedTime
  contents <- getContents
  let note = makeNote args time
  let formattedContents = "\t+---\n" ++ (unlines $ map ("\t| " ++) $ lines contents) ++ "\t+---\n"
  let noteAndContents = note ++ formattedContents
  appendFile notefilePath noteAndContents
  putStr noteAndContents

findMode :: [String] -> IO ()
findMode ((_:_:subcommands):params) = do
  notes <- getNotes
  let findPattern = head params
  putStr . concat . filterAction findPattern $ notes
    where
      filterAction
        | elem 'm' subcommands = filterByMessage
        | otherwise = filterBySubject

filterBySubject :: String -> [Note] -> [Note]
filterBySubject patt = filter ((isInfixOf patt) . getSubjectFromNote)

filterByMessage :: String -> [Note] -> [Note]
filterByMessage patt = filter ((isInfixOf patt) . getMessageFromNote)

getSubjectFromNote :: Note -> String
getSubjectFromNote = snd . splitOnFirst ' ' . fst . splitOnFirst ':' . snd . splitOnFirst ':'

getMessageFromNote :: Note -> String
getMessageFromNote = tail . snd . splitOnNth 2 ':'

readMode :: [String] -> IO ()
readMode args@((_:_:n):params) = do
  let numNotes = tryParseInt n 1
  notes <- getNotes
  if numNotes < 0
    then putStr $ concat $ take (-numNotes) notes
    else putStr $ concat $ takeLast numNotes notes

getNotes :: IO ([Note])
getNotes = do
  notefilePath <- getNotefilePath
  handle <- openFile notefilePath ReadMode
  contents <- hGetContents handle
  return $ parseNotes contents

parseNotes :: String -> [Note]
parseNotes contents = nonEmptyNotes
  where
    nonEmptyNotes = filter (/= "") notes
    notes = map unlines linesByNote
    linesByNote = splitInclusiveWhen (not . isPrefixOf "\t") $ lines contents

helpMode :: [String] -> IO ()
helpMode _ = putStrLn "Help message" -- todo make useful

makeNote :: [String] -> ZonedTime -> Note
makeNote [] time = (makeTimestamp time) ++ " MISC: \n"
makeNote (subject:msgParts) time = indentedNote
  where
    subject' = filter (/= ':') subject
    msg = intercalate " " msgParts
    note = (makeTimestamp time) ++ " " ++ subject' ++ ": " ++ msg ++ "\n"
    noteLines = lines note
    indentedNote = unlines $ head noteLines : (map ('\t':) $ tail noteLines)

makeTimestamp :: ZonedTime -> String
makeTimestamp time = formatTime defaultTimeLocale "%F %R" time
