module Main (main) where

import ConfigParser      -- Import the module we just created
import Tproxy
import Ip
import Udp2Raw
import Network.Socket
import Control.Concurrent (forkIO)
import Control.Exception (catch, try, SomeException)
import System.Environment (getArgs)
import System.Exit (exitFailure)
import System.IO (hPutStrLn, stderr, IOMode( ReadWriteMode ), hSetBuffering, BufferMode( LineBuffering ), hGetLine, hClose, stdout, BufferMode( NoBuffering ))
import Control.Monad (forM_, unless, forever)
import System.Posix.Files (setFileMode)
import Data.Maybe (fromMaybe)

import qualified Data.Text as T

import System.Exit (exitSuccess)
import System.Directory (removeFile)

socketPath :: FilePath
socketPath = "/run/tproxy/socket"


main :: IO ()
main = do
  hSetBuffering stdout NoBuffering
  -- 1. Get the configuration file path
  args <- getArgs
  let configFile = case args of
                     (f:_) -> f
                     [] -> "servers.yaml"

  -- 2. Call the exported function from the ConfigParser module
  parseResult <- ConfigParser.parseConfigFile configFile

  case parseResult of
    -- Handle failure (The Left String contains the formatted error)
    Left errorMessage -> do
      hPutStrLn stderr errorMessage

    -- Handle success
    Right cfg -> do
      putStrLn $ "Successfully loaded configuration from " ++ configFile ++ ":"

      -- Accessing the first entry's nameserver
      case cfg of
        (entry:_) -> do
          putStrLn $ "First server " ++ show (name entry)
          ip <- waitForIP (T.unpack $ nameserver entry) (T.unpack $ domain entry)
          let raw = fromMaybe ICMP (textToRawMode $ raw_mode entry)
          let pwd = T.unpack $ udp2raw_password entry
          writeEnv ip pwd raw
          start ip

              -- Clean up old socket if exists
          catch (removeFile socketPath) (\(_::SomeException) -> pure ())
          sock <- socket AF_UNIX Stream 0
          bind sock (SockAddrUnix socketPath)
          setFileMode socketPath 0o775
          listen sock 1

          forever $ do
            (conn, _) <- accept sock
            _ <- forkIO $ (handleClient conn ip)
            pure ()
        [] -> putStrLn "Configuration file was empty."

handleClient :: Socket
                -> String   -- ^ server IP (resolved at runtime)
                -> IO ()
handleClient conn ip = do
  h <- socketToHandle conn ReadWriteMode
  hSetBuffering h LineBuffering
  cmd <- hGetLine h
  case cmd of
    "start" -> do
      putStrLn "Received start command"
      start ip
      hPutStrLn h "started"
    "stop" -> do
      putStrLn "Received stop command"
      stop ip
      hPutStrLn h "stopped"
    "exit" -> do
      putStrLn "Exiting tproxy service"
      hPutStrLn h "bye"
      stop ip
      hClose h
      exitSuccess
    _ -> hPutStrLn h "unknown command"
  hClose h

start :: String       -- ^ server IP (resolved at runtime)
          -> IO ()
start ip = do
  applyRules
  applyICMP ip

stop :: String       -- ^ server IP (resolved at runtime)
          -> IO ()
stop ip = do
  clearRules
  clearICMP ip