import { test } from '@japa/runner'
import { assert } from '@esm-bundle/chai'
import { Kind as ProjectRequestKind, ProjectRequest, IProjectRequest } from '../../../src/models/ProjectRequest.js'
import { HttpProject } from '../../../src/models/HttpProject.js'
import { Kind as HttpRequestKind, IHttpRequest } from '../../../src/models/HttpRequest.js'
import { Kind as RequestLogKind, RequestLog } from '../../../src/models/RequestLog.js'
import { SentRequest } from '../../../src/models/SentRequest.js'
import { RequestConfig } from '../../../src/models/RequestConfig.js'
import { RequestAuthorization } from '../../../src/models/RequestAuthorization.js'
import { Request, Kind as RequestKind } from '../../../src/models/Request.js'
import { ProjectFolderKind, ThingKind } from '../../../src/models/kinds.js'

test.group('ProjectRequest - Default project initialization', () => {
  test('initializes a default project request', () => {
    const project = new HttpProject()
    const result = new ProjectRequest(project)
    assert.equal(result.kind, ProjectRequestKind, 'sets the kind property')
    assert.typeOf(result.created, 'number', 'sets the created property')
    assert.equal(result.updated, result.created, 'sets the updated property')
    assert.typeOf(result.midnight, 'number', 'sets the updated property')
    assert.typeOf(result.key, 'string', 'sets the key property')
    assert.typeOf(result.info, 'EventTarget', 'sets the info property')
    assert.typeOf(result.expects, 'object', 'sets the expects property')
    assert.isUndefined(result.log, 'does not set the log property')
    assert.isUndefined(result.config, 'does not set the config property')
    assert.isUndefined(result.authorization, 'does not set the authorization property')
    assert.isUndefined(result.flows, 'does not set the flows property')
    assert.isUndefined(result.clientCertificate, 'does not set the clientCertificate property')
    assert.isUndefined(result.environment, 'does not set the environment property')
  })
})

