module Assignment (markdownParser, convertADTHTML) where  -- No additional exports for Part A

import           Control.Applicative (Alternative (..), some, many, optional)
import           Data.Char           (isDigit, isSpace)
import           Instances           (ParseError (..), ParseResult (..), Parser (..), readInt)
import qualified Data.Text as T
import           Data.Time           (getCurrentTime, formatTime, defaultTimeLocale)

-- Algebraic Data Type (ADT) to represent Markdown elements
data ADT 
  = Text String
  | Italic ADT
  | Bold ADT
  | Strikethrough ADT
  | Link ADT ADT
  | InlineCode ADT
  | Footnote Int    -- Changed to Int since footnote marker is a number
  | Image ADT String ADT -- Alt text, URL, Caption
  | FootnoteReference Int ADT   -- Footnote number, reference text
  | Heading Int ADT
  | BlockQuote [ADT]
  | CodeBlock (Maybe String) ADT  -- Optional language, code
  | OrderedList [ADT]
  | Table [[ADT]]  -- Table is now a list of rows (each row is a list of cells)
  deriving (Show, Eq)

-- Parser for plain text (helper function)
parsePlainText :: Parser String
parsePlainText = some (satisfy (not . isSpace))  -- Match one or more non-space characters

-- Parser for Markdown (main parser)
markdownParser :: Parser ADT
markdownParser = choice  -- Try each parser in order
  [ parseTextModifier  -- Text modifiers take precedence
  , parseImage
  , parseFootnoteReference
  , parseHeading
  , parseBlockQuote
  , parseCodeBlock
  , parseOrderedList
  , parseTable
  , Text <$> parsePlainText  -- Plain text is the last resort
  ]

-- Parsers for text modifiers
parseTextModifier :: Parser ADT
parseTextModifier = choice
  [ Italic <$> (string "_" *> markdownParser <* string "_")
  , Bold <$> (string "**" *> markdownParser <* string "**")
  , Strikethrough <$> (string "~~" *> markdownParser <* string "~~")
  , Link <$> (char '[' *> markdownParser <* char ']') <*> (char '(' *> parsePlainText <* char ')') 
  , InlineCode <$> (char '`' *> parsePlainText <* char '`') 
  , Footnote . read <$> (string "[^" *> some (satisfy isDigit) <* char ']')  -- Extract footnote number
  ]

parseImage :: Parser ADT
parseImage = do
  _ <- char '!'
  _ <- char '['
  alt <- parsePlainText
  _ <- char ']'
  _ <- char '('
  url <- some (satisfy (/= ')')) 
  _ <- char ')'
  caption <- optional $ do
    _ <- char '\"'
    cap <- parsePlainText
    _ <- char '\"'
    return cap
  return $ Image (Text alt) url (maybe (Text "") Text caption)  

parseFootnoteReference :: Parser ADT
parseFootnoteReference = do
  _ <- string "[^"
  num <- read <$> some (satisfy isDigit)  -- Parse and convert footnote number
  _ <- char ']'
  _ <- char ':'
  _ <- satisfy isSpace
  text <- parsePlainText  -- Parse the footnote reference text
  return $ FootnoteReference num (Text text) 

parseHeading :: Parser ADT
parseHeading = do
  hashes <- some (char '#')
  _ <- satisfy isSpace
  text <- parsePlainText
  return $ Heading (length hashes) (Text text)

parseBlockQuote :: Parser ADT
parseBlockQuote = do
  _ <- char '>'
  _ <- satisfy isSpace 
  lines <- many1 $ Text <$> parsePlainText <* char '\n'
  return $ BlockQuote lines 

parseCodeBlock :: Parser ADT
parseCodeBlock = do
  _ <- string "```"
  lang <- optional $ some (satisfy (/= '\n')) -- Optional language identifier 
  _ <- char '\n'
  code <- manyTill anyChar (try $ string "```") 
  return $ CodeBlock lang (Text code)  

parseOrderedList :: Parser ADT
parseOrderedList = do
    _ <- some (satisfy isDigit) -- Allow for numbers other than '1'
    _ <- char '.'
    _ <- satisfy isSpace
    item1 <- parsePlainText
    restItems <- many $ do
        _ <- char '\n'
        _ <- some (satisfy isDigit)
        _ <- char '.'
        _ <- satisfy isSpace
        Text <$> parsePlainText
    return $ OrderedList (Text item1 : restItems)

parseTable :: Parser ADT
parseTable = do
  header <- some (char '|' *> parsePlainText) <* char '|'
  _ <- char '\n' 
  _ <- many (char '|') *> many (char '-') <* many (char '|') <* char '\n'
  rows <- some $ some (char '|' *> parsePlainText) <* char '|' <* char '\n'
  return $ Table (map (map Text) (header : rows)) -- Transform to ADT

convertADTHTML :: ADT -> String
-- Implement HTML Conversion for Part B. No implementation needed for Part A.
convertADTHTML _ = "" 

getTime :: IO String
getTime = formatTime defaultTimeLocale "%Y-%m-%dT%H:%M:%S" <$> getCurrentTime