/**
 * Contains the [[DateFolder]] class
 * This tracks data stored in dated folders and sub-folders
 *
 */
import * as debug from '../debug'
import { store } from 'solid-logic'
import * as ns from '../ns'
import * as $rdf from 'rdflib' // pull in first avoid cross-refs

export async function emptyLeaf (leafDocument) {
  await store.fetcher.load(leafDocument)
  // files can have seealso links. skip ones with no leafObjects with a date
  return !(
    store.statementsMatching(null, ns.dct('created'), null, leafDocument)
      .length > 0
  )
}

/**
 * Track back through the YYYY/MM/DD tree to find the previous/next day
 */
export class DateFolder {
  constructor (rootThing, leafFileName, membershipProperty) {
    this.root = rootThing
    this.rootFolder = rootThing.dir()
    this.leafFileName = leafFileName || 'index.ttl' // typically chat.ttl
    this.membershipProperty = membershipProperty || ns.wf('leafObject')
  }

  /* Generate the leaf document (rdf object) from date
   * @returns: <NamedNode> - document
   */
  leafDocumentFromDate (date) {
    // debug.log('incoming date: ' + date)
    const isoDate = date.toISOString() // Like "2018-05-07T17:42:46.576Z"
    let path = isoDate.split('T')[0].replace(/-/g, '/') //  Like "2018/05/07"
    path = this.root.dir().uri + path + '/' + this.leafFileName
    return store.sym(path)
  }

  /* Generate a date object from the leaf file name
   */
  dateFromLeafDocument (doc) {
    const head = this.rootFolder.uri.length
    const str = doc.uri.slice(head, head + 10).replace(/\//g, '-')
    // let date = new Date(str + 'Z') // GMT - but fails in FF - invalid format :-(
    const date = new Date(str) // not explicitly UTC but is assumed so in spec
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse
    // debug.log('Date for ' + doc + ':' + date.toISOString())
    return date
  }

  async loadPrevious (date, backwards) {
    async function previousPeriod (file, level) {
      function younger (x) {
        if (backwards ? x.uri >= file.uri : x.uri <= file.uri) return false // later than we want or same -- looking for different
        return true
      }
      function suitable (x) {
        const tail = x.uri
          .slice(0, -1)
          .split('/')
          .slice(-1)[0]
        if (!'0123456789'.includes(tail[0])) return false // not numeric
        return true
      }

      function lastOrFirst (siblings) {
        siblings = siblings.filter(suitable)
        siblings.sort() // chronological order
        if (!backwards) siblings.reverse()
        return siblings.pop() // date folder
      }
      // debug.log('  previousPeriod level' + level + ' file ' + file)
      const parent = file.dir()
      try {
        await store.fetcher.load(parent)
        let siblings = store.each(parent, ns.ldp('contains'))
        siblings = siblings.filter(younger)
        const folder = lastOrFirst(siblings)
        if (folder) return folder
        // debug.log(' parent no suitable offspring ' + parent)
      } catch (err) {
        if (err.response && err.response.status && err.response.status === 404) {
          debug.log('Error 404 for chat parent file ' + parent)
        } else {
          debug.log('*** Error NON 404 for chat parent file ' + parent)
          // statusTR.appendChild(widgets.errorMessageBlock(dom, err, 'pink'))
          throw (new Error(`*** ${err.message} for chat folder ${parent}`))
        }
      }
      if (level === 0) {
        // debug.log('loadPrevious:  returning as level is zero')
        return null // 3:day, 2:month, 1: year  0: no
      }

      const uncle = await previousPeriod(parent, level - 1)
      if (!uncle) {
        // debug.log('   previousPeriod: nothing left before. ', parent)
        return null // reached first ever
      }
      await store.fetcher.load(uncle)
      const cousins = store.each(uncle, ns.ldp('contains'))
      const result = lastOrFirst(cousins)
      // debug.log('   previousPeriod: returning cousins at level ' + level, cousins)
      // debug.log('   previousPeriod: returning result at level ' + level, result)

      return result
    } // previousPeriod

    let folder = this.leafDocumentFromDate(date).dir()
    while (true) {
      const found = await previousPeriod(folder, 3)
      if (found) {
        const leafDocument = store.sym(found.uri + this.leafFileName)
        const nextDate = this.dateFromLeafDocument(leafDocument)
        if (!await emptyLeaf(leafDocument)) {
          return nextDate
        } else {
          // debug.log('  loadPrevious: skipping empty ' + leafDocument)
          date = nextDate
          folder = this.leafDocumentFromDate(date).dir()
          // debug.log('    loadPrevious: moved back to ' + folder)
        }
      } else {
        return null // no more left
      }
    }
  } // loadPrevious

  async firstLeaf (backwards) {
    // backwards -> last leafObject
    const folderStore = $rdf.graph()
    const folderFetcher = new $rdf.Fetcher(folderStore)
    async function earliestSubfolder (parent) {
      function suitable (x) {
        const tail = x.uri
          .slice(0, -1)
          .split('/')
          .slice(-1)[0]
        if (!'0123456789'.includes(tail[0])) return false // not numeric
        return true
      }
      // debug.log('            parent ' + parent)
      delete folderFetcher.requested[parent.uri]
      // try {
      await folderFetcher.load(parent, { force: true }) // Force fetch as will have changed
      // }catch (err) {
      // }

      let kids = folderStore.each(parent, ns.ldp('contains'))
      kids = kids.filter(suitable)
      if (kids.length === 0) {
        throw new Error(' @@@  No children to         parent2 ' + parent)
      }

      kids.sort()
      if (backwards) kids.reverse()
      return kids[0]
    }
    const y = await earliestSubfolder(this.root.dir())
    const month = await earliestSubfolder(y)
    const d = await earliestSubfolder(month)
    const leafDocument = $rdf.sym(d.uri + 'chat.ttl')
    await folderFetcher.load(leafDocument)
    const leafObjects = folderStore.each(
      this.root,
      this.membershipProperty,
      null,
      leafDocument
    )
    if (leafObjects.length === 0) {
      const msg =
        '  INCONSISTENCY -- no chat leafObject in file ' + leafDocument
      debug.trace(msg)
      throw new Error(msg)
    }
    const sortMe = leafObjects.map(leafObject => [
      folderStore.any(leafObject, ns.dct('created')),
      leafObject
    ])
    sortMe.sort()
    if (backwards) sortMe.reverse()
    /* debug.log(
      (backwards ? 'Latest' : 'Earliest') + ' leafObject is ' + sortMe[0][1]
    ) */
    return sortMe[0][1]
  } // firstleafObject
} // class