test.group('ProjectRequest - From schema initialization', () => {
  test('sets the kind property', () => {
    const project = new HttpProject()
    const base: IProjectRequest = {
      kind: ProjectRequestKind,
      info: { kind: ThingKind, name: '' },
      key: '',
      expects: { kind: HttpRequestKind, method: '', url: '' },
    }
    const init: IProjectRequest = { ...base }
    const request = new ProjectRequest(project, init)
    assert.equal(request.kind, ProjectRequestKind)
  })

  test('sets the key property when missing', () => {
    const project = new HttpProject()
    const base: IProjectRequest = {
      kind: ProjectRequestKind,
      info: { kind: ThingKind, name: '' },
      key: '',
      expects: { kind: HttpRequestKind, method: '', url: '' },
    }
    const init: IProjectRequest = { ...base }
    // @ts-expect-error Testing missing properties
    delete init.key
    const request = new ProjectRequest(project, init)
    assert.typeOf(request.key, 'string')
    assert.isNotEmpty(request.key)
  })

  test('sets the key property', () => {
    const project = new HttpProject()
    const base: IProjectRequest = {
      kind: ProjectRequestKind,
      info: { kind: ThingKind, name: '' },
      key: '',
      expects: { kind: HttpRequestKind, method: '', url: '' },
    }
    const init: IProjectRequest = { ...base, key: 'test' }
    const request = new ProjectRequest(project, init)
    assert.equal(request.key, 'test')
  })

  test('sets the expects property when missing', () => {
    const project = new HttpProject()
    const base: IProjectRequest = {
      kind: ProjectRequestKind,
      info: { kind: ThingKind, name: '' },
      key: '',
      expects: { kind: HttpRequestKind, method: '', url: '' },
    }
    const init: IProjectRequest = { ...base }
    // @ts-expect-error Testing missing properties
    delete init.expects
    const request = new ProjectRequest(project, init)
    assert.typeOf(request.expects, 'object')
    assert.equal(request.expects.method, 'GET')
    assert.equal(request.expects.url, '')
  })

  test('sets the expects property', () => {
    const project = new HttpProject()
    const base: IProjectRequest = {
      kind: ProjectRequestKind,
      info: { kind: ThingKind, name: '' },
      key: '',
      expects: { kind: HttpRequestKind, method: '', url: '' },
    }
    const init: IProjectRequest = {
      ...base,
      expects: { kind: HttpRequestKind, url: 'test.com', method: 'GET' },
    }
    const request = new ProjectRequest(project, init)
    assert.typeOf(request.expects, 'object')
    assert.equal(request.expects.method, 'GET')
    assert.equal(request.expects.url, 'test.com')
  })

  test('sets the info property when missing', () => {
    const project = new HttpProject()
    const base: IProjectRequest = {
      kind: ProjectRequestKind,
      info: { kind: ThingKind, name: '' },
      key: '',
      expects: { kind: HttpRequestKind, method: '', url: '' },
    }
    const init: IProjectRequest = { ...base }
    // @ts-expect-error Testing missing properties
    delete init.info
    const request = new ProjectRequest(project, init)
    assert.typeOf(request.info, 'EventTarget')
    assert.equal(request.info.name, '')
  })

  test('sets the info property', () => {
    const project = new HttpProject()
    const base: IProjectRequest = {
      kind: ProjectRequestKind,
      info: { kind: ThingKind, name: '' },
      key: '',
      expects: { kind: HttpRequestKind, method: '', url: '' },
    }
    const init: IProjectRequest = { ...base, info: { kind: ThingKind, name: 'A request' } }
    const request = new ProjectRequest(project, init)
    assert.typeOf(request.info, 'EventTarget')
    assert.equal(request.info.name, 'A request')
  })

  test('sets the log property', () => {
    const project = new HttpProject()
    const base: IProjectRequest = {
      kind: ProjectRequestKind,
      info: { kind: ThingKind, name: '' },
      key: '',
      expects: { kind: HttpRequestKind, method: '', url: '' },
    }
    const sentRequest = SentRequest.fromBaseValues({
      url: 'test.com',
      startTime: Date.now(),
    })
    const log = RequestLog.fromRequest(sentRequest.toJSON())
    const init: IProjectRequest = { ...base, log: log.toJSON() }
    const request = new ProjectRequest(project, init)
    assert.typeOf(request.log, 'object')
    assert.equal(request.log!.kind, RequestLogKind)
    assert.typeOf(request.log!.request, 'object')
    assert.equal(request.log!.request!.url, 'test.com')
  })

  test('sets the config property', () => {
    const project = new HttpProject()
    const base: IProjectRequest = {
      kind: ProjectRequestKind,
      info: { kind: ThingKind, name: '' },
      key: '',
      expects: { kind: HttpRequestKind, method: '', url: '' },
    }
    const config = RequestConfig.withDefaults()
    const init: IProjectRequest = { ...base, config: config.toJSON() }
    const request = new ProjectRequest(project, init)
    assert.typeOf(request.config, 'object')
    assert.isTrue(request.config!.enabled)
    assert.equal(request.config!.timeout, 90)
  })

  test('sets the authorization property', () => {
    const project = new HttpProject()
    const base: IProjectRequest = {
      kind: ProjectRequestKind,
      info: { kind: ThingKind, name: '' },
      key: '',
      expects: { kind: HttpRequestKind, method: '', url: '' },
    }
    const authorization = new RequestAuthorization()
    const init: IProjectRequest = { ...base, authorization: [authorization.toJSON()] }
    const request = new ProjectRequest(project, init)
    assert.typeOf(request.authorization, 'array')
    assert.lengthOf(request.authorization!, 1)
  })

  test('sets the created property', () => {
    const project = new HttpProject()
    const base: IProjectRequest = {
      kind: ProjectRequestKind,
      info: { kind: ThingKind, name: '' },
      key: '',
      expects: { kind: HttpRequestKind, method: '', url: '' },
    }
    const init: IProjectRequest = { ...base, created: 123 }
    const request = new ProjectRequest(project, init)
    assert.equal(request.created, 123)
  })

  test('sets the default created property', () => {
    const project = new HttpProject()
    const base: IProjectRequest = {
      kind: ProjectRequestKind,
      info: { kind: ThingKind, name: '' },
      key: '',
      expects: { kind: HttpRequestKind, method: '', url: '' },
    }
    const init: IProjectRequest = { ...base }
    const request = new ProjectRequest(project, init)
    assert.typeOf(request.created, 'number')
  })

  test('sets the updated property', () => {
    const project = new HttpProject()
    const base: IProjectRequest = {
      kind: ProjectRequestKind,
      info: { kind: ThingKind, name: '' },
      key: '',
      expects: { kind: HttpRequestKind, method: '', url: '' },
    }
    const init: IProjectRequest = { ...base, updated: 123 }
    const request = new ProjectRequest(project, init)
    assert.equal(request.updated, 123)
  })

  test('sets the default updated property', () => {
    const project = new HttpProject()
    const base: IProjectRequest = {
      kind: ProjectRequestKind,
      info: { kind: ThingKind, name: '' },
      key: '',
      expects: { kind: HttpRequestKind, method: '', url: '' },
    }
    const init: IProjectRequest = { ...base }
    const request = new ProjectRequest(project, init)
    assert.equal(request.updated, request.created)
  })

  test('sets the midnight property', () => {
    const project = new HttpProject()
    const base: IProjectRequest = {
      kind: ProjectRequestKind,
      info: { kind: ThingKind, name: '' },
      key: '',
      expects: { kind: HttpRequestKind, method: '', url: '' },
    }
    const init: IProjectRequest = { ...base, midnight: 123 }
    const request = new ProjectRequest(project, init)
    assert.equal(request.midnight, 123)
  })

  test('sets the default midnight property', () => {
    const project = new HttpProject()
    const base: IProjectRequest = {
      kind: ProjectRequestKind,
      info: { kind: ThingKind, name: '' },
      key: '',
      expects: { kind: HttpRequestKind, method: '', url: '' },
    }
    const now = new Date()
    const init: IProjectRequest = { ...base, updated: now.getTime() }
    const request = new ProjectRequest(project, init)
    now.setHours(0, 0, 0, 0)
    assert.equal(request.midnight, now.getTime())
  })

  test('sets the environment property', () => {
    const project = new HttpProject()
    const base: IProjectRequest = {
      kind: ProjectRequestKind,
      info: { kind: ThingKind, name: '' },
      key: '',
      expects: { kind: HttpRequestKind, method: '', url: '' },
    }
    const env = project.addEnvironment('test')
    const init: IProjectRequest = { ...base, environment: env.key }
    const request = new ProjectRequest(project, init)
    assert.equal(request.environment, env.key)
  })
})

