/*
 * Copyright 2021 Juan Cruz Viotti
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import tap from 'tap'

import {
  readdirSync,
  readFileSync,
  statSync
} from 'fs'

import {
  resolve,
  basename
} from 'path'

import {
  JSONSchema,
  JSONValue,
  compileSchema,
  serialize,
  deserialize,
  Encoding
} from '../../lib'

import {
  validateSchema
} from '../../lib/schema'

import {
  preprocessSchema
} from '../../lib/preprocessor'

const SPECIFICATION: string = 'draft2020-12'

const ROOT_DIRECTORY: string = resolve(__dirname, '..', '..', '..')
const JSON_SCHEMA_TEST_SUITE: string =
  resolve(ROOT_DIRECTORY, 'vendor', 'json-schema-test-suite')
const JSON_SCHEMA_TESTS_PATH: string =
  resolve(JSON_SCHEMA_TEST_SUITE, 'tests', SPECIFICATION)

const recursiveReadDirectory = (filePath: string): string[] => {
  return readdirSync(filePath).reduce((accumulator: string[], element: string) => {
    const fullPath: string = resolve(filePath, element)
    return statSync(fullPath).isDirectory()
      ? accumulator.concat(recursiveReadDirectory(fullPath))
      : accumulator.concat(fullPath)
  }, [])
}

interface JSONSchemaTest {
  readonly description: string;
  readonly data: JSONValue;
  readonly valid: boolean;
}

interface JSONSchemaTestSuite {
  readonly description: string;
  readonly comment?: string;
  readonly schema: JSONSchema;
  readonly tests: JSONSchemaTest[];
}

for (const suitePath of recursiveReadDirectory(JSON_SCHEMA_TESTS_PATH)) {
  const name: string = basename(suitePath)

  if ([
    // TODO: These suites require resolving local files, which we do not do yet
    'refRemote.json',
    'ref.json',
    'id.json',
    'anchor.json',
    'unknownKeyword.json',

    // The top-level format suite expects formats to be treated
    // as only annotations
    'format.json',

    // TODO: Make these suites pass
    'dynamicRef.json',
    'float-overflow.json'
  ].includes(name)) {
    continue
  }

  const suites: JSONSchemaTestSuite[] = JSON.parse(readFileSync(suitePath, 'utf8'))
  for (const suite of suites) {
    for (const testCase of suite.tests) {
      if (!testCase.valid) {
        continue
      }

      tap.test(`${name} | ${suite.description} -> ${testCase.description}`, async (test) => {
        const schema: JSONSchema = await preprocessSchema(suite.schema)
        test.true(validateSchema(schema, testCase.data))

        const encoding: Encoding = await compileSchema(schema)
        const buffer: Buffer = serialize(encoding, testCase.data)
        const result: JSONValue = deserialize(encoding, buffer)
        test.strictSame(testCase.data, result)

        test.end()
      })
    }
  }
}
