module Test.Smoke.Spec.PathGenerator
  ( genAbsoluteFilePath,
    genRelativeDir,
    genRelativeFile,
    genRelativeFilePath,
    genNamedSegment,
  )
where

import qualified Data.List as List
import Hedgehog
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import qualified System.FilePath as FilePath
import Test.Smoke.Paths
import Test.Smoke.Spec.RootDirectory

data FilePathSegment
  = Current
  | Parent
  | Named String

genRelativeDir :: Range Int -> Gen (RelativePath Dir)
genRelativeDir segmentRange = parseDir <$> genRelativeFilePath segmentRange

genRelativeFile :: Range Int -> Gen (RelativePath File)
genRelativeFile segmentRange = parseFile <$> genRelativeFilePath segmentRange

genAbsoluteFilePath :: Range Int -> Gen FilePath
genAbsoluteFilePath segmentRange =
  FilePath.joinDrive rootDirectory <$> genRelativeFilePath segmentRange

genRelativeFilePath :: Range Int -> Gen FilePath
genRelativeFilePath segmentRange = do
  segments <-
    Gen.filter segmentsAreNotSubtractive $ Gen.list segmentRange genSegment
  let joined =
        List.intercalate [FilePath.pathSeparator] $ map segmentString segments
  return $ dropWhile (== FilePath.pathSeparator) joined

genSegment :: Gen FilePathSegment
genSegment =
  Gen.frequency
    [ (2, Gen.constant Current),
      (1, Gen.constant Parent),
      (5, Named <$> genNamedSegment)
    ]

genNamedSegment :: Gen FilePath
genNamedSegment = do
  name <- Gen.string (Range.linear 1 100) Gen.alphaNum
  trailingSeparators <-
    Gen.string (Range.linear 0 3) $ Gen.constant FilePath.pathSeparator
  return $ name <> trailingSeparators

segmentsAreNotSubtractive :: [FilePathSegment] -> Bool
segmentsAreNotSubtractive = (>= 0) . countSegments

countSegment :: FilePathSegment -> Int
countSegment Current = 0
countSegment Parent = -1
countSegment (Named _) = 1

countSegments :: [FilePathSegment] -> Int
countSegments = sum . map countSegment

segmentString :: FilePathSegment -> String
segmentString Current = "."
segmentString Parent = ".."
segmentString (Named value) = value