test.group('ProjectRequest - From JSON string initialization', () => {
  test('restores project data from JSON string', () => {
    const project = new HttpProject()
    const request = project.addRequest('https://api.com')
    request.environment = 'test'
    const str = JSON.stringify(request)

    const result = new ProjectRequest(project, str)

    assert.equal(result.key, request.key, 'restores the key')
    assert.equal(result.info.name, 'https://api.com', 'restores the info object')
    assert.equal(result.expects.url, 'https://api.com', 'restores the expects object')
    assert.equal(result.environment, 'test', 'restores the environment object')
  })
})

test.group('ProjectRequest.fromUrl()', () => {
  test('sets the request values', () => {
    const url = 'https://api.com'
    const project = new HttpProject()
    const request = ProjectRequest.fromUrl(url, project)
    const { expects } = request
    assert.equal(expects.url, url, 'sets the url')
    assert.equal(expects.kind, HttpRequestKind, 'sets the kind')
    assert.equal(expects.method, 'GET', 'sets the HTTP method')
  })

  test('sets the info values', () => {
    const url = 'https://api.com'
    const project = new HttpProject()
    const request = ProjectRequest.fromUrl(url, project)
    const { info } = request
    assert.equal(info.name, url, 'sets the name')
    assert.equal(info.kind, ThingKind, 'sets the kind')
  })

  test('sets request meta', () => {
    const url = 'https://api.com'
    const project = new HttpProject()
    const request = ProjectRequest.fromUrl(url, project)
    assert.typeOf(request.key, 'string', 'has the key')
    assert.equal(request.kind, ProjectRequestKind, 'sets the kind')
    assert.typeOf(request.created, 'number', 'sets the created')
    assert.equal(request.updated, request.created, 'sets the updated')
  })

  test('throws when the project is missing', () => {
    const url = 'https://api.com'
    assert.throws(() => {
      ProjectRequest.fromUrl(url)
    }, 'The project is required.')
  })
})

test.group('ProjectRequest.fromName()', () => {
  test('sets the request values', () => {
    const name = 'a request'
    const project = new HttpProject()
    const request = ProjectRequest.fromName(name, project)
    const { expects } = request
    assert.equal(expects.url, '', 'sets the empty url')
    assert.equal(expects.kind, HttpRequestKind, 'sets the kind')
    assert.equal(expects.method, 'GET', 'sets the HTTP method')
  })

  test('sets the info values', () => {
    const name = 'a request'
    const project = new HttpProject()
    const request = ProjectRequest.fromName(name, project)
    const { info } = request
    assert.equal(info.name, name, 'sets the name')
    assert.equal(info.kind, ThingKind, 'sets the kind')
  })

  test('sets request meta', () => {
    const name = 'a request'
    const project = new HttpProject()
    const request = ProjectRequest.fromName(name, project)
    assert.typeOf(request.key, 'string', 'has the key')
    assert.equal(request.kind, ProjectRequestKind, 'sets the kind')
    assert.typeOf(request.created, 'number', 'sets the created')
    assert.equal(request.updated, request.created, 'sets the updated')
  })

  test('throws when the project is missing', () => {
    const name = 'a request'
    assert.throws(() => {
      ProjectRequest.fromName(name)
    }, 'The project is required.')
  })
})

