import { test } from '@japa/runner'
import { HostRule, HostRuleKind, IHostRule } from '../../../src/index.js'

test.group('Default project initialization', () => {
  test('initializes a default rule', ({ assert }) => {
    const result = new HostRule()
    assert.equal(result.kind, HostRuleKind, 'sets the kind property')
    assert.equal(result.from, '', 'sets the from property')
    assert.equal(result.to, '', 'sets the to property')
    assert.isUndefined(result.enabled, 'does not set the enabled property')
    assert.isUndefined(result.comment, 'does not set the comment property')
  })
})

test.group('From schema initialization', (group) => {
  let base: IHostRule
  group.each.setup(() => {
    base = {
      kind: HostRuleKind,
      from: '',
      to: '',
      key: '123',
    }
  })

  test('sets the from property', ({ assert }) => {
    const init: IHostRule = { ...base, ...{ from: 'test' } }
    const rule = new HostRule(init)
    assert.equal(rule.from, 'test')
  })

  test('sets the to property', ({ assert }) => {
    const init: IHostRule = { ...base, ...{ to: 'test' } }
    const rule = new HostRule(init)
    assert.equal(rule.to, 'test')
  })

  test('sets the comment property', ({ assert }) => {
    const init: IHostRule = { ...base, ...{ comment: 'test' } }
    const rule = new HostRule(JSON.stringify(init))
    assert.equal(rule.comment, 'test')
  })

  test('sets the enabled property', ({ assert }) => {
    const init: IHostRule = { ...base, ...{ enabled: false } }
    const rule = new HostRule(JSON.stringify(init))
    assert.isFalse(rule.enabled)
  })
})

test.group('fromValues()', () => {
  test('creates a rule with required values', ({ assert }) => {
    const rule = HostRule.fromValues('a', 'b')
    assert.equal(rule.from, 'a')
    assert.equal(rule.to, 'b')
    assert.equal(rule.kind, HostRuleKind)
    assert.isTrue(rule.enabled)
  })
})

test.group('toJSON()', () => {
  test('serializes the from value', ({ assert }) => {
    const rule = HostRule.fromValues('a', 'b')
    const result = rule.toJSON()
    assert.equal(result.from, 'a')
  })

  test('serializes the to value', ({ assert }) => {
    const rule = HostRule.fromValues('a', 'b')
    const result = rule.toJSON()
    assert.equal(result.to, 'b')
  })

  test('serializes the kind value', ({ assert }) => {
    const rule = HostRule.fromValues('a', 'b')
    const result = rule.toJSON()
    assert.equal(result.kind, HostRuleKind)
  })

  test('serializes the enabled value', ({ assert }) => {
    const rule = HostRule.fromValues('a', 'b')
    const result = rule.toJSON()
    assert.isTrue(result.enabled)
  })

  test('serializes the comment value', ({ assert }) => {
    const rule = HostRule.fromValues('a', 'b')
    rule.comment = 'test'
    const result = rule.toJSON()
    assert.equal(result.comment, 'test')
  })
})

test.group('applyHosts()', () => {
  const url = 'https://api.host.domain.com/path?query=param'
  test('Returns the URL if there is no rules', ({ assert }) => {
    // @ts-expect-error Used in testing
    assert.equal(HostRule.applyHosts(url, undefined), url)
  })

  test('Returns the URL if rules is empty', ({ assert }) => {
    assert.equal(HostRule.applyHosts(url, []), url)
  })

  test('Alters the URL by all rules', ({ assert }) => {
    const rules: HostRule[] = [
      HostRule.fromValues('https:', 'ftp:'),
      HostRule.fromValues('/api\\.', '/0.'),
      HostRule.fromValues('\\.host\\.', '.'),
      HostRule.fromValues('/path', '/api'),
      HostRule.fromValues('query=param', 'a=b'),
    ]
    const result = HostRule.applyHosts(url, rules)
    assert.equal(result, 'ftp://0.domain.com/api?a=b')
  })
})

test.group('createRuleRe()', () => {
  test('Creates a regular expression', ({ assert }) => {
    const result = HostRule.createRuleRe('test')
    assert.typeOf(result, 'RegExp')
  })

  test('Expression is based on the input', ({ assert }) => {
    const input = 'test-input'
    const result = HostRule.createRuleRe(input)
    assert.equal(result.source, input)
  })

  test('Replaces asterisk with regular expression group input', ({ assert }) => {
    const result = HostRule.createRuleRe('test*input')
    assert.equal(result.source, 'test(.*)input')
  })

  test('Replaces asterisks globally', ({ assert }) => {
    const result = HostRule.createRuleRe('test*input*')
    assert.equal(result.source, 'test(.*)input(.*)')
  })
})

test.group('evaluateRule()', () => {
  test('Returns undefined when rule is not defined', ({ assert }) => {
    const url = 'test'
    // @ts-expect-error Used in testing
    assert.isUndefined(HostRule.evaluateRule(url, undefined))
    // @ts-expect-error Used in testing
    assert.isUndefined(HostRule.evaluateRule(url, HostRule.fromValues(undefined, undefined)))
    // @ts-expect-error Used in testing
    assert.isUndefined(HostRule.evaluateRule(url, HostRule.fromValues('from', undefined)))
    // @ts-expect-error Used in testing
    assert.isUndefined(HostRule.evaluateRule(url, HostRule.fromValues(undefined, 'to')))
  })

  test('Returns undefined if the rule does not match', ({ assert }) => {
    const url = 'abc'
    const rule = HostRule.fromValues('xyz', 'test')
    assert.isUndefined(HostRule.evaluateRule(url, rule))
  })

  test('Alters the url', ({ assert }) => {
    const url = 'abc'
    const rule = HostRule.fromValues('a', 'test')
    const result = HostRule.evaluateRule(url, rule)
    assert.equal(result, 'testbc')
  })

  test('Alters the url globally', ({ assert }) => {
    const url = 'abca'
    const rule = HostRule.fromValues('a', 'test')
    const result = HostRule.evaluateRule(url, rule)
    assert.equal(result, 'testbctest')
  })

  test('Includes asterisk', ({ assert }) => {
    const url = 'abca'
    const rule = HostRule.fromValues('abc*', 'test')
    const result = HostRule.evaluateRule(url, rule)
    assert.equal(result, 'test')
  })
  ;[
    [
      'https://api.domain.com/api',
      'https://test.domain.com/api',
      HostRule.fromValues('api.domain.com', 'test.domain.com'),
    ],
    ['https://api.domain.com/api', 'https://test.domain.com/api', HostRule.fromValues('api.*.com', 'test.domain.com')],
    ['https://a123.domain.com/api', 'https://test.domain.com/api', HostRule.fromValues('a(\\d+)', 'test')],
    ['https://a123.domain.com/api', 'https://secured/api', HostRule.fromValues('https://*/', 'https://secured/')],
    ['https://var.domain.com/var', 'https://abc.domain.com/abc', HostRule.fromValues('var', 'abc')],
  ].forEach((item, index) => {
    test(`Evaluates test #${index}`, ({ assert }) => {
      const result = HostRule.evaluateRule(String(item[0]), item[2] as HostRule)
      assert.equal(result, item[1])
    })
  })
})