test.group('ProjectRequest.fromHttpRequest()', () => {
  test('sets the request values', () => {
    const project = new HttpProject()
    const iRequest: IHttpRequest = {
      kind: HttpRequestKind,
      method: 'PUT',
      url: 'https://api.com',
      headers: 'x-test: true',
      payload: 'something',
    }
    const request = ProjectRequest.fromHttpRequest(iRequest, project)
    const { expects } = request
    assert.equal(expects.url, iRequest.url, 'sets the empty url')
    assert.equal(expects.kind, HttpRequestKind, 'sets the kind')
    assert.equal(expects.method, iRequest.method, 'sets the HTTP method')
    assert.equal(expects.headers, iRequest.headers, 'sets the headers')
    assert.equal(expects.payload, iRequest.payload, 'sets the payload')
  })

  test('sets the info values', () => {
    const project = new HttpProject()
    const iRequest: IHttpRequest = {
      kind: HttpRequestKind,
      method: 'PUT',
      url: 'https://api.com',
      headers: 'x-test: true',
      payload: 'something',
    }
    const request = ProjectRequest.fromHttpRequest(iRequest, project)
    const { info } = request
    assert.equal(info.name, iRequest.url, 'sets the name')
    assert.equal(info.kind, ThingKind, 'sets the kind')
  })

  test('sets request meta', () => {
    const project = new HttpProject()
    const iRequest: IHttpRequest = {
      kind: HttpRequestKind,
      method: 'PUT',
      url: 'https://api.com',
      headers: 'x-test: true',
      payload: 'something',
    }
    const request = ProjectRequest.fromHttpRequest(iRequest, project)
    assert.typeOf(request.key, 'string', 'has the key')
    assert.equal(request.kind, ProjectRequestKind, 'sets the kind')
    assert.typeOf(request.created, 'number', 'sets the created')
    assert.equal(request.updated, request.created, 'sets the updated')
  })

  test('throws when the project is missing', () => {
    const iRequest: IHttpRequest = {
      kind: HttpRequestKind,
      method: 'PUT',
      url: 'https://api.com',
      headers: 'x-test: true',
      payload: 'something',
    }
    assert.throws(() => {
      ProjectRequest.fromHttpRequest(iRequest)
    }, 'The project is required.')
  })
})

test.group('ProjectRequest.fromRequest()', () => {
  test('sets the request values', () => {
    const project = new HttpProject()
    const httpRequest: IHttpRequest = {
      kind: RequestKind,
      method: 'PUT',
      url: 'https://api.com',
      headers: 'x-test: true',
      payload: 'something',
    }
    const r = Request.fromHttpRequest(httpRequest)
    r.info.name = 'a name'
    const iRequest = r.toJSON()
    const request = ProjectRequest.fromRequest(iRequest, project)
    const { expects } = request
    assert.equal(expects.url, iRequest.expects.url, 'sets the empty url')
    assert.equal(expects.kind, HttpRequestKind, 'sets the kind')
    assert.equal(expects.method, iRequest.expects.method, 'sets the HTTP method')
    assert.equal(expects.headers, iRequest.expects.headers, 'sets the headers')
    assert.equal(expects.payload, iRequest.expects.payload, 'sets the payload')
  })

  test('sets the info values', () => {
    const project = new HttpProject()
    const httpRequest: IHttpRequest = {
      kind: RequestKind,
      method: 'PUT',
      url: 'https://api.com',
      headers: 'x-test: true',
      payload: 'something',
    }
    const r = Request.fromHttpRequest(httpRequest)
    r.info.name = 'a name'
    const iRequest = r.toJSON()
    const request = ProjectRequest.fromRequest(iRequest, project)
    const { info } = request
    assert.equal(info.name, 'a name', 'sets the name')
    assert.equal(info.kind, ThingKind, 'sets the kind')
  })

  test('sets request meta', () => {
    const project = new HttpProject()
    const httpRequest: IHttpRequest = {
      kind: RequestKind,
      method: 'PUT',
      url: 'https://api.com',
      headers: 'x-test: true',
      payload: 'something',
    }
    const r = Request.fromHttpRequest(httpRequest)
    r.info.name = 'a name'
    const iRequest = r.toJSON()
    const request = ProjectRequest.fromRequest(iRequest, project)
    assert.typeOf(request.key, 'string', 'has the key')
    assert.equal(request.kind, ProjectRequestKind, 'sets the kind')
    assert.typeOf(request.created, 'number', 'sets the created')
    assert.equal(request.updated, request.created, 'sets the updated')
  })
})

test.group('ProjectRequest getParent()', () => {
  test('returns the project object', () => {
    const project = new HttpProject()
    const r = project.addRequest('https://api.com')
    const result = r.getParent()
    assert.isTrue(result === project)
  })

  test('returns the folder object', () => {
    const project = new HttpProject()
    const folder = project.addFolder('a folder')
    const r = folder.addRequest('https://api.com')
    const result = r.getParent()
    assert.isTrue(result === folder)
  })
})

test.group('ProjectRequest getProject()', () => {
  test('returns the project when added to the project', () => {
    const project = new HttpProject()
    const r = project.addRequest('https://api.com')
    const result = r.getProject()
    assert.isTrue(result === project)
  })

  test('returns the project when added to a folder', () => {
    const project = new HttpProject()
    const folder = project.addFolder('a folder')
    const r = folder.addRequest('https://api.com')
    const result = r.getProject()
    assert.isTrue(result === project)
  })
})

test.group('ProjectRequest remove()', () => {
  test('removes from a project root', () => {
    const project = new HttpProject()
    const r = project.addRequest('https://api.com')
    r.remove()
    assert.deepEqual(project.items, [])
    assert.deepEqual(project.definitions.requests, [])
  })

  test('removes from a folder', () => {
    const project = new HttpProject()
    const folder = project.addFolder('a folder')
    const r = folder.addRequest('https://api.com')
    r.remove()
    assert.deepEqual(folder.items, [], 'folder has no items')
    assert.lengthOf(project.items, 1, 'projects has a single item')
    assert.lengthOf(
      project.items.filter((i) => i.kind === ProjectFolderKind),
      1,
      'projects has only folder items'
    )
    assert.lengthOf(project.definitions.requests, 0, 'projects has no request definitions')
  })
})

test.group('ProjectRequest clone() - project root', () => {
  test('clones with defaults from a project root', () => {
    const project = new HttpProject()
    const r = project.addRequest('https://api.com')
    const result = r.clone()
    assert.typeOf(result.key, 'string', 'has the key')
    assert.notEqual(result.key, r.key, 'has a different key')
    assert.lengthOf(project.items, 2, 'project has a new item')
    assert.isTrue(
      project.items.some((i) => i.key === result.key),
      'the project has the new item'
    )
    assert.lengthOf(project.definitions.requests, 2, 'project has a new definition')
    assert.isTrue(
      project.definitions.requests.some((i) => i.key === result.key),
      'the project has the new definition'
    )
  })

  test('does not change the key', () => {
    const project = new HttpProject()
    const r = project.addRequest('https://api.com')
    const result = r.clone({ withoutRevalidate: true })
    assert.equal(result.key, r.key)
  })

  test('does not attach to the project', () => {
    const project = new HttpProject()
    const r = project.addRequest('https://api.com')
    const result = r.clone({ withoutAttach: true })

    assert.lengthOf(project.items, 1, 'project has no new items')
    assert.isFalse(
      project.items.some((i) => i.key === result.key),
      'the project has the new item'
    )
    assert.lengthOf(project.definitions.requests, 1, 'project has no new definitions')
    assert.isFalse(
      project.definitions.requests.some((i) => i.key === result.key),
      'the project has no new definitions'
    )
  })

  test('copies a request from one project to another', () => {
    const project = new HttpProject()
    const r = project.addRequest('https://api.com')
    const result = r.clone({ withoutAttach: true })
    const target = new HttpProject()
    target.addRequest(result)
    assert.isTrue(result.getProject() === target, 'the request has a new project')
    assert.lengthOf(target.items, 1)
    assert.lengthOf(target.definitions.requests, 1)
  })
})

test.group('ProjectRequest clone() - folder root', () => {
  test('clones with defaults from a folder root', () => {
    const project = new HttpProject()
    const folder = project.addFolder('a folder')
    const request = folder.addRequest('https://api.com')
    const result = request.clone()
    assert.typeOf(result.key, 'string', 'has the key')
    assert.notEqual(result.key, request.key, 'has a different key')
    assert.lengthOf(project.items, 1, 'project has still a single item')
    assert.lengthOf(folder.items, 2, 'folder has a new item')
    assert.isTrue(
      folder.items.some((i) => i.key === result.key),
      'the folder has the new item'
    )
    assert.lengthOf(project.definitions.requests, 2, 'project has a new definition')
    assert.isTrue(
      project.definitions.requests.some((i) => i.key === result.key),
      'the project has the new definition'
    )
  })

  test('copies a request from one project to another', () => {
    const project = new HttpProject()
    const folder = project.addFolder('a folder')
    const request = folder.addRequest('https://api.com')
    const result = request.clone({ withoutAttach: true })
    const target = new HttpProject()
    target.addRequest(result)
    assert.isTrue(result.getProject() === target, 'the request has a new project')
    assert.lengthOf(target.items, 1)
    assert.lengthOf(target.definitions.requests, 1)
  })
})

test.group('ProjectRequest.clone()', () => {
  test('clones the request', () => {
    const project = new HttpProject()
    const request = project.addRequest('https://api.com')
    const result = ProjectRequest.clone(request.toJSON(), project)

    assert.typeOf(result.key, 'string', 'has the key')
    assert.notEqual(result.key, request.key, 'has a different key')
    assert.lengthOf(project.items, 2, 'project has a new item')
    assert.isTrue(
      project.items.some((i) => i.key === result.key),
      'the project has the new item'
    )
    assert.lengthOf(project.definitions.requests, 2, 'project has a new definition')
    assert.isTrue(
      project.definitions.requests.some((i) => i.key === result.key),
      'the project has the new definition'
    )
  })
})

test.group('ProjectRequest getEnvironment()', () => {
  test('returns project associated with the request', () => {
    const project = new HttpProject()
    const request = project.addRequest('https://api.com')
    const env = project.addEnvironment('test')
    request.environment = env.key
    const result = request.getEnvironment()
    assert.equal(result, env)
  })

  test('returns undefined when unknown key', () => {
    const project = new HttpProject()
    const request = project.addRequest('https://api.com')
    project.addEnvironment('test')
    request.environment = 'test'
    const result = request.getEnvironment()
    assert.isUndefined(result)
  })

  test('returns undefined when no key', () => {
    const project = new HttpProject()
    const request = project.addRequest('https://api.com')
    const result = request.getEnvironment()
    assert.isUndefined(result)
  })
})

test.group('ProjectRequest readEnvironments()', () => {
  test('returns the list for root environments', () => {
    const project = new HttpProject()
    const request = project.addRequest('https://api.com')
    const env = project.addEnvironment('test')
    const result = request.readEnvironments()
    assert.deepEqual(result, [env])
  })

  test('returns empty array when no root environments', () => {
    const project = new HttpProject()
    const request = project.addRequest('https://api.com')
    const result = request.readEnvironments()
    assert.deepEqual(result, [])
  })

  test('returns empty array when no folder environments', () => {
    const project = new HttpProject()
    const f1 = project.addFolder('f1')
    const r1 = f1.addRequest('r1')
    const result = r1.readEnvironments()
    assert.deepEqual(result, [])
  })

  test('returns folder environments', () => {
    const project = new HttpProject()
    const f1 = project.addFolder('f1')
    const env = f1.addEnvironment('test')
    const r1 = f1.addRequest('r1')
    const result = r1.readEnvironments()
    assert.deepEqual(result, [env])
  })

  test('returns folder and root environments', () => {
    const project = new HttpProject()
    const f1 = project.addFolder('f1')
    const e1 = f1.addEnvironment('e1')
    const e2 = project.addEnvironment('e2')
    const r1 = f1.addRequest('r1')
    const result = r1.readEnvironments()
    assert.deepEqual(result, [e2, e1])
  })
})

test.group('ProjectRequest readUrl()', () => {
  test('reads the URL from the request only when no environments and default options', () => {
    const project = new HttpProject()
    const request = project.addRequest('https://api.com/v1/endpoint')
    const result = request.readUrl()
    assert.equal(result, request.expects.url)
  })

  test('returns undefined when no environments and baseUrl option', () => {
    const project = new HttpProject()
    const request = project.addRequest('https://api.com/v1/endpoint')
    const result = request.readUrl({ baseUrl: true })
    assert.isUndefined(result)
  })

  test('returns URL when no variables option is set', () => {
    const project = new HttpProject()
    const request = project.addRequest('https://api.com/v1/{endpoint}')
    const result = request.readUrl({ noVariables: true })
    assert.equal(result, 'https://api.com/v1/{endpoint}')
  })

  test('applies variables from a single environment', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    e1.addVariable('var1', 'test')
    const request = project.addRequest('https://api.com/v1/{?var1}')
    const result = request.readUrl()
    assert.equal(result, 'https://api.com/v1/?var1=test')
  })

  test('does not apply base URL to an absolute URL', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    e1.addServer('http://base.com')
    const request = project.addRequest('https://api.com/v1/endpoint')
    const result = request.readUrl()
    assert.equal(result, request.expects.url)
  })

  test('adds base URL to a relative URL', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    e1.addServer('http://base.com')
    const request = project.addRequest('/v1/endpoint')
    const result = request.readUrl()
    assert.equal(result, 'http://base.com/v1/endpoint')
  })

  test('adds base URL when there is no scheme delimiter', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    e1.addServer('http://base.com/v1')
    const request = project.addRequest('endpoint')
    const result = request.readUrl()
    assert.equal(result, 'http://base.com/v1/endpoint')
  })

  test('applies variables to the base URI in the server', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    e1.addServer('http://{host}/v1')
    e1.addVariable('host', 'base.com')
    const request = project.addRequest('endpoint')
    const result = request.readUrl()
    assert.equal(result, 'http://base.com/v1/endpoint')
  })

  test('inherits variables in a folder', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    e1.addServer('http://{host}/v1')
    e1.addVariable('host', 'base.com')
    e1.addVariable('path', 'endpoint')
    const f1 = project.addFolder('f1')
    const request = f1.addRequest('{path}')
    const result = request.readUrl()
    assert.equal(result, 'http://base.com/v1/endpoint')
  })

  test('mixes in inherited variables and ones defined in a folder', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    e1.addServer('http://{host}/v1')
    e1.addVariable('host', 'base.com')
    const f1 = project.addFolder('f1')
    const e2 = f1.addEnvironment('e2')
    e2.addVariable('path', 'endpoint')
    const request = f1.addRequest('{path}')
    const result = request.readUrl()
    assert.equal(result, 'http://base.com/v1/endpoint')
  })

  test('folder variables override parent variables', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    e1.addServer('http://{host}/v1')
    e1.addVariable('host', 'base.com')
    e1.addVariable('path', 'other')
    const f1 = project.addFolder('f1')
    const e2 = f1.addEnvironment('e2')
    e2.addVariable('path', 'endpoint')
    const request = f1.addRequest('{path}')
    const result = request.readUrl()
    assert.equal(result, 'http://base.com/v1/endpoint')
  })

  test('folder server overrides parent server', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    e1.addServer('http://{host}/v1')
    e1.addVariable('host', 'base.com')
    e1.addVariable('path', 'other')
    const f1 = project.addFolder('f1')
    const e2 = f1.addEnvironment('e2')
    e2.addServer('http://alternative.pl/')
    e2.addVariable('path', 'endpoint')
    const request = f1.addRequest('{path}')
    const result = request.readUrl()
    assert.equal(result, 'http://alternative.pl/endpoint')
  })

  test('ignores variables when configured', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    e1.addServer('http://{host}/v1')
    e1.addVariable('host', 'base.com')
    e1.addVariable('path', 'other')
    const f1 = project.addFolder('f1')
    const e2 = f1.addEnvironment('e2')
    e2.addVariable('path', 'endpoint')
    const request = f1.addRequest('{path}')
    const result = request.readUrl({ noVariables: true })
    assert.equal(result, 'http://{host}/v1/{path}')
  })

  test('ignores variables when configured but keeps the full server', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    e1.addServer('http://{host}/v1')
    e1.addVariable('host', 'base.com')
    e1.addVariable('path', 'other')
    const f1 = project.addFolder('f1')
    const e2 = f1.addEnvironment('e2')
    e2.addServer('http://alternative.pl/')
    e2.addVariable('path', 'endpoint')
    const request = f1.addRequest('{path}')
    const result = request.readUrl({ noVariables: true })
    assert.equal(result, 'http://alternative.pl/{path}')
  })

  test('stops searching for variables on the selected environment', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    e1.addVariable('v1', 'a1')
    const f1 = project.addFolder('f1')
    const e2 = f1.addEnvironment('e2')
    e2.addVariable('v2', 'a2')
    const e3 = f1.addEnvironment('e3')
    e3.addVariable('v2', 'a3')
    const request = f1.addRequest('/{v1}/{v2}')
    request.environment = e2.key
    const result1 = request.readUrl()
    assert.equal(result1, '/a1/a2')
    request.environment = e3.key
    const result2 = request.readUrl()
    assert.equal(result2, '/a1/a3')
  })

  test('applies servers from a selected environment', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    e1.addServer('http://first.com')
    const f1 = project.addFolder('f1')
    const e2 = f1.addEnvironment('e2')
    e2.addServer('http://second.com')
    const e3 = f1.addEnvironment('e3')
    e3.addServer('http://third.com')
    const request = f1.addRequest('/api')
    request.environment = e2.key
    const result1 = request.readUrl()
    assert.equal(result1, 'http://second.com/api')
    request.environment = e3.key
    const result2 = request.readUrl()
    assert.equal(result2, 'http://third.com/api')
  })

  test('returns base URI only for a folder environment', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    e1.addServer('http://first.com')
    const f1 = project.addFolder('f1')
    const e2 = f1.addEnvironment('e2')
    e2.addServer('http://second.com')
    const request = f1.addRequest('/api')
    const result = request.readUrl({ baseUrl: true })
    assert.equal(result, 'http://second.com')
  })

  test('returns inherited base URI only', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    e1.addServer('http://first.com')
    const f1 = project.addFolder('f1')
    const request = f1.addRequest('/api')
    const result = request.readUrl({ baseUrl: true })
    assert.equal(result, 'http://first.com')
  })
})

test.group('ProjectRequest readAuthorization()', () => {
  test('returns empty array when no authorization is defined', () => {
    const project = new HttpProject()
    const r1 = project.addRequest('r1')
    const result = r1.readAuthorization()
    assert.deepEqual(result, [])
  })

  test('returns request authorization', () => {
    const project = new HttpProject()
    const authorization = new RequestAuthorization()
    const r1 = project.addRequest('r1')
    r1.authorization = [authorization]
    const result = r1.readAuthorization()
    assert.deepEqual(result, [authorization])
  })

  test('returns environment authorization', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    const authorization = new RequestAuthorization()
    e1.authorization = [authorization]
    const r1 = project.addRequest('r1')
    r1.environment = e1.key
    const result = r1.readAuthorization()
    assert.deepEqual(result, [authorization])
  })

  test('returns request authorization and not environment authorization', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    const a1 = new RequestAuthorization()
    e1.authorization = [a1]
    const r1 = project.addRequest('r1')
    r1.environment = e1.key
    const a2 = new RequestAuthorization()
    r1.authorization = [a2]
    const result = r1.readAuthorization()
    assert.deepEqual(result, [a2])
  })

  test('returns inherited authorization', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    const a1 = new RequestAuthorization()
    e1.authorization = [a1]
    const f1 = project.addFolder('f1')
    const r1 = f1.addRequest('r1')
    const result = r1.readAuthorization()
    assert.deepEqual(result, [a1])
  })

  test('returns first inherited authorization', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    const a1 = new RequestAuthorization()
    e1.authorization = [a1]
    const f1 = project.addFolder('f1')
    const e2 = f1.addEnvironment('e2')
    const a2 = new RequestAuthorization()
    e2.authorization = [a2]
    const f2 = f1.addFolder('f2')
    const r1 = f2.addRequest('r1')
    const result = r1.readAuthorization()
    assert.deepEqual(result, [a2])
  })

  test('prohibits reading inherited authorization with encapsulated environment', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    const a1 = new RequestAuthorization()
    e1.authorization = [a1]
    const f1 = project.addFolder('f1')
    const e2 = f1.addEnvironment('e2')
    e2.encapsulated = true
    const f2 = f1.addFolder('f2')
    const r1 = f2.addRequest('r1')
    const result = r1.readAuthorization()
    assert.deepEqual(result, [])
  })

  test('returns empty array when selected environment has no authorization', () => {
    const project = new HttpProject()
    const e1 = project.addEnvironment('e1')
    const a1 = new RequestAuthorization()
    e1.authorization = [a1]
    const f1 = project.addFolder('f1')
    const e2 = f1.addEnvironment('e2')
    const r1 = f1.addRequest('r1')
    r1.environment = e2.key
    const result = r1.readAuthorization()
    assert.deepEqual(result, [])
  })
})
